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 #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>
  40 
  41 /*
  42  * Debug macros
  43  */
  44 
  45 #if     defined(DEBUG) && !defined(lint)
  46 uint32_t        ipp_debug_flags =
  47 /*
  48  * DBG_IO |
  49  */
  50 DBG_ERR |
  51 0;
  52 
  53 #define DBG0(flags, fmt)                                                \
  54         do {                                                            \
  55                 if (flags & ipp_debug_flags)                                \
  56                         fprintf(stderr, "libipp: " __FN__ ": " fmt);    \
  57         } while (0)
  58 
  59 #define DBG1(flags, fmt, a)                                             \
  60         do {                                                            \
  61                 if (flags & ipp_debug_flags)                                \
  62                         fprintf(stderr, "libipp: " __FN__ ": " fmt, a); \
  63         } while (0)
  64 
  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)
  71 
  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)
  78 
  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) */
  85 
  86 /*
  87  * Control device node
  88  */
  89 
  90 #define IPPCTL_DEVICE   "/devices/pseudo/ippctl@0:ctl"
  91 
  92 /*
  93  * Structures.
  94  */
  95 
  96 typedef struct array_desc_t {
  97         char    *name;
  98         char    **array;
  99         int     nelt;
 100 } array_desc_t;
 101 
 102 /*
 103  * Prototypes
 104  */
 105 
 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 *);
 110 
 111 /*
 112  * API functions
 113  */
 114 #define __FN__  "ipp_action_create"
 115 int
 116 ipp_action_create(
 117         const char      *modname,
 118         const char      *aname,
 119         nvlist_t        **nvlpp,
 120         ipp_flags_t     flags)
 121 {
 122         nvlist_t        *nvlp;
 123         int             rc;
 124 
 125         /*
 126          * Sanity check the arguments.
 127          */
 128 
 129         if (nvlpp == NULL || modname == NULL || aname == NULL) {
 130                 DBG0(DBG_ERR, "bad argument\n");
 131                 errno = EINVAL;
 132                 return (-1);
 133         }
 134 
 135         /*
 136          * Add our data to the nvlist. (This information will be removed for
 137          * use by ippctl).
 138          */
 139 
 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;
 145         }
 146 
 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;
 152         }
 153 
 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;
 157         }
 158 
 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;
 162         }
 163 
 164         /*
 165          * Talk to the kernel.
 166          */
 167 
 168         return (dispatch(nvlpp, nvlist_callback, (void *)nvlpp));
 169 failed:
 170         errno = rc;
 171         return (-1);
 172 }
 173 #undef  __FN__
 174 
 175 #define __FN__  "ipp_action_destroy"
 176 int
 177 ipp_action_destroy(
 178         const char      *aname,
 179         ipp_flags_t     flags)
 180 {
 181         nvlist_t        *nvlp;
 182         int             rc;
 183 
 184         /*
 185          * Sanity check the arguments.
 186          */
 187 
 188         if (aname == NULL) {
 189                 DBG0(DBG_ERR, "bad argument\n");
 190                 errno = EINVAL;
 191                 return (-1);
 192         }
 193 
 194         /*
 195          * Create an nvlist for our data as none is passed into the function.
 196          */
 197 
 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;
 202         }
 203 
 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;
 208         }
 209 
 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;
 213         }
 214 
 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;
 218         }
 219 
 220         /*
 221          * Talk to the kernel.
 222          */
 223 
 224         return (dispatch(&nvlp, NULL, NULL));
 225 failed:
 226         nvlist_free(nvlp);
 227         errno = rc;
 228         return (-1);
 229 }
 230 #undef  __FN__
 231 
 232 #define __FN__  "ipp_action_modify"
 233 int
 234 ipp_action_modify(
 235         const char      *aname,
 236         nvlist_t        **nvlpp,
 237         ipp_flags_t     flags)
 238 {
 239         nvlist_t        *nvlp;
 240         int             rc;
 241 
 242         /*
 243          * Sanity check the arguments.
 244          */
 245 
 246         if (nvlpp == NULL || aname == NULL) {
 247                 DBG0(DBG_ERR, "bad argument\n");
 248                 errno = EINVAL;
 249                 return (-1);
 250         }
 251 
 252         /*
 253          * Add our data to the nvlist.
 254          */
 255 
 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;
 261         }
 262 
 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;
 266         }
 267 
 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;
 271         }
 272 
 273         /*
 274          * Talk to the kernel.
 275          */
 276 
 277         return (dispatch(nvlpp, nvlist_callback, (void *)nvlpp));
 278 failed:
 279         errno = rc;
 280         return (-1);
 281 }
 282 #undef  __FN__
 283 
 284 #define __FN__  "ipp_action_info"
 285 int
 286 ipp_action_info(
 287         const char      *aname,
 288         int             (*fn)(nvlist_t *, void *),
 289         void            *arg,
 290         ipp_flags_t     flags)
 291 {
 292         nvlist_t        *nvlp;
 293         int             rc;
 294 
 295         /*
 296          * Sanity check the arguments.
 297          */
 298 
 299         if (aname == NULL || fn == NULL) {
 300                 DBG0(DBG_ERR, "bad argument\n");
 301                 errno = EINVAL;
 302                 return (-1);
 303         }
 304 
 305         /*
 306          * Create an nvlist for our data.
 307          */
 308 
 309         if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
 310                 DBG0(DBG_ERR, "failed to allocate nvlist\n");
 311                 nvlp = NULL;
 312         }
 313 
 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;
 318         }
 319 
 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;
 323         }
 324 
 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;
 328         }
 329 
 330         /*
 331          * Talk to the kernel.
 332          */
 333 
 334         return (dispatch(&nvlp, fn, arg));
 335 failed:
 336         nvlist_free(nvlp);
 337         errno = rc;
 338         return (-1);
 339 }
 340 #undef  __FN__
 341 
 342 #define __FN__  "ipp_action_mod"
 343 int
 344 ipp_action_mod(
 345         const char      *aname,
 346         char            **modnamep)
 347 {
 348         nvlist_t        *nvlp;
 349         int             rc;
 350 
 351         /*
 352          * Sanity check the arguments.
 353          */
 354 
 355         if (aname == NULL || modnamep == NULL) {
 356                 DBG0(DBG_ERR, "bad argument\n");
 357                 errno = EINVAL;
 358                 return (-1);
 359         }
 360 
 361         /*
 362          * Create an nvlist for our data.
 363          */
 364 
 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;
 369         }
 370 
 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;
 375         }
 376 
 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;
 380         }
 381 
 382         /*
 383          * Talk to the kernel.
 384          */
 385 
 386         return (dispatch(&nvlp, string_callback, (void *)modnamep));
 387 failed:
 388         nvlist_free(nvlp);
 389         errno = rc;
 390         return (-1);
 391 }
 392 #undef  __FN__
 393 
 394 #define __FN__  "ipp_list_mods"
 395 int
 396 ipp_list_mods(
 397         char            ***modname_arrayp,
 398         int             *neltp)
 399 {
 400         nvlist_t        *nvlp;
 401         array_desc_t    ad;
 402         int             rc;
 403 
 404         /*
 405          * Sanity check the arguments.
 406          */
 407 
 408         if (modname_arrayp == NULL || neltp == NULL) {
 409                 DBG0(DBG_ERR, "bad argument");
 410                 errno = EINVAL;
 411                 return (-1);
 412         }
 413 
 414         /*
 415          * Create an nvlist for our data.
 416          */
 417 
 418         if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
 419                 DBG0(DBG_ERR, "failed to allocate nvlist\n");
 420                 nvlp = NULL;
 421         }
 422 
 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;
 427         }
 428 
 429         /*
 430          * Talk to the kernel.
 431          */
 432 
 433         ad.name = IPPCTL_MODNAME_ARRAY;
 434         ad.array = NULL;
 435         ad.nelt = 0;
 436 
 437         if ((rc = dispatch(&nvlp, string_array_callback, (void *)&ad)) == 0) {
 438                 *modname_arrayp = ad.array;
 439                 *neltp = ad.nelt;
 440         }
 441 
 442         return (rc);
 443 failed:
 444         nvlist_free(nvlp);
 445         errno = rc;
 446         return (-1);
 447 }
 448 #undef  __FN__
 449 
 450 #define __FN__  "ipp_mod_list_actions"
 451 int
 452 ipp_mod_list_actions(
 453         const char      *modname,
 454         char            ***aname_arrayp,
 455         int             *neltp)
 456 {
 457         nvlist_t        *nvlp;
 458         array_desc_t    ad;
 459         int             rc;
 460 
 461         /*
 462          * Sanity check the arguments.
 463          */
 464 
 465         if (modname == NULL || aname_arrayp == NULL || neltp == NULL) {
 466                 DBG0(DBG_ERR, "bad argument");
 467                 errno = EINVAL;
 468                 return (-1);
 469         }
 470 
 471         /*
 472          * Create an nvlist for our data.
 473          */
 474 
 475         if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
 476                 DBG0(DBG_ERR, "failed to allocate nvlist\n");
 477                 nvlp = NULL;
 478         }
 479 
 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;
 484         }
 485 
 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;
 490         }
 491 
 492         /*
 493          * Talk to the kernel.
 494          */
 495 
 496         ad.name = IPPCTL_ANAME_ARRAY;
 497         ad.array = NULL;
 498         ad.nelt = 0;
 499 
 500         if ((rc = dispatch(&nvlp, string_array_callback, (void *)&ad)) == 0) {
 501                 *aname_arrayp = ad.array;
 502                 *neltp = ad.nelt;
 503         }
 504 
 505         return (rc);
 506 failed:
 507         nvlist_free(nvlp);
 508         errno = rc;
 509         return (-1);
 510 }
 511 #undef  __FN__
 512 
 513 #define __FN__  "ipp_free"
 514 void
 515 ipp_free(
 516         char    *buf)
 517 {
 518         free(buf);
 519 }
 520 #undef  __FN__
 521 
 522 #define __FN__  "ipp_free_array"
 523 void
 524 ipp_free_array(
 525         char    **array,
 526         int     nelt)
 527 {
 528         int     i;
 529 
 530         assert(array[nelt] == NULL);
 531 
 532         for (i = 0; i < nelt; i++)
 533                 free(array[i]);
 534 
 535         free(array);
 536 }
 537 #undef  __FN__
 538 
 539 #define __FN__  "nvlist_callback"
 540 static int
 541 nvlist_callback(
 542         nvlist_t        *nvlp,
 543         void            *arg)
 544 {
 545         nvlist_t        **nvlpp = (nvlist_t **)arg;
 546         int             rc;
 547 
 548         /*
 549          * Callback function used by ipp_action_create() and
 550          * ipp_action_modify()
 551          */
 552 
 553         DBG0(DBG_IO, "called\n");
 554 
 555         assert(nvlpp != NULL);
 556         assert(*nvlpp == NULL);
 557 
 558         /*
 559          * Duplicate the nvlist and set the given pointer to point at the new
 560          * copy.
 561          */
 562 
 563         if ((rc = nvlist_dup(nvlp, nvlpp, 0)) != 0) {
 564                 DBG0(DBG_ERR, "failed to dup nvlist\n");
 565                 errno = rc;
 566                 return (-1);
 567         }
 568 
 569         return (0);
 570 }
 571 #undef  __FN__
 572 
 573 #define __FN__  "string_callback"
 574 static int
 575 string_callback(
 576         nvlist_t        *nvlp,
 577         void            *arg)
 578 {
 579         char            **namep = (char **)arg;
 580         char            *name;
 581         char            *ptr;
 582         int             rc;
 583 
 584         /*
 585          * Callback function used by ipp_action_mod()
 586          */
 587 
 588         DBG0(DBG_IO, "called\n");
 589 
 590         assert(namep != NULL);
 591 
 592         /*
 593          * Look up the module name from the nvlist.
 594          */
 595 
 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);
 600         }
 601 
 602         /*
 603          * Allocate a duplicate string.
 604          */
 605 
 606         if ((name = strdup(ptr)) == NULL) {
 607                 DBG0(DBG_ERR, "failed to duplicate string\n");
 608                 return (-1);
 609         }
 610 
 611         /*
 612          * Set the given pointer to point at the string.
 613          */
 614 
 615         *namep = name;
 616         return (0);
 617 }
 618 #undef  __FN__
 619 
 620 #define __FN__  "string_array_callback"
 621 static int
 622 string_array_callback(
 623         nvlist_t        *nvlp,
 624         void            *arg)
 625 {
 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;
 632 
 633         /*
 634          * Callback function used by ipp_list_mods()
 635          */
 636 
 637         DBG0(DBG_IO, "called\n");
 638 
 639         assert(adp != NULL);
 640 
 641         /*
 642          * Look up the module name from the nvlist.
 643          */
 644 
 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);
 650         }
 651 
 652         /*
 653          * Allocate an array.
 654          */
 655 
 656         if ((dst = malloc((nelt + 1) * sizeof (char *))) == NULL) {
 657                 DBG0(DBG_ERR, "failed to allocate new array\n");
 658                 return (-1);
 659         }
 660 
 661         /*
 662          * For each string in the array, allocate a new buffer and copy
 663          * the string into it.
 664          */
 665 
 666         for (i = 0; i < nelt; i++) {
 667                 if ((dst[i] = strdup(src[i])) == NULL) {
 668                         while (--i >= 0) {
 669                                 free(dst[i]);
 670                         }
 671                         free(dst);
 672                         DBG0(DBG_ERR, "failed to duplicate array\n");
 673                         return (-1);
 674                 }
 675         }
 676         dst[nelt] = NULL;
 677 
 678         /*
 679          * Set the information to be passed back.
 680          */
 681 
 682         adp->array = dst;
 683         adp->nelt = nelt;
 684 
 685         return (0);
 686 }
 687 #undef  __FN__
 688 
 689 #define __FN__  "dispatch"
 690 static int
 691 dispatch(
 692         nvlist_t        **nvlpp,
 693         int             (*fn)(nvlist_t *, void *),
 694         void            *arg)
 695 {
 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;
 709 
 710         /*
 711          * Sanity check the 'command' nvlist.
 712          */
 713 
 714         cnvlp = *nvlpp;
 715         if (cnvlp == NULL) {
 716                 rc = EINVAL;
 717                 return (-1);
 718         }
 719 
 720         /*
 721          * Pack the nvlist and then free the original.
 722          */
 723 
 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);
 730         }
 731         nvlist_free(cnvlp);
 732         *nvlpp = NULL;
 733 
 734         /*
 735          * Open the control device node.
 736          */
 737 
 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;
 742         }
 743 
 744         /*
 745          * Set up an ioctl structure to point at the packed nvlist.
 746          */
 747 
 748         iioc.ii_buf = cbuf;
 749         iioc.ii_buflen = cbuflen;
 750 
 751         /*
 752          * Issue a command ioctl, passing the ioctl structure.
 753          */
 754 
 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;
 759         }
 760 
 761         /*
 762          * Get back the length of the first data buffer.
 763          */
 764 
 765         if ((nextbuflen = (size_t)rc) == 0) {
 766                 DBG0(DBG_ERR, "no data buffer\n");
 767                 errno = EPROTO;
 768                 goto command_failed;
 769         }
 770 
 771         /*
 772          * Try to re-use the command buffer as the first data buffer.
 773          */
 774 
 775         dbuf = cbuf;
 776         thisbuflen = cbuflen;
 777 
 778         count = 0;
 779         while (nextbuflen != 0) {
 780                 dbuflen = nextbuflen;
 781 
 782                 /*
 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.
 786                  */
 787 
 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;
 793                         }
 794                         thisbuflen = nextbuflen;
 795                 }
 796 
 797                 /*
 798                  * Set up an ioctl structure to point at the data buffer.
 799                  */
 800 
 801                 iioc.ii_buf = dbuf;
 802                 iioc.ii_buflen = dbuflen;
 803 
 804                 /*
 805                  * Issue a data ioctl, passing the ioctl structure.
 806                  */
 807 
 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;
 812                 }
 813 
 814                 /*
 815                  * Get the length of the *next* data buffer, if there is
 816                  * one.
 817                  */
 818 
 819                 nextbuflen = (size_t)rc;
 820                 DBG1(DBG_IO, "nextbuflen = %d\n", nextbuflen);
 821 
 822                 /*
 823                  * Unpack the nvlist that the current data buffer should
 824                  * now contain.
 825                  */
 826 
 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;
 831                 }
 832 
 833                 /*
 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.
 837                  */
 838 
 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;
 846                         }
 847                 } else {
 848                         if (fn != NULL)
 849                                 if (fn(dnvlp, arg) != 0) {
 850 
 851                                         /*
 852                                          * The callback function returned
 853                                          * a non-zero value. Abort any further
 854                                          * data collection.
 855                                          */
 856 
 857                                         nvlist_free(dnvlp);
 858                                         free(dbuf);
 859                                 }
 860                 }
 861 
 862                 /*
 863                  * Free the nvlist now that we have extracted the return
 864                  * code or called the callback function.
 865                  */
 866 
 867                 nvlist_free(dnvlp);
 868                 dnvlp = NULL;
 869 
 870                 count++;
 871         }
 872 
 873         /*
 874          * Free the data buffer as data collection is now complete.
 875          */
 876 
 877         free(dbuf);
 878 
 879         /*
 880          * Close the control device.
 881          */
 882 
 883         (void) close(fd);
 884 
 885         /*
 886          * If the kernel returned an error, we should return an error.
 887          * and set errno.
 888          */
 889 
 890         if (rval != 0) {
 891                 DBG1(DBG_IO, "kernel return code = %d\n", rval);
 892                 errno = rval;
 893                 return (-1);
 894         }
 895 
 896         return (0);
 897 
 898 command_failed:
 899         free(cbuf);
 900         if (fd != -1)
 901                 (void) close(fd);
 902         return (-1);
 903 
 904 data_failed:
 905         if (dbuf != NULL)
 906                 free(dbuf);
 907         (void) close(fd);
 908         return (-1);
 909 }
 910 #undef  __FN__