8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libipp / libipp.c
blob0b931a10e8444ce1b8463188f7a11e7ce64e95f1
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
23 * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <strings.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <assert.h>
37 #include <libipp.h>
38 #include <libnvpair.h>
39 #include <ipp/ippctl.h>
42 * Debug macros
45 #if defined(DEBUG) && !defined(lint)
46 uint32_t ipp_debug_flags =
48 * DBG_IO |
50 DBG_ERR |
53 #define DBG0(flags, fmt) \
54 do { \
55 if (flags & ipp_debug_flags) \
56 fprintf(stderr, "libipp: " __FN__ ": " fmt); \
57 } while (0)
59 #define DBG1(flags, fmt, a) \
60 do { \
61 if (flags & ipp_debug_flags) \
62 fprintf(stderr, "libipp: " __FN__ ": " fmt, a); \
63 } while (0)
65 #define DBG2(flags, fmt, a, b) \
66 do { \
67 if (flags & ipp_debug_flags) \
68 fprintf(stderr, "libipp: " __FN__ ": " fmt, a, \
69 b); \
70 } while (0)
72 #define DBG3(flags, fmt, a, b, c) \
73 do { \
74 if (flags & ipp_debug_flags) \
75 fprintf(stderr, "libipp: " __FN__ ": " fmt, a, \
76 b, c); \
77 } while (0)
79 #else /* defined(DEBUG) && !defined(lint) */
80 #define DBG0(flags, fmt)
81 #define DBG1(flags, fmt, a)
82 #define DBG2(flags, fmt, a, b)
83 #define DBG3(flags, fmt, a, b, c)
84 #endif /* defined(DEBUG) && !defined(lint) */
87 * Control device node
90 #define IPPCTL_DEVICE "/devices/pseudo/ippctl@0:ctl"
93 * Structures.
96 typedef struct array_desc_t {
97 char *name;
98 char **array;
99 int nelt;
100 } array_desc_t;
103 * Prototypes
106 static int nvlist_callback(nvlist_t *, void *);
107 static int string_callback(nvlist_t *, void *);
108 static int string_array_callback(nvlist_t *, void *);
109 static int dispatch(nvlist_t **, int (*)(nvlist_t *, void *), void *);
112 * API functions
114 #define __FN__ "ipp_action_create"
116 ipp_action_create(
117 const char *modname,
118 const char *aname,
119 nvlist_t **nvlpp,
120 ipp_flags_t flags)
122 nvlist_t *nvlp;
123 int rc;
126 * Sanity check the arguments.
129 if (nvlpp == NULL || modname == NULL || aname == NULL) {
130 DBG0(DBG_ERR, "bad argument\n");
131 errno = EINVAL;
132 return (-1);
136 * Add our data to the nvlist. (This information will be removed for
137 * use by ippctl).
140 nvlp = *nvlpp;
141 if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
142 IPPCTL_OP_ACTION_CREATE)) != 0) {
143 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
144 goto failed;
147 if ((rc = nvlist_add_string(nvlp, IPPCTL_MODNAME,
148 (char *)modname)) != 0) {
149 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n",
150 IPPCTL_MODNAME);
151 goto failed;
154 if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
155 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
156 goto failed;
159 if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
160 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
161 goto failed;
165 * Talk to the kernel.
168 return (dispatch(nvlpp, nvlist_callback, (void *)nvlpp));
169 failed:
170 errno = rc;
171 return (-1);
173 #undef __FN__
175 #define __FN__ "ipp_action_destroy"
177 ipp_action_destroy(
178 const char *aname,
179 ipp_flags_t flags)
181 nvlist_t *nvlp;
182 int rc;
185 * Sanity check the arguments.
188 if (aname == NULL) {
189 DBG0(DBG_ERR, "bad argument\n");
190 errno = EINVAL;
191 return (-1);
195 * Create an nvlist for our data as none is passed into the function.
198 if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
199 DBG0(DBG_ERR, "failed to allocate nvlist\n");
200 nvlp = NULL;
201 goto failed;
204 if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
205 IPPCTL_OP_ACTION_DESTROY)) != 0) {
206 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
207 goto failed;
210 if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
211 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
212 goto failed;
215 if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
216 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
217 goto failed;
221 * Talk to the kernel.
224 return (dispatch(&nvlp, NULL, NULL));
225 failed:
226 nvlist_free(nvlp);
227 errno = rc;
228 return (-1);
230 #undef __FN__
232 #define __FN__ "ipp_action_modify"
234 ipp_action_modify(
235 const char *aname,
236 nvlist_t **nvlpp,
237 ipp_flags_t flags)
239 nvlist_t *nvlp;
240 int rc;
243 * Sanity check the arguments.
246 if (nvlpp == NULL || aname == NULL) {
247 DBG0(DBG_ERR, "bad argument\n");
248 errno = EINVAL;
249 return (-1);
253 * Add our data to the nvlist.
256 nvlp = *nvlpp;
257 if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
258 IPPCTL_OP_ACTION_MODIFY)) != 0) {
259 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
260 goto failed;
263 if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
264 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
265 goto failed;
268 if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
269 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
270 goto failed;
274 * Talk to the kernel.
277 return (dispatch(nvlpp, nvlist_callback, (void *)nvlpp));
278 failed:
279 errno = rc;
280 return (-1);
282 #undef __FN__
284 #define __FN__ "ipp_action_info"
286 ipp_action_info(
287 const char *aname,
288 int (*fn)(nvlist_t *, void *),
289 void *arg,
290 ipp_flags_t flags)
292 nvlist_t *nvlp;
293 int rc;
296 * Sanity check the arguments.
299 if (aname == NULL || fn == NULL) {
300 DBG0(DBG_ERR, "bad argument\n");
301 errno = EINVAL;
302 return (-1);
306 * Create an nvlist for our data.
309 if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
310 DBG0(DBG_ERR, "failed to allocate nvlist\n");
311 nvlp = NULL;
314 if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
315 IPPCTL_OP_ACTION_INFO)) != 0) {
316 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
317 goto failed;
320 if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
321 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
322 goto failed;
325 if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
326 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
327 goto failed;
331 * Talk to the kernel.
334 return (dispatch(&nvlp, fn, arg));
335 failed:
336 nvlist_free(nvlp);
337 errno = rc;
338 return (-1);
340 #undef __FN__
342 #define __FN__ "ipp_action_mod"
344 ipp_action_mod(
345 const char *aname,
346 char **modnamep)
348 nvlist_t *nvlp;
349 int rc;
352 * Sanity check the arguments.
355 if (aname == NULL || modnamep == NULL) {
356 DBG0(DBG_ERR, "bad argument\n");
357 errno = EINVAL;
358 return (-1);
362 * Create an nvlist for our data.
365 if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
366 DBG0(DBG_ERR, "failed to allocate nvlist\n");
367 nvlp = NULL;
368 goto failed;
371 if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
372 IPPCTL_OP_ACTION_MOD)) != 0) {
373 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
374 goto failed;
377 if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
378 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
379 goto failed;
383 * Talk to the kernel.
386 return (dispatch(&nvlp, string_callback, (void *)modnamep));
387 failed:
388 nvlist_free(nvlp);
389 errno = rc;
390 return (-1);
392 #undef __FN__
394 #define __FN__ "ipp_list_mods"
396 ipp_list_mods(
397 char ***modname_arrayp,
398 int *neltp)
400 nvlist_t *nvlp;
401 array_desc_t ad;
402 int rc;
405 * Sanity check the arguments.
408 if (modname_arrayp == NULL || neltp == NULL) {
409 DBG0(DBG_ERR, "bad argument");
410 errno = EINVAL;
411 return (-1);
415 * Create an nvlist for our data.
418 if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
419 DBG0(DBG_ERR, "failed to allocate nvlist\n");
420 nvlp = NULL;
423 if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
424 IPPCTL_OP_LIST_MODS)) != 0) {
425 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
426 goto failed;
430 * Talk to the kernel.
433 ad.name = IPPCTL_MODNAME_ARRAY;
434 ad.array = NULL;
435 ad.nelt = 0;
437 if ((rc = dispatch(&nvlp, string_array_callback, (void *)&ad)) == 0) {
438 *modname_arrayp = ad.array;
439 *neltp = ad.nelt;
442 return (rc);
443 failed:
444 nvlist_free(nvlp);
445 errno = rc;
446 return (-1);
448 #undef __FN__
450 #define __FN__ "ipp_mod_list_actions"
452 ipp_mod_list_actions(
453 const char *modname,
454 char ***aname_arrayp,
455 int *neltp)
457 nvlist_t *nvlp;
458 array_desc_t ad;
459 int rc;
462 * Sanity check the arguments.
465 if (modname == NULL || aname_arrayp == NULL || neltp == NULL) {
466 DBG0(DBG_ERR, "bad argument");
467 errno = EINVAL;
468 return (-1);
472 * Create an nvlist for our data.
475 if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
476 DBG0(DBG_ERR, "failed to allocate nvlist\n");
477 nvlp = NULL;
480 if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
481 IPPCTL_OP_MOD_LIST_ACTIONS)) != 0) {
482 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
483 goto failed;
486 if ((rc = nvlist_add_string(nvlp, IPPCTL_MODNAME,
487 (char *)modname)) != 0) {
488 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_MODNAME);
489 goto failed;
493 * Talk to the kernel.
496 ad.name = IPPCTL_ANAME_ARRAY;
497 ad.array = NULL;
498 ad.nelt = 0;
500 if ((rc = dispatch(&nvlp, string_array_callback, (void *)&ad)) == 0) {
501 *aname_arrayp = ad.array;
502 *neltp = ad.nelt;
505 return (rc);
506 failed:
507 nvlist_free(nvlp);
508 errno = rc;
509 return (-1);
511 #undef __FN__
513 #define __FN__ "ipp_free"
514 void
515 ipp_free(
516 char *buf)
518 free(buf);
520 #undef __FN__
522 #define __FN__ "ipp_free_array"
523 void
524 ipp_free_array(
525 char **array,
526 int nelt)
528 int i;
530 assert(array[nelt] == NULL);
532 for (i = 0; i < nelt; i++)
533 free(array[i]);
535 free(array);
537 #undef __FN__
539 #define __FN__ "nvlist_callback"
540 static int
541 nvlist_callback(
542 nvlist_t *nvlp,
543 void *arg)
545 nvlist_t **nvlpp = (nvlist_t **)arg;
546 int rc;
549 * Callback function used by ipp_action_create() and
550 * ipp_action_modify()
553 DBG0(DBG_IO, "called\n");
555 assert(nvlpp != NULL);
556 assert(*nvlpp == NULL);
559 * Duplicate the nvlist and set the given pointer to point at the new
560 * copy.
563 if ((rc = nvlist_dup(nvlp, nvlpp, 0)) != 0) {
564 DBG0(DBG_ERR, "failed to dup nvlist\n");
565 errno = rc;
566 return (-1);
569 return (0);
571 #undef __FN__
573 #define __FN__ "string_callback"
574 static int
575 string_callback(
576 nvlist_t *nvlp,
577 void *arg)
579 char **namep = (char **)arg;
580 char *name;
581 char *ptr;
582 int rc;
585 * Callback function used by ipp_action_mod()
588 DBG0(DBG_IO, "called\n");
590 assert(namep != NULL);
593 * Look up the module name from the nvlist.
596 if ((rc = nvlist_lookup_string(nvlp, IPPCTL_MODNAME, &ptr)) != 0) {
597 DBG0(DBG_ERR, "failed to find string\n");
598 errno = rc;
599 return (-1);
603 * Allocate a duplicate string.
606 if ((name = strdup(ptr)) == NULL) {
607 DBG0(DBG_ERR, "failed to duplicate string\n");
608 return (-1);
612 * Set the given pointer to point at the string.
615 *namep = name;
616 return (0);
618 #undef __FN__
620 #define __FN__ "string_array_callback"
621 static int
622 string_array_callback(
623 nvlist_t *nvlp,
624 void *arg)
626 array_desc_t *adp = (array_desc_t *)arg;
627 char **dst;
628 char **src;
629 uint_t nelt;
630 int i;
631 int rc;
634 * Callback function used by ipp_list_mods()
637 DBG0(DBG_IO, "called\n");
639 assert(adp != NULL);
642 * Look up the module name from the nvlist.
645 if ((rc = nvlist_lookup_string_array(nvlp, adp->name, &src,
646 &nelt)) != 0) {
647 DBG0(DBG_ERR, "failed to find array\n");
648 errno = rc;
649 return (-1);
653 * Allocate an array.
656 if ((dst = malloc((nelt + 1) * sizeof (char *))) == NULL) {
657 DBG0(DBG_ERR, "failed to allocate new array\n");
658 return (-1);
662 * For each string in the array, allocate a new buffer and copy
663 * the string into it.
666 for (i = 0; i < nelt; i++) {
667 if ((dst[i] = strdup(src[i])) == NULL) {
668 while (--i >= 0) {
669 free(dst[i]);
671 free(dst);
672 DBG0(DBG_ERR, "failed to duplicate array\n");
673 return (-1);
676 dst[nelt] = NULL;
679 * Set the information to be passed back.
682 adp->array = dst;
683 adp->nelt = nelt;
685 return (0);
687 #undef __FN__
689 #define __FN__ "dispatch"
690 static int
691 dispatch(
692 nvlist_t **nvlpp,
693 int (*fn)(nvlist_t *, void *),
694 void *arg)
696 char *cbuf = NULL;
697 char *dbuf = NULL;
698 size_t cbuflen = 0;
699 size_t dbuflen = 0;
700 size_t thisbuflen = 0;
701 size_t nextbuflen = 0;
702 int rc;
703 ippctl_ioctl_t iioc;
704 int fd;
705 nvlist_t *cnvlp;
706 nvlist_t *dnvlp = NULL;
707 int count;
708 int rval;
711 * Sanity check the 'command' nvlist.
714 cnvlp = *nvlpp;
715 if (cnvlp == NULL) {
716 rc = EINVAL;
717 return (-1);
721 * Pack the nvlist and then free the original.
724 if ((rc = nvlist_pack(cnvlp, &cbuf, &cbuflen, NV_ENCODE_NATIVE,
725 0)) != 0) {
726 DBG0(DBG_ERR, "failed to pack nvlist\n");
727 nvlist_free(cnvlp);
728 errno = rc;
729 return (-1);
731 nvlist_free(cnvlp);
732 *nvlpp = NULL;
735 * Open the control device node.
738 DBG1(DBG_IO, "opening %s\n", IPPCTL_DEVICE);
739 if ((fd = open(IPPCTL_DEVICE, O_RDWR | O_NOCTTY)) == -1) {
740 DBG1(DBG_ERR, "failed to open %s\n", IPPCTL_DEVICE);
741 goto command_failed;
745 * Set up an ioctl structure to point at the packed nvlist.
748 iioc.ii_buf = cbuf;
749 iioc.ii_buflen = cbuflen;
752 * Issue a command ioctl, passing the ioctl structure.
755 DBG0(DBG_IO, "command\n");
756 if ((rc = ioctl(fd, IPPCTL_CMD, &iioc)) < 0) {
757 DBG0(DBG_ERR, "command ioctl failed\n");
758 goto command_failed;
762 * Get back the length of the first data buffer.
765 if ((nextbuflen = (size_t)rc) == 0) {
766 DBG0(DBG_ERR, "no data buffer\n");
767 errno = EPROTO;
768 goto command_failed;
772 * Try to re-use the command buffer as the first data buffer.
775 dbuf = cbuf;
776 thisbuflen = cbuflen;
778 count = 0;
779 while (nextbuflen != 0) {
780 dbuflen = nextbuflen;
783 * Check whether the buffer we have is long enough for the
784 * next lot of data. If it isn't, allocate a new one of
785 * the appropriate length.
788 if (nextbuflen > thisbuflen) {
789 if ((dbuf = realloc(dbuf, nextbuflen)) == NULL) {
790 DBG0(DBG_ERR,
791 "failed to allocate data buffer\n");
792 goto data_failed;
794 thisbuflen = nextbuflen;
798 * Set up an ioctl structure to point at the data buffer.
801 iioc.ii_buf = dbuf;
802 iioc.ii_buflen = dbuflen;
805 * Issue a data ioctl, passing the ioctl structure.
808 DBG2(DBG_IO, "data[%d]: length = %d\n", count, dbuflen);
809 if ((rc = ioctl(fd, IPPCTL_DATA, &iioc)) < 0) {
810 DBG0(DBG_ERR, "data ioctl failed\n");
811 goto data_failed;
815 * Get the length of the *next* data buffer, if there is
816 * one.
819 nextbuflen = (size_t)rc;
820 DBG1(DBG_IO, "nextbuflen = %d\n", nextbuflen);
823 * Unpack the nvlist that the current data buffer should
824 * now contain.
827 if ((rc = nvlist_unpack(dbuf, dbuflen, &dnvlp, 0)) != 0) {
828 DBG0(DBG_ERR, "failed to unpack nvlist\n");
829 errno = rc;
830 goto data_failed;
834 * The first data buffer should contain the kernel function's
835 * return code. Subsequent buffers contain nvlists which
836 * should be passed to the given callback function.
839 if (count == 0) {
840 if ((rc = nvlist_lookup_int32(dnvlp, IPPCTL_RC,
841 &rval)) != 0) {
842 DBG0(DBG_ERR, "failed to find return code\n");
843 nvlist_free(dnvlp);
844 errno = rc;
845 goto data_failed;
847 } else {
848 if (fn != NULL)
849 if (fn(dnvlp, arg) != 0) {
852 * The callback function returned
853 * a non-zero value. Abort any further
854 * data collection.
857 nvlist_free(dnvlp);
858 free(dbuf);
863 * Free the nvlist now that we have extracted the return
864 * code or called the callback function.
867 nvlist_free(dnvlp);
868 dnvlp = NULL;
870 count++;
874 * Free the data buffer as data collection is now complete.
877 free(dbuf);
880 * Close the control device.
883 (void) close(fd);
886 * If the kernel returned an error, we should return an error.
887 * and set errno.
890 if (rval != 0) {
891 DBG1(DBG_IO, "kernel return code = %d\n", rval);
892 errno = rval;
893 return (-1);
896 return (0);
898 command_failed:
899 free(cbuf);
900 if (fd != -1)
901 (void) close(fd);
902 return (-1);
904 data_failed:
905 if (dbuf != NULL)
906 free(dbuf);
907 (void) close(fd);
908 return (-1);
910 #undef __FN__