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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 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 <unistd.h>
  30 #include <fcntl.h>
  31 #include <errno.h>
  32 #include <strings.h>
  33 #include <alloca.h>
  34 #include <door.h>
  35 #include <pthread.h>
  36 #include <synch.h>
  37 #include <pwd.h>
  38 #include <auth_list.h>
  39 #include <auth_attr.h>
  40 #include <bsm/adt.h>
  41 #include <bsm/adt_event.h>
  42 #include <sys/sunddi.h>
  43 #include <sys/ddi_hp.h>
  44 #include <libnvpair.h>
  45 #include <libhotplug.h>
  46 #include <libhotplug_impl.h>
  47 #include "hotplugd_impl.h"
  48 
  49 /*
  50  * Buffer management for results.
  51  */
  52 typedef struct i_buffer {
  53         uint64_t        seqnum;
  54         char            *buffer;
  55         struct i_buffer *next;
  56 } i_buffer_t;
  57 
  58 static uint64_t         buffer_seqnum = 1;
  59 static i_buffer_t       *buffer_list = NULL;
  60 static pthread_mutex_t  buffer_lock = PTHREAD_MUTEX_INITIALIZER;
  61 
  62 /*
  63  * Door file descriptor.
  64  */
  65 static int      door_fd = -1;
  66 
  67 /*
  68  * Function prototypes.
  69  */
  70 static void     door_server(void *, char *, size_t, door_desc_t *, uint_t);
  71 static int      check_auth(ucred_t *, const char *);
  72 static int      cmd_getinfo(nvlist_t *, nvlist_t **);
  73 static int      cmd_changestate(nvlist_t *, nvlist_t **);
  74 static int      cmd_private(hp_cmd_t, nvlist_t *, nvlist_t **);
  75 static void     add_buffer(uint64_t, char *);
  76 static void     free_buffer(uint64_t);
  77 static uint64_t get_seqnum(void);
  78 static char     *state_str(int);
  79 static int      audit_session(ucred_t *, adt_session_data_t **);
  80 static void     audit_changestate(ucred_t *, char *, char *, char *, int, int,
  81                     int);
  82 static void     audit_setprivate(ucred_t *, char *, char *, char *, char *,
  83                     int);
  84 
  85 /*
  86  * door_server_init()
  87  *
  88  *      Create the door file, and initialize the door server.
  89  */
  90 boolean_t
  91 door_server_init(void)
  92 {
  93         int     fd;
  94 
  95         /* Create the door file */
  96         if ((fd = open(HOTPLUGD_DOOR, O_CREAT|O_EXCL|O_RDONLY, 0644)) == -1) {
  97                 if (errno == EEXIST) {
  98                         log_err("Door service is already running.\n");
  99                 } else {
 100                         log_err("Cannot open door file '%s': %s\n",
 101                             HOTPLUGD_DOOR, strerror(errno));
 102                 }
 103                 return (B_FALSE);
 104         }
 105         (void) close(fd);
 106 
 107         /* Initialize the door service */
 108         if ((door_fd = door_create(door_server, NULL,
 109             DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
 110                 log_err("Cannot create door service: %s\n", strerror(errno));
 111                 return (B_FALSE);
 112         }
 113 
 114         /* Cleanup stale door associations */
 115         (void) fdetach(HOTPLUGD_DOOR);
 116 
 117         /* Associate door service with door file */
 118         if (fattach(door_fd, HOTPLUGD_DOOR) != 0) {
 119                 log_err("Cannot attach to door file '%s': %s\n", HOTPLUGD_DOOR,
 120                     strerror(errno));
 121                 (void) door_revoke(door_fd);
 122                 (void) fdetach(HOTPLUGD_DOOR);
 123                 door_fd = -1;
 124                 return (B_FALSE);
 125         }
 126 
 127         return (B_TRUE);
 128 }
 129 
 130 /*
 131  * door_server_fini()
 132  *
 133  *      Terminate and cleanup the door server.
 134  */
 135 void
 136 door_server_fini(void)
 137 {
 138         if (door_fd != -1) {
 139                 (void) door_revoke(door_fd);
 140                 (void) fdetach(HOTPLUGD_DOOR);
 141         }
 142 
 143         (void) unlink(HOTPLUGD_DOOR);
 144 }
 145 
 146 /*
 147  * door_server()
 148  *
 149  *      This routine is the handler which responds to each door call.
 150  *      Each incoming door call is expected to send a packed nvlist
 151  *      of arguments which describe the requested action.  And each
 152  *      response is sent back as a packed nvlist of results.
 153  *
 154  *      Results are always allocated on the heap.  A global list of
 155  *      allocated result buffers is managed, and each one is tracked
 156  *      by a unique sequence number.  The final step in the protocol
 157  *      is for the caller to send a short response using the sequence
 158  *      number when the buffer can be released.
 159  */
 160 /*ARGSUSED*/
 161 static void
 162 door_server(void *cookie, char *argp, size_t sz, door_desc_t *dp, uint_t ndesc)
 163 {
 164         nvlist_t        *args = NULL;
 165         nvlist_t        *results = NULL;
 166         hp_cmd_t        cmd;
 167         int             rv;
 168 
 169         dprintf("Door call: cookie=%p, argp=%p, sz=%d\n", cookie, (void *)argp,
 170             sz);
 171 
 172         /* Special case to free a results buffer */
 173         if (sz == sizeof (uint64_t)) {
 174                 free_buffer(*(uint64_t *)(uintptr_t)argp);
 175                 (void) door_return(NULL, 0, NULL, 0);
 176                 return;
 177         }
 178 
 179         /* Unpack the arguments nvlist */
 180         if (nvlist_unpack(argp, sz, &args, 0) != 0) {
 181                 log_err("Cannot unpack door arguments.\n");
 182                 rv = EINVAL;
 183                 goto fail;
 184         }
 185 
 186         /* Extract the requested command */
 187         if (nvlist_lookup_int32(args, HPD_CMD, (int32_t *)&cmd) != 0) {
 188                 log_err("Cannot decode door command.\n");
 189                 rv = EINVAL;
 190                 goto fail;
 191         }
 192 
 193         /* Implement the command */
 194         switch (cmd) {
 195         case HP_CMD_GETINFO:
 196                 rv = cmd_getinfo(args, &results);
 197                 break;
 198         case HP_CMD_CHANGESTATE:
 199                 rv = cmd_changestate(args, &results);
 200                 break;
 201         case HP_CMD_SETPRIVATE:
 202         case HP_CMD_GETPRIVATE:
 203                 rv = cmd_private(cmd, args, &results);
 204                 break;
 205         default:
 206                 rv = EINVAL;
 207                 break;
 208         }
 209 
 210         /* The arguments nvlist is no longer needed */
 211         nvlist_free(args);
 212         args = NULL;
 213 
 214         /*
 215          * If an nvlist was constructed for the results,
 216          * then pack the results nvlist and return it.
 217          */
 218         if (results != NULL) {
 219                 uint64_t        seqnum;
 220                 char            *buf = NULL;
 221                 size_t          len = 0;
 222 
 223                 /* Add a sequence number to the results */
 224                 seqnum = get_seqnum();
 225                 if (nvlist_add_uint64(results, HPD_SEQNUM, seqnum) != 0) {
 226                         log_err("Cannot add sequence number.\n");
 227                         rv = EFAULT;
 228                         goto fail;
 229                 }
 230 
 231                 /* Pack the results nvlist */
 232                 if (nvlist_pack(results, &buf, &len,
 233                     NV_ENCODE_NATIVE, 0) != 0) {
 234                         log_err("Cannot pack door results.\n");
 235                         rv = EFAULT;
 236                         goto fail;
 237                 }
 238 
 239                 /* Link results buffer into list */
 240                 add_buffer(seqnum, buf);
 241 
 242                 /* The results nvlist is no longer needed */
 243                 nvlist_free(results);
 244 
 245                 /* Return the results */
 246                 (void) door_return(buf, len, NULL, 0);
 247                 return;
 248         }
 249 
 250         /* Return result code (when no nvlist) */
 251         (void) door_return((char *)&rv, sizeof (int), NULL, 0);
 252         return;
 253 
 254 fail:
 255         log_err("Door call failed (%s)\n", strerror(rv));
 256         nvlist_free(args);
 257         nvlist_free(results);
 258         (void) door_return((char *)&rv, sizeof (int), NULL, 0);
 259 }
 260 
 261 /*
 262  * check_auth()
 263  *
 264  *      Perform an RBAC authorization check.
 265  */
 266 static int
 267 check_auth(ucred_t *ucred, const char *auth)
 268 {
 269         struct passwd   pwd;
 270         uid_t           euid;
 271         char            buf[MAXPATHLEN];
 272 
 273         euid = ucred_geteuid(ucred);
 274 
 275         if ((getpwuid_r(euid, &pwd, buf, sizeof (buf)) == NULL) ||
 276             (chkauthattr(auth, pwd.pw_name) == 0)) {
 277                 log_info("Unauthorized door call.\n");
 278                 return (-1);
 279         }
 280 
 281         return (0);
 282 }
 283 
 284 /*
 285  * cmd_getinfo()
 286  *
 287  *      Implements the door command to get a hotplug information snapshot.
 288  */
 289 static int
 290 cmd_getinfo(nvlist_t *args, nvlist_t **resultsp)
 291 {
 292         hp_node_t       root;
 293         nvlist_t        *results;
 294         char            *path;
 295         char            *connection;
 296         char            *buf = NULL;
 297         size_t          len = 0;
 298         uint_t          flags;
 299         int             rv;
 300 
 301         dprintf("cmd_getinfo:\n");
 302 
 303         /* Get arguments */
 304         if (nvlist_lookup_string(args, HPD_PATH, &path) != 0) {
 305                 dprintf("cmd_getinfo: invalid arguments.\n");
 306                 return (EINVAL);
 307         }
 308         if (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0)
 309                 connection = NULL;
 310         if (nvlist_lookup_uint32(args, HPD_FLAGS, (uint32_t *)&flags) != 0)
 311                 flags = 0;
 312 
 313         /* Get and pack the requested snapshot */
 314         if ((rv = getinfo(path, connection, flags, &root)) == 0) {
 315                 rv = hp_pack(root, &buf, &len);
 316                 hp_fini(root);
 317         }
 318         dprintf("cmd_getinfo: getinfo(): rv = %d, buf = %p.\n", rv,
 319             (void *)buf);
 320 
 321         /*
 322          * If the above failed or there is no snapshot,
 323          * then only return a status code.
 324          */
 325         if (rv != 0)
 326                 return (rv);
 327         if (buf == NULL)
 328                 return (EFAULT);
 329 
 330         /* Allocate nvlist for results */
 331         if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) {
 332                 dprintf("cmd_getinfo: nvlist_alloc() failed.\n");
 333                 free(buf);
 334                 return (ENOMEM);
 335         }
 336 
 337         /* Add snapshot and successful status to results */
 338         if ((nvlist_add_int32(results, HPD_STATUS, 0) != 0) ||
 339             (nvlist_add_byte_array(results, HPD_INFO,
 340             (uchar_t *)buf, len) != 0)) {
 341                 dprintf("cmd_getinfo: nvlist add failure.\n");
 342                 nvlist_free(results);
 343                 free(buf);
 344                 return (ENOMEM);
 345         }
 346 
 347         /* Packed snapshot no longer needed */
 348         free(buf);
 349 
 350         /* Success */
 351         *resultsp = results;
 352         return (0);
 353 }
 354 
 355 /*
 356  * cmd_changestate()
 357  *
 358  *      Implements the door command to initate a state change operation.
 359  *
 360  *      NOTE: requires 'modify' authorization.
 361  */
 362 static int
 363 cmd_changestate(nvlist_t *args, nvlist_t **resultsp)
 364 {
 365         hp_node_t       root = NULL;
 366         nvlist_t        *results = NULL;
 367         char            *path, *connection;
 368         ucred_t         *uc = NULL;
 369         uint_t          flags;
 370         int             rv, state, old_state, status;
 371 
 372         dprintf("cmd_changestate:\n");
 373 
 374         /* Get arguments */
 375         if ((nvlist_lookup_string(args, HPD_PATH, &path) != 0) ||
 376             (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0) ||
 377             (nvlist_lookup_int32(args, HPD_STATE, &state) != 0)) {
 378                 dprintf("cmd_changestate: invalid arguments.\n");
 379                 return (EINVAL);
 380         }
 381         if (nvlist_lookup_uint32(args, HPD_FLAGS, (uint32_t *)&flags) != 0)
 382                 flags = 0;
 383 
 384         /* Get caller's credentials */
 385         if (door_ucred(&uc) != 0) {
 386                 log_err("Cannot get door credentials (%s)\n", strerror(errno));
 387                 return (EACCES);
 388         }
 389 
 390         /* Check authorization */
 391         if (check_auth(uc, HP_MODIFY_AUTH) != 0) {
 392                 dprintf("cmd_changestate: access denied.\n");
 393                 audit_changestate(uc, HP_MODIFY_AUTH, path, connection,
 394                     state, -1, ADT_FAIL_VALUE_AUTH);
 395                 ucred_free(uc);
 396                 return (EACCES);
 397         }
 398 
 399         /* Perform the state change operation */
 400         status = changestate(path, connection, state, flags, &old_state, &root);
 401         dprintf("cmd_changestate: changestate() == %d\n", status);
 402 
 403         /* Audit the operation */
 404         audit_changestate(uc, HP_MODIFY_AUTH, path, connection, state,
 405             old_state, status);
 406 
 407         /* Caller's credentials no longer needed */
 408         ucred_free(uc);
 409 
 410         /*
 411          * Pack the results into an nvlist if there is an error snapshot.
 412          *
 413          * If any error occurs while packing the results, the original
 414          * error code from changestate() above is still returned.
 415          */
 416         if (root != NULL) {
 417                 char    *buf = NULL;
 418                 size_t  len = 0;
 419 
 420                 dprintf("cmd_changestate: results nvlist required.\n");
 421 
 422                 /* Pack and discard the error snapshot */
 423                 rv = hp_pack(root, &buf, &len);
 424                 hp_fini(root);
 425                 if (rv != 0) {
 426                         dprintf("cmd_changestate: hp_pack() failed (%s).\n",
 427                             strerror(rv));
 428                         return (status);
 429                 }
 430 
 431                 /* Allocate nvlist for results */
 432                 if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) {
 433                         dprintf("cmd_changestate: nvlist_alloc() failed.\n");
 434                         free(buf);
 435                         return (status);
 436                 }
 437 
 438                 /* Add the results into the nvlist */
 439                 if ((nvlist_add_int32(results, HPD_STATUS, status) != 0) ||
 440                     (nvlist_add_byte_array(results, HPD_INFO, (uchar_t *)buf,
 441                     len) != 0)) {
 442                         dprintf("cmd_changestate: nvlist add failed.\n");
 443                         nvlist_free(results);
 444                         free(buf);
 445                         return (status);
 446                 }
 447 
 448                 *resultsp = results;
 449         }
 450 
 451         return (status);
 452 }
 453 
 454 /*
 455  * cmd_private()
 456  *
 457  *      Implementation of the door command to set or get bus private options.
 458  *
 459  *      NOTE: requires 'modify' authorization for the 'set' command.
 460  */
 461 static int
 462 cmd_private(hp_cmd_t cmd, nvlist_t *args, nvlist_t **resultsp)
 463 {
 464         nvlist_t        *results = NULL;
 465         ucred_t         *uc = NULL;
 466         char            *path, *connection, *options;
 467         char            *values = NULL;
 468         int             status;
 469 
 470         dprintf("cmd_private:\n");
 471 
 472         /* Get caller's credentials */
 473         if ((cmd == HP_CMD_SETPRIVATE) && (door_ucred(&uc) != 0)) {
 474                 log_err("Cannot get door credentials (%s)\n", strerror(errno));
 475                 return (EACCES);
 476         }
 477 
 478         /* Get arguments */
 479         if ((nvlist_lookup_string(args, HPD_PATH, &path) != 0) ||
 480             (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0) ||
 481             (nvlist_lookup_string(args, HPD_OPTIONS, &options) != 0)) {
 482                 dprintf("cmd_private: invalid arguments.\n");
 483                 return (EINVAL);
 484         }
 485 
 486         /* Check authorization */
 487         if ((cmd == HP_CMD_SETPRIVATE) &&
 488             (check_auth(uc, HP_MODIFY_AUTH) != 0)) {
 489                 dprintf("cmd_private: access denied.\n");
 490                 audit_setprivate(uc, HP_MODIFY_AUTH, path, connection, options,
 491                     ADT_FAIL_VALUE_AUTH);
 492                 ucred_free(uc);
 493                 return (EACCES);
 494         }
 495 
 496         /* Perform the operation */
 497         status = private_options(path, connection, cmd, options, &values);
 498         dprintf("cmd_private: private_options() == %d\n", status);
 499 
 500         /* Audit the operation */
 501         if (cmd == HP_CMD_SETPRIVATE) {
 502                 audit_setprivate(uc, HP_MODIFY_AUTH, path, connection, options,
 503                     status);
 504                 ucred_free(uc);
 505         }
 506 
 507         /* Construct an nvlist if values were returned */
 508         if (values != NULL) {
 509 
 510                 /* Allocate nvlist for results */
 511                 if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) {
 512                         dprintf("cmd_private: nvlist_alloc() failed.\n");
 513                         free(values);
 514                         return (ENOMEM);
 515                 }
 516 
 517                 /* Add values and status to the results */
 518                 if ((nvlist_add_int32(results, HPD_STATUS, status) != 0) ||
 519                     (nvlist_add_string(results, HPD_OPTIONS, values) != 0)) {
 520                         dprintf("cmd_private: nvlist add failed.\n");
 521                         nvlist_free(results);
 522                         free(values);
 523                         return (ENOMEM);
 524                 }
 525 
 526                 /* The values string is no longer needed */
 527                 free(values);
 528 
 529                 *resultsp = results;
 530         }
 531 
 532         return (status);
 533 }
 534 
 535 /*
 536  * get_seqnum()
 537  *
 538  *      Allocate the next unique sequence number for a results buffer.
 539  */
 540 static uint64_t
 541 get_seqnum(void)
 542 {
 543         uint64_t seqnum;
 544 
 545         (void) pthread_mutex_lock(&buffer_lock);
 546 
 547         seqnum = buffer_seqnum++;
 548 
 549         (void) pthread_mutex_unlock(&buffer_lock);
 550 
 551         return (seqnum);
 552 }
 553 
 554 /*
 555  * add_buffer()
 556  *
 557  *      Link a results buffer into the list containing all buffers.
 558  */
 559 static void
 560 add_buffer(uint64_t seqnum, char *buf)
 561 {
 562         i_buffer_t      *node;
 563 
 564         if ((node = (i_buffer_t *)malloc(sizeof (i_buffer_t))) == NULL) {
 565                 /* The consequence is a memory leak. */
 566                 log_err("Cannot allocate results buffer: %s\n",
 567                     strerror(errno));
 568                 return;
 569         }
 570 
 571         node->seqnum = seqnum;
 572         node->buffer = buf;
 573 
 574         (void) pthread_mutex_lock(&buffer_lock);
 575 
 576         node->next = buffer_list;
 577         buffer_list = node;
 578 
 579         (void) pthread_mutex_unlock(&buffer_lock);
 580 }
 581 
 582 /*
 583  * free_buffer()
 584  *
 585  *      Remove a results buffer from the list containing all buffers.
 586  */
 587 static void
 588 free_buffer(uint64_t seqnum)
 589 {
 590         i_buffer_t      *node, *prev;
 591 
 592         (void) pthread_mutex_lock(&buffer_lock);
 593 
 594         prev = NULL;
 595         node = buffer_list;
 596 
 597         while (node) {
 598                 if (node->seqnum == seqnum) {
 599                         dprintf("Free buffer %lld\n", seqnum);
 600                         if (prev) {
 601                                 prev->next = node->next;
 602                         } else {
 603                                 buffer_list = node->next;
 604                         }
 605                         free(node->buffer);
 606                         free(node);
 607                         break;
 608                 }
 609                 prev = node;
 610                 node = node->next;
 611         }
 612 
 613         (void) pthread_mutex_unlock(&buffer_lock);
 614 }
 615 
 616 /*
 617  * audit_session()
 618  *
 619  *      Initialize an audit session.
 620  */
 621 static int
 622 audit_session(ucred_t *ucred, adt_session_data_t **sessionp)
 623 {
 624         adt_session_data_t      *session;
 625 
 626         if (adt_start_session(&session, NULL, 0) != 0) {
 627                 log_err("Cannot start audit session.\n");
 628                 return (-1);
 629         }
 630 
 631         if (adt_set_from_ucred(session, ucred, ADT_NEW) != 0) {
 632                 log_err("Cannot set audit session from ucred.\n");
 633                 (void) adt_end_session(session);
 634                 return (-1);
 635         }
 636 
 637         *sessionp = session;
 638         return (0);
 639 }
 640 
 641 /*
 642  * audit_changestate()
 643  *
 644  *      Audit a 'changestate' door command.
 645  */
 646 static void
 647 audit_changestate(ucred_t *ucred, char *auth, char *path, char *connection,
 648     int new_state, int old_state, int result)
 649 {
 650         adt_session_data_t      *session;
 651         adt_event_data_t        *event;
 652         int                     pass_fail, fail_reason;
 653 
 654         if (audit_session(ucred, &session) != 0)
 655                 return;
 656 
 657         if ((event = adt_alloc_event(session, ADT_hotplug_state)) == NULL) {
 658                 (void) adt_end_session(session);
 659                 return;
 660         }
 661 
 662         if (result == 0) {
 663                 pass_fail = ADT_SUCCESS;
 664                 fail_reason = ADT_SUCCESS;
 665         } else {
 666                 pass_fail = ADT_FAILURE;
 667                 fail_reason = result;
 668         }
 669 
 670         event->adt_hotplug_state.auth_used = auth;
 671         event->adt_hotplug_state.device_path = path;
 672         event->adt_hotplug_state.connection = connection;
 673         event->adt_hotplug_state.new_state = state_str(new_state);
 674         event->adt_hotplug_state.old_state = state_str(old_state);
 675 
 676         /* Put the event */
 677         if (adt_put_event(event, pass_fail, fail_reason) != 0)
 678                 log_err("Cannot put audit event.\n");
 679 
 680         adt_free_event(event);
 681         (void) adt_end_session(session);
 682 }
 683 
 684 /*
 685  * audit_setprivate()
 686  *
 687  *      Audit a 'set private' door command.
 688  */
 689 static void
 690 audit_setprivate(ucred_t *ucred, char *auth, char *path, char *connection,
 691     char *options, int result)
 692 {
 693         adt_session_data_t      *session;
 694         adt_event_data_t        *event;
 695         int                     pass_fail, fail_reason;
 696 
 697         if (audit_session(ucred, &session) != 0)
 698                 return;
 699 
 700         if ((event = adt_alloc_event(session, ADT_hotplug_set)) == NULL) {
 701                 (void) adt_end_session(session);
 702                 return;
 703         }
 704 
 705         if (result == 0) {
 706                 pass_fail = ADT_SUCCESS;
 707                 fail_reason = ADT_SUCCESS;
 708         } else {
 709                 pass_fail = ADT_FAILURE;
 710                 fail_reason = result;
 711         }
 712 
 713         event->adt_hotplug_set.auth_used = auth;
 714         event->adt_hotplug_set.device_path = path;
 715         event->adt_hotplug_set.connection = connection;
 716         event->adt_hotplug_set.options = options;
 717 
 718         /* Put the event */
 719         if (adt_put_event(event, pass_fail, fail_reason) != 0)
 720                 log_err("Cannot put audit event.\n");
 721 
 722         adt_free_event(event);
 723         (void) adt_end_session(session);
 724 }
 725 
 726 /*
 727  * state_str()
 728  *
 729  *      Convert a state from integer to string.
 730  */
 731 static char *
 732 state_str(int state)
 733 {
 734         switch (state) {
 735         case DDI_HP_CN_STATE_EMPTY:
 736                 return ("EMPTY");
 737         case DDI_HP_CN_STATE_PRESENT:
 738                 return ("PRESENT");
 739         case DDI_HP_CN_STATE_POWERED:
 740                 return ("POWERED");
 741         case DDI_HP_CN_STATE_ENABLED:
 742                 return ("ENABLED");
 743         case DDI_HP_CN_STATE_PORT_EMPTY:
 744                 return ("PORT-EMPTY");
 745         case DDI_HP_CN_STATE_PORT_PRESENT:
 746                 return ("PORT-PRESENT");
 747         case DDI_HP_CN_STATE_OFFLINE:
 748                 return ("OFFLINE");
 749         case DDI_HP_CN_STATE_ATTACHED:
 750                 return ("ATTACHED");
 751         case DDI_HP_CN_STATE_MAINTENANCE:
 752                 return ("MAINTENANCE");
 753         case DDI_HP_CN_STATE_ONLINE:
 754                 return ("ONLINE");
 755         default:
 756                 return ("UNKNOWN");
 757         }
 758 }