1 /*
   2  * CDDL HEADER START
   3  *
   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.
   8  *
   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.
  13  *
  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]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2001-2002 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <stdio.h>
  30 #include <stdlib.h>
  31 #include <sys/types.h>
  32 #include <sys/stat.h>
  33 #include <unistd.h>
  34 #include <errno.h>
  35 #include <strings.h>
  36 #include <string.h>
  37 #include <fcntl.h>
  38 #include <assert.h>
  39 #include <libipp.h>
  40 #include <libnvpair.h>
  41 #include <ipp/ippctl.h>
  42 
  43 /*
  44  * Debug macros
  45  */
  46 
  47 #if     defined(DEBUG) && !defined(lint)
  48 uint32_t        ipp_debug_flags =
  49 /*
  50  * DBG_IO |
  51  */
  52 DBG_ERR |
  53 0;
  54 
  55 #define DBG0(flags, fmt)                                                \
  56         do {                                                            \
  57                 if (flags & ipp_debug_flags)                                \
  58                         fprintf(stderr, "libipp: " __FN__ ": " fmt);    \
  59         } while (0)
  60 
  61 #define DBG1(flags, fmt, a)                                             \
  62         do {                                                            \
  63                 if (flags & ipp_debug_flags)                                \
  64                         fprintf(stderr, "libipp: " __FN__ ": " fmt, a); \
  65         } while (0)
  66 
  67 #define DBG2(flags, fmt, a, b)                                          \
  68         do {                                                            \
  69                 if (flags & ipp_debug_flags)                                \
  70                         fprintf(stderr, "libipp: " __FN__ ": " fmt, a,  \
  71                             b);                                         \
  72         } while (0)
  73 
  74 #define DBG3(flags, fmt, a, b, c)                                       \
  75         do {                                                            \
  76                 if (flags & ipp_debug_flags)                                \
  77                         fprintf(stderr, "libipp: " __FN__ ": " fmt, a,  \
  78                             b, c);                                      \
  79         } while (0)
  80 
  81 #else   /* defined(DEBUG) && !defined(lint) */
  82 #define DBG0(flags, fmt)
  83 #define DBG1(flags, fmt, a)
  84 #define DBG2(flags, fmt, a, b)
  85 #define DBG3(flags, fmt, a, b, c)
  86 #endif  /* defined(DEBUG) && !defined(lint) */
  87 
  88 /*
  89  * Control device node
  90  */
  91 
  92 #define IPPCTL_DEVICE   "/devices/pseudo/ippctl@0:ctl"
  93 
  94 /*
  95  * Structures.
  96  */
  97 
  98 typedef struct array_desc_t {
  99         char    *name;
 100         char    **array;
 101         int     nelt;
 102 } array_desc_t;
 103 
 104 /*
 105  * Prototypes
 106  */
 107 
 108 static int      nvlist_callback(nvlist_t *, void *);
 109 static int      string_callback(nvlist_t *, void *);
 110 static int      string_array_callback(nvlist_t *, void *);
 111 static int      dispatch(nvlist_t **, int (*)(nvlist_t *, void *), void *);
 112 
 113 /*
 114  * API functions
 115  */
 116 #define __FN__  "ipp_action_create"
 117 int
 118 ipp_action_create(
 119         const char      *modname,
 120         const char      *aname,
 121         nvlist_t        **nvlpp,
 122         ipp_flags_t     flags)
 123 {
 124         nvlist_t        *nvlp;
 125         int             rc;
 126 
 127         /*
 128          * Sanity check the arguments.
 129          */
 130 
 131         if (nvlpp == NULL || modname == NULL || aname == NULL) {
 132                 DBG0(DBG_ERR, "bad argument\n");
 133                 errno = EINVAL;
 134                 return (-1);
 135         }
 136 
 137         /*
 138          * Add our data to the nvlist. (This information will be removed for
 139          * use by ippctl).
 140          */
 141 
 142         nvlp = *nvlpp;
 143         if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
 144             IPPCTL_OP_ACTION_CREATE)) != 0) {
 145                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
 146                 goto failed;
 147         }
 148 
 149         if ((rc = nvlist_add_string(nvlp, IPPCTL_MODNAME,
 150             (char *)modname)) != 0) {
 151                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n",
 152                     IPPCTL_MODNAME);
 153                 goto failed;
 154         }
 155 
 156         if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
 157                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
 158                 goto failed;
 159         }
 160 
 161         if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
 162                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
 163                 goto failed;
 164         }
 165 
 166         /*
 167          * Talk to the kernel.
 168          */
 169 
 170         return (dispatch(nvlpp, nvlist_callback, (void *)nvlpp));
 171 failed:
 172         errno = rc;
 173         return (-1);
 174 }
 175 #undef  __FN__
 176 
 177 #define __FN__  "ipp_action_destroy"
 178 int
 179 ipp_action_destroy(
 180         const char      *aname,
 181         ipp_flags_t     flags)
 182 {
 183         nvlist_t        *nvlp;
 184         int             rc;
 185 
 186         /*
 187          * Sanity check the arguments.
 188          */
 189 
 190         if (aname == NULL) {
 191                 DBG0(DBG_ERR, "bad argument\n");
 192                 errno = EINVAL;
 193                 return (-1);
 194         }
 195 
 196         /*
 197          * Create an nvlist for our data as none is passed into the function.
 198          */
 199 
 200         if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
 201                 DBG0(DBG_ERR, "failed to allocate nvlist\n");
 202                 nvlp = NULL;
 203                 goto failed;
 204         }
 205 
 206         if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
 207             IPPCTL_OP_ACTION_DESTROY)) != 0) {
 208                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
 209                 goto failed;
 210         }
 211 
 212         if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
 213                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
 214                 goto failed;
 215         }
 216 
 217         if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
 218                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
 219                 goto failed;
 220         }
 221 
 222         /*
 223          * Talk to the kernel.
 224          */
 225 
 226         return (dispatch(&nvlp, NULL, NULL));
 227 failed:
 228         if (nvlp != NULL)
 229                 nvlist_free(nvlp);
 230         errno = rc;
 231         return (-1);
 232 }
 233 #undef  __FN__
 234 
 235 #define __FN__  "ipp_action_modify"
 236 int
 237 ipp_action_modify(
 238         const char      *aname,
 239         nvlist_t        **nvlpp,
 240         ipp_flags_t     flags)
 241 {
 242         nvlist_t        *nvlp;
 243         int             rc;
 244 
 245         /*
 246          * Sanity check the arguments.
 247          */
 248 
 249         if (nvlpp == NULL || aname == NULL) {
 250                 DBG0(DBG_ERR, "bad argument\n");
 251                 errno = EINVAL;
 252                 return (-1);
 253         }
 254 
 255         /*
 256          * Add our data to the nvlist.
 257          */
 258 
 259         nvlp = *nvlpp;
 260         if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
 261             IPPCTL_OP_ACTION_MODIFY)) != 0) {
 262                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
 263                 goto failed;
 264         }
 265 
 266         if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
 267                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
 268                 goto failed;
 269         }
 270 
 271         if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
 272                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
 273                 goto failed;
 274         }
 275 
 276         /*
 277          * Talk to the kernel.
 278          */
 279 
 280         return (dispatch(nvlpp, nvlist_callback, (void *)nvlpp));
 281 failed:
 282         errno = rc;
 283         return (-1);
 284 }
 285 #undef  __FN__
 286 
 287 #define __FN__  "ipp_action_info"
 288 int
 289 ipp_action_info(
 290         const char      *aname,
 291         int             (*fn)(nvlist_t *, void *),
 292         void            *arg,
 293         ipp_flags_t     flags)
 294 {
 295         nvlist_t        *nvlp;
 296         int             rc;
 297 
 298         /*
 299          * Sanity check the arguments.
 300          */
 301 
 302         if (aname == NULL || fn == NULL) {
 303                 DBG0(DBG_ERR, "bad argument\n");
 304                 errno = EINVAL;
 305                 return (-1);
 306         }
 307 
 308         /*
 309          * Create an nvlist for our data.
 310          */
 311 
 312         if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
 313                 DBG0(DBG_ERR, "failed to allocate nvlist\n");
 314                 nvlp = NULL;
 315         }
 316 
 317         if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
 318             IPPCTL_OP_ACTION_INFO)) != 0) {
 319                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
 320                 goto failed;
 321         }
 322 
 323         if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
 324                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
 325                 goto failed;
 326         }
 327 
 328         if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
 329                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
 330                 goto failed;
 331         }
 332 
 333         /*
 334          * Talk to the kernel.
 335          */
 336 
 337         return (dispatch(&nvlp, fn, arg));
 338 failed:
 339         if (nvlp != NULL)
 340                 nvlist_free(nvlp);
 341         errno = rc;
 342         return (-1);
 343 }
 344 #undef  __FN__
 345 
 346 #define __FN__  "ipp_action_mod"
 347 int
 348 ipp_action_mod(
 349         const char      *aname,
 350         char            **modnamep)
 351 {
 352         nvlist_t        *nvlp;
 353         int             rc;
 354 
 355         /*
 356          * Sanity check the arguments.
 357          */
 358 
 359         if (aname == NULL || modnamep == NULL) {
 360                 DBG0(DBG_ERR, "bad argument\n");
 361                 errno = EINVAL;
 362                 return (-1);
 363         }
 364 
 365         /*
 366          * Create an nvlist for our data.
 367          */
 368 
 369         if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
 370                 DBG0(DBG_ERR, "failed to allocate nvlist\n");
 371                 nvlp = NULL;
 372                 goto failed;
 373         }
 374 
 375         if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
 376             IPPCTL_OP_ACTION_MOD)) != 0) {
 377                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
 378                 goto failed;
 379         }
 380 
 381         if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
 382                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
 383                 goto failed;
 384         }
 385 
 386         /*
 387          * Talk to the kernel.
 388          */
 389 
 390         return (dispatch(&nvlp, string_callback, (void *)modnamep));
 391 failed:
 392         if (nvlp != NULL)
 393                 nvlist_free(nvlp);
 394         errno = rc;
 395         return (-1);
 396 }
 397 #undef  __FN__
 398 
 399 #define __FN__  "ipp_list_mods"
 400 int
 401 ipp_list_mods(
 402         char            ***modname_arrayp,
 403         int             *neltp)
 404 {
 405         nvlist_t        *nvlp;
 406         array_desc_t    ad;
 407         int             rc;
 408 
 409         /*
 410          * Sanity check the arguments.
 411          */
 412 
 413         if (modname_arrayp == NULL || neltp == NULL) {
 414                 DBG0(DBG_ERR, "bad argument");
 415                 errno = EINVAL;
 416                 return (-1);
 417         }
 418 
 419         /*
 420          * Create an nvlist for our data.
 421          */
 422 
 423         if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
 424                 DBG0(DBG_ERR, "failed to allocate nvlist\n");
 425                 nvlp = NULL;
 426         }
 427 
 428         if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
 429             IPPCTL_OP_LIST_MODS)) != 0) {
 430                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
 431                 goto failed;
 432         }
 433 
 434         /*
 435          * Talk to the kernel.
 436          */
 437 
 438         ad.name = IPPCTL_MODNAME_ARRAY;
 439         ad.array = NULL;
 440         ad.nelt = 0;
 441 
 442         if ((rc = dispatch(&nvlp, string_array_callback, (void *)&ad)) == 0) {
 443                 *modname_arrayp = ad.array;
 444                 *neltp = ad.nelt;
 445         }
 446 
 447         return (rc);
 448 failed:
 449         if (nvlp != NULL)
 450                 nvlist_free(nvlp);
 451         errno = rc;
 452         return (-1);
 453 }
 454 #undef  __FN__
 455 
 456 #define __FN__  "ipp_mod_list_actions"
 457 int
 458 ipp_mod_list_actions(
 459         const char      *modname,
 460         char            ***aname_arrayp,
 461         int             *neltp)
 462 {
 463         nvlist_t        *nvlp;
 464         array_desc_t    ad;
 465         int             rc;
 466 
 467         /*
 468          * Sanity check the arguments.
 469          */
 470 
 471         if (modname == NULL || aname_arrayp == NULL || neltp == NULL) {
 472                 DBG0(DBG_ERR, "bad argument");
 473                 errno = EINVAL;
 474                 return (-1);
 475         }
 476 
 477         /*
 478          * Create an nvlist for our data.
 479          */
 480 
 481         if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
 482                 DBG0(DBG_ERR, "failed to allocate nvlist\n");
 483                 nvlp = NULL;
 484         }
 485 
 486         if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
 487             IPPCTL_OP_MOD_LIST_ACTIONS)) != 0) {
 488                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
 489                 goto failed;
 490         }
 491 
 492         if ((rc = nvlist_add_string(nvlp, IPPCTL_MODNAME,
 493             (char *)modname)) != 0) {
 494                 DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_MODNAME);
 495                 goto failed;
 496         }
 497 
 498         /*
 499          * Talk to the kernel.
 500          */
 501 
 502         ad.name = IPPCTL_ANAME_ARRAY;
 503         ad.array = NULL;
 504         ad.nelt = 0;
 505 
 506         if ((rc = dispatch(&nvlp, string_array_callback, (void *)&ad)) == 0) {
 507                 *aname_arrayp = ad.array;
 508                 *neltp = ad.nelt;
 509         }
 510 
 511         return (rc);
 512 failed:
 513         if (nvlp != NULL)
 514                 nvlist_free(nvlp);
 515         errno = rc;
 516         return (-1);
 517 }
 518 #undef  __FN__
 519 
 520 #define __FN__  "ipp_free"
 521 void
 522 ipp_free(
 523         char    *buf)
 524 {
 525         free(buf);
 526 }
 527 #undef  __FN__
 528 
 529 #define __FN__  "ipp_free_array"
 530 void
 531 ipp_free_array(
 532         char    **array,
 533         int     nelt)
 534 {
 535         int     i;
 536 
 537         assert(array[nelt] == NULL);
 538 
 539         for (i = 0; i < nelt; i++)
 540                 free(array[i]);
 541 
 542         free(array);
 543 }
 544 #undef  __FN__
 545 
 546 #define __FN__  "nvlist_callback"
 547 static int
 548 nvlist_callback(
 549         nvlist_t        *nvlp,
 550         void            *arg)
 551 {
 552         nvlist_t        **nvlpp = (nvlist_t **)arg;
 553         int             rc;
 554 
 555         /*
 556          * Callback function used by ipp_action_create() and
 557          * ipp_action_modify()
 558          */
 559 
 560         DBG0(DBG_IO, "called\n");
 561 
 562         assert(nvlpp != NULL);
 563         assert(*nvlpp == NULL);
 564 
 565         /*
 566          * Duplicate the nvlist and set the given pointer to point at the new
 567          * copy.
 568          */
 569 
 570         if ((rc = nvlist_dup(nvlp, nvlpp, 0)) != 0) {
 571                 DBG0(DBG_ERR, "failed to dup nvlist\n");
 572                 errno = rc;
 573                 return (-1);
 574         }
 575 
 576         return (0);
 577 }
 578 #undef  __FN__
 579 
 580 #define __FN__  "string_callback"
 581 static int
 582 string_callback(
 583         nvlist_t        *nvlp,
 584         void            *arg)
 585 {
 586         char            **namep = (char **)arg;
 587         char            *name;
 588         char            *ptr;
 589         int             rc;
 590 
 591         /*
 592          * Callback function used by ipp_action_mod()
 593          */
 594 
 595         DBG0(DBG_IO, "called\n");
 596 
 597         assert(namep != NULL);
 598 
 599         /*
 600          * Look up the module name from the nvlist.
 601          */
 602 
 603         if ((rc = nvlist_lookup_string(nvlp, IPPCTL_MODNAME, &ptr)) != 0) {
 604                 DBG0(DBG_ERR, "failed to find string\n");
 605                 errno = rc;
 606                 return (-1);
 607         }
 608 
 609         /*
 610          * Allocate a duplicate string.
 611          */
 612 
 613         if ((name = strdup(ptr)) == NULL) {
 614                 DBG0(DBG_ERR, "failed to duplicate string\n");
 615                 return (-1);
 616         }
 617 
 618         /*
 619          * Set the given pointer to point at the string.
 620          */
 621 
 622         *namep = name;
 623         return (0);
 624 }
 625 #undef  __FN__
 626 
 627 #define __FN__  "string_array_callback"
 628 static int
 629 string_array_callback(
 630         nvlist_t        *nvlp,
 631         void            *arg)
 632 {
 633         array_desc_t    *adp = (array_desc_t *)arg;
 634         char            **dst;
 635         char            **src;
 636         uint_t          nelt;
 637         int             i;
 638         int             rc;
 639 
 640         /*
 641          * Callback function used by ipp_list_mods()
 642          */
 643 
 644         DBG0(DBG_IO, "called\n");
 645 
 646         assert(adp != NULL);
 647 
 648         /*
 649          * Look up the module name from the nvlist.
 650          */
 651 
 652         if ((rc = nvlist_lookup_string_array(nvlp, adp->name, &src,
 653             &nelt)) != 0) {
 654                 DBG0(DBG_ERR, "failed to find array\n");
 655                 errno = rc;
 656                 return (-1);
 657         }
 658 
 659         /*
 660          * Allocate an array.
 661          */
 662 
 663         if ((dst = malloc((nelt + 1) * sizeof (char *))) == NULL) {
 664                 DBG0(DBG_ERR, "failed to allocate new array\n");
 665                 return (-1);
 666         }
 667 
 668         /*
 669          * For each string in the array, allocate a new buffer and copy
 670          * the string into it.
 671          */
 672 
 673         for (i = 0; i < nelt; i++) {
 674                 if ((dst[i] = strdup(src[i])) == NULL) {
 675                         while (--i >= 0) {
 676                                 free(dst[i]);
 677                         }
 678                         free(dst);
 679                         DBG0(DBG_ERR, "failed to duplicate array\n");
 680                         return (-1);
 681                 }
 682         }
 683         dst[nelt] = NULL;
 684 
 685         /*
 686          * Set the information to be passed back.
 687          */
 688 
 689         adp->array = dst;
 690         adp->nelt = nelt;
 691 
 692         return (0);
 693 }
 694 #undef  __FN__
 695 
 696 #define __FN__  "dispatch"
 697 static int
 698 dispatch(
 699         nvlist_t        **nvlpp,
 700         int             (*fn)(nvlist_t *, void *),
 701         void            *arg)
 702 {
 703         char            *cbuf = NULL;
 704         char            *dbuf = NULL;
 705         size_t          cbuflen = 0;
 706         size_t          dbuflen = 0;
 707         size_t          thisbuflen = 0;
 708         size_t          nextbuflen = 0;
 709         int             rc;
 710         ippctl_ioctl_t  iioc;
 711         int             fd;
 712         nvlist_t        *cnvlp;
 713         nvlist_t        *dnvlp = NULL;
 714         int             count;
 715         int             rval;
 716 
 717         /*
 718          * Sanity check the 'command' nvlist.
 719          */
 720 
 721         cnvlp = *nvlpp;
 722         if (cnvlp == NULL) {
 723                 rc = EINVAL;
 724                 return (-1);
 725         }
 726 
 727         /*
 728          * Pack the nvlist and then free the original.
 729          */
 730 
 731         if ((rc = nvlist_pack(cnvlp, &cbuf, &cbuflen, NV_ENCODE_NATIVE,
 732             0)) != 0) {
 733                 DBG0(DBG_ERR, "failed to pack nvlist\n");
 734                 nvlist_free(cnvlp);
 735                 errno = rc;
 736                 return (-1);
 737         }
 738         nvlist_free(cnvlp);
 739         *nvlpp = NULL;
 740 
 741         /*
 742          * Open the control device node.
 743          */
 744 
 745         DBG1(DBG_IO, "opening %s\n", IPPCTL_DEVICE);
 746         if ((fd = open(IPPCTL_DEVICE, O_RDWR | O_NOCTTY)) == -1) {
 747                 DBG1(DBG_ERR, "failed to open %s\n", IPPCTL_DEVICE);
 748                 goto command_failed;
 749         }
 750 
 751         /*
 752          * Set up an ioctl structure to point at the packed nvlist.
 753          */
 754 
 755         iioc.ii_buf = cbuf;
 756         iioc.ii_buflen = cbuflen;
 757 
 758         /*
 759          * Issue a command ioctl, passing the ioctl structure.
 760          */
 761 
 762         DBG0(DBG_IO, "command\n");
 763         if ((rc = ioctl(fd, IPPCTL_CMD, &iioc)) < 0) {
 764                 DBG0(DBG_ERR, "command ioctl failed\n");
 765                 goto command_failed;
 766         }
 767 
 768         /*
 769          * Get back the length of the first data buffer.
 770          */
 771 
 772         if ((nextbuflen = (size_t)rc) == 0) {
 773                 DBG0(DBG_ERR, "no data buffer\n");
 774                 errno = EPROTO;
 775                 goto command_failed;
 776         }
 777 
 778         /*
 779          * Try to re-use the command buffer as the first data buffer.
 780          */
 781 
 782         dbuf = cbuf;
 783         thisbuflen = cbuflen;
 784 
 785         count = 0;
 786         while (nextbuflen != 0) {
 787                 dbuflen = nextbuflen;
 788 
 789                 /*
 790                  * Check whether the buffer we have is long enough for the
 791                  * next lot of data. If it isn't, allocate a new one of
 792                  * the appropriate length.
 793                  */
 794 
 795                 if (nextbuflen > thisbuflen) {
 796                         if ((dbuf = realloc(dbuf, nextbuflen)) == NULL) {
 797                                 DBG0(DBG_ERR,
 798                                     "failed to allocate data buffer\n");
 799                                 goto data_failed;
 800                         }
 801                         thisbuflen = nextbuflen;
 802                 }
 803 
 804                 /*
 805                  * Set up an ioctl structure to point at the data buffer.
 806                  */
 807 
 808                 iioc.ii_buf = dbuf;
 809                 iioc.ii_buflen = dbuflen;
 810 
 811                 /*
 812                  * Issue a data ioctl, passing the ioctl structure.
 813                  */
 814 
 815                 DBG2(DBG_IO, "data[%d]: length = %d\n", count, dbuflen);
 816                 if ((rc = ioctl(fd, IPPCTL_DATA, &iioc)) < 0) {
 817                         DBG0(DBG_ERR, "data ioctl failed\n");
 818                         goto data_failed;
 819                 }
 820 
 821                 /*
 822                  * Get the length of the *next* data buffer, if there is
 823                  * one.
 824                  */
 825 
 826                 nextbuflen = (size_t)rc;
 827                 DBG1(DBG_IO, "nextbuflen = %d\n", nextbuflen);
 828 
 829                 /*
 830                  * Unpack the nvlist that the current data buffer should
 831                  * now contain.
 832                  */
 833 
 834                 if ((rc = nvlist_unpack(dbuf, dbuflen, &dnvlp, 0)) != 0) {
 835                         DBG0(DBG_ERR, "failed to unpack nvlist\n");
 836                         errno = rc;
 837                         goto data_failed;
 838                 }
 839 
 840                 /*
 841                  * The first data buffer should contain the kernel function's
 842                  * return code. Subsequent buffers contain nvlists which
 843                  * should be passed to the given callback function.
 844                  */
 845 
 846                 if (count == 0) {
 847                         if ((rc = nvlist_lookup_int32(dnvlp, IPPCTL_RC,
 848                             &rval)) != 0) {
 849                                 DBG0(DBG_ERR, "failed to find return code\n");
 850                                 nvlist_free(dnvlp);
 851                                 errno = rc;
 852                                 goto data_failed;
 853                         }
 854                 } else {
 855                         if (fn != NULL)
 856                                 if (fn(dnvlp, arg) != 0) {
 857 
 858                                         /*
 859                                          * The callback function returned
 860                                          * a non-zero value. Abort any further
 861                                          * data collection.
 862                                          */
 863 
 864                                         nvlist_free(dnvlp);
 865                                         free(dbuf);
 866                                 }
 867                 }
 868 
 869                 /*
 870                  * Free the nvlist now that we have extracted the return
 871                  * code or called the callback function.
 872                  */
 873 
 874                 nvlist_free(dnvlp);
 875                 dnvlp = NULL;
 876 
 877                 count++;
 878         }
 879 
 880         /*
 881          * Free the data buffer as data collection is now complete.
 882          */
 883 
 884         free(dbuf);
 885 
 886         /*
 887          * Close the control device.
 888          */
 889 
 890         (void) close(fd);
 891 
 892         /*
 893          * If the kernel returned an error, we should return an error.
 894          * and set errno.
 895          */
 896 
 897         if (rval != 0) {
 898                 DBG1(DBG_IO, "kernel return code = %d\n", rval);
 899                 errno = rval;
 900                 return (-1);
 901         }
 902 
 903         return (0);
 904 
 905 command_failed:
 906         free(cbuf);
 907         if (fd != -1)
 908                 (void) close(fd);
 909         return (-1);
 910 
 911 data_failed:
 912         if (dbuf != NULL)
 913                 free(dbuf);
 914         (void) close(fd);
 915         return (-1);
 916 }
 917 #undef  __FN__