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         if (args != NULL)
 257                 nvlist_free(args);
 258         if (results != NULL)
 259                 nvlist_free(results);
 260         (void) door_return((char *)&rv, sizeof (int), NULL, 0);
 261 }
 262 
 263 /*
 264  * check_auth()
 265  *
 266  *      Perform an RBAC authorization check.
 267  */
 268 static int
 269 check_auth(ucred_t *ucred, const char *auth)
 270 {
 271         struct passwd   pwd;
 272         uid_t           euid;
 273         char            buf[MAXPATHLEN];
 274 
 275         euid = ucred_geteuid(ucred);
 276 
 277         if ((getpwuid_r(euid, &pwd, buf, sizeof (buf)) == NULL) ||
 278             (chkauthattr(auth, pwd.pw_name) == 0)) {
 279                 log_info("Unauthorized door call.\n");
 280                 return (-1);
 281         }
 282 
 283         return (0);
 284 }
 285 
 286 /*
 287  * cmd_getinfo()
 288  *
 289  *      Implements the door command to get a hotplug information snapshot.
 290  */
 291 static int
 292 cmd_getinfo(nvlist_t *args, nvlist_t **resultsp)
 293 {
 294         hp_node_t       root;
 295         nvlist_t        *results;
 296         char            *path;
 297         char            *connection;
 298         char            *buf = NULL;
 299         size_t          len = 0;
 300         uint_t          flags;
 301         int             rv;
 302 
 303         dprintf("cmd_getinfo:\n");
 304 
 305         /* Get arguments */
 306         if (nvlist_lookup_string(args, HPD_PATH, &path) != 0) {
 307                 dprintf("cmd_getinfo: invalid arguments.\n");
 308                 return (EINVAL);
 309         }
 310         if (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0)
 311                 connection = NULL;
 312         if (nvlist_lookup_uint32(args, HPD_FLAGS, (uint32_t *)&flags) != 0)
 313                 flags = 0;
 314 
 315         /* Get and pack the requested snapshot */
 316         if ((rv = getinfo(path, connection, flags, &root)) == 0) {
 317                 rv = hp_pack(root, &buf, &len);
 318                 hp_fini(root);
 319         }
 320         dprintf("cmd_getinfo: getinfo(): rv = %d, buf = %p.\n", rv,
 321             (void *)buf);
 322 
 323         /*
 324          * If the above failed or there is no snapshot,
 325          * then only return a status code.
 326          */
 327         if (rv != 0)
 328                 return (rv);
 329         if (buf == NULL)
 330                 return (EFAULT);
 331 
 332         /* Allocate nvlist for results */
 333         if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) {
 334                 dprintf("cmd_getinfo: nvlist_alloc() failed.\n");
 335                 free(buf);
 336                 return (ENOMEM);
 337         }
 338 
 339         /* Add snapshot and successful status to results */
 340         if ((nvlist_add_int32(results, HPD_STATUS, 0) != 0) ||
 341             (nvlist_add_byte_array(results, HPD_INFO,
 342             (uchar_t *)buf, len) != 0)) {
 343                 dprintf("cmd_getinfo: nvlist add failure.\n");
 344                 nvlist_free(results);
 345                 free(buf);
 346                 return (ENOMEM);
 347         }
 348 
 349         /* Packed snapshot no longer needed */
 350         free(buf);
 351 
 352         /* Success */
 353         *resultsp = results;
 354         return (0);
 355 }
 356 
 357 /*
 358  * cmd_changestate()
 359  *
 360  *      Implements the door command to initate a state change operation.
 361  *
 362  *      NOTE: requires 'modify' authorization.
 363  */
 364 static int
 365 cmd_changestate(nvlist_t *args, nvlist_t **resultsp)
 366 {
 367         hp_node_t       root = NULL;
 368         nvlist_t        *results = NULL;
 369         char            *path, *connection;
 370         ucred_t         *uc = NULL;
 371         uint_t          flags;
 372         int             rv, state, old_state, status;
 373 
 374         dprintf("cmd_changestate:\n");
 375 
 376         /* Get arguments */
 377         if ((nvlist_lookup_string(args, HPD_PATH, &path) != 0) ||
 378             (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0) ||
 379             (nvlist_lookup_int32(args, HPD_STATE, &state) != 0)) {
 380                 dprintf("cmd_changestate: invalid arguments.\n");
 381                 return (EINVAL);
 382         }
 383         if (nvlist_lookup_uint32(args, HPD_FLAGS, (uint32_t *)&flags) != 0)
 384                 flags = 0;
 385 
 386         /* Get caller's credentials */
 387         if (door_ucred(&uc) != 0) {
 388                 log_err("Cannot get door credentials (%s)\n", strerror(errno));
 389                 return (EACCES);
 390         }
 391 
 392         /* Check authorization */
 393         if (check_auth(uc, HP_MODIFY_AUTH) != 0) {
 394                 dprintf("cmd_changestate: access denied.\n");
 395                 audit_changestate(uc, HP_MODIFY_AUTH, path, connection,
 396                     state, -1, ADT_FAIL_VALUE_AUTH);
 397                 ucred_free(uc);
 398                 return (EACCES);
 399         }
 400 
 401         /* Perform the state change operation */
 402         status = changestate(path, connection, state, flags, &old_state, &root);
 403         dprintf("cmd_changestate: changestate() == %d\n", status);
 404 
 405         /* Audit the operation */
 406         audit_changestate(uc, HP_MODIFY_AUTH, path, connection, state,
 407             old_state, status);
 408 
 409         /* Caller's credentials no longer needed */
 410         ucred_free(uc);
 411 
 412         /*
 413          * Pack the results into an nvlist if there is an error snapshot.
 414          *
 415          * If any error occurs while packing the results, the original
 416          * error code from changestate() above is still returned.
 417          */
 418         if (root != NULL) {
 419                 char    *buf = NULL;
 420                 size_t  len = 0;
 421 
 422                 dprintf("cmd_changestate: results nvlist required.\n");
 423 
 424                 /* Pack and discard the error snapshot */
 425                 rv = hp_pack(root, &buf, &len);
 426                 hp_fini(root);
 427                 if (rv != 0) {
 428                         dprintf("cmd_changestate: hp_pack() failed (%s).\n",
 429                             strerror(rv));
 430                         return (status);
 431                 }
 432 
 433                 /* Allocate nvlist for results */
 434                 if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) {
 435                         dprintf("cmd_changestate: nvlist_alloc() failed.\n");
 436                         free(buf);
 437                         return (status);
 438                 }
 439 
 440                 /* Add the results into the nvlist */
 441                 if ((nvlist_add_int32(results, HPD_STATUS, status) != 0) ||
 442                     (nvlist_add_byte_array(results, HPD_INFO, (uchar_t *)buf,
 443                     len) != 0)) {
 444                         dprintf("cmd_changestate: nvlist add failed.\n");
 445                         nvlist_free(results);
 446                         free(buf);
 447                         return (status);
 448                 }
 449 
 450                 *resultsp = results;
 451         }
 452 
 453         return (status);
 454 }
 455 
 456 /*
 457  * cmd_private()
 458  *
 459  *      Implementation of the door command to set or get bus private options.
 460  *
 461  *      NOTE: requires 'modify' authorization for the 'set' command.
 462  */
 463 static int
 464 cmd_private(hp_cmd_t cmd, nvlist_t *args, nvlist_t **resultsp)
 465 {
 466         nvlist_t        *results = NULL;
 467         ucred_t         *uc = NULL;
 468         char            *path, *connection, *options;
 469         char            *values = NULL;
 470         int             status;
 471 
 472         dprintf("cmd_private:\n");
 473 
 474         /* Get caller's credentials */
 475         if ((cmd == HP_CMD_SETPRIVATE) && (door_ucred(&uc) != 0)) {
 476                 log_err("Cannot get door credentials (%s)\n", strerror(errno));
 477                 return (EACCES);
 478         }
 479 
 480         /* Get arguments */
 481         if ((nvlist_lookup_string(args, HPD_PATH, &path) != 0) ||
 482             (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0) ||
 483             (nvlist_lookup_string(args, HPD_OPTIONS, &options) != 0)) {
 484                 dprintf("cmd_private: invalid arguments.\n");
 485                 return (EINVAL);
 486         }
 487 
 488         /* Check authorization */
 489         if ((cmd == HP_CMD_SETPRIVATE) &&
 490             (check_auth(uc, HP_MODIFY_AUTH) != 0)) {
 491                 dprintf("cmd_private: access denied.\n");
 492                 audit_setprivate(uc, HP_MODIFY_AUTH, path, connection, options,
 493                     ADT_FAIL_VALUE_AUTH);
 494                 ucred_free(uc);
 495                 return (EACCES);
 496         }
 497 
 498         /* Perform the operation */
 499         status = private_options(path, connection, cmd, options, &values);
 500         dprintf("cmd_private: private_options() == %d\n", status);
 501 
 502         /* Audit the operation */
 503         if (cmd == HP_CMD_SETPRIVATE) {
 504                 audit_setprivate(uc, HP_MODIFY_AUTH, path, connection, options,
 505                     status);
 506                 ucred_free(uc);
 507         }
 508 
 509         /* Construct an nvlist if values were returned */
 510         if (values != NULL) {
 511 
 512                 /* Allocate nvlist for results */
 513                 if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) {
 514                         dprintf("cmd_private: nvlist_alloc() failed.\n");
 515                         free(values);
 516                         return (ENOMEM);
 517                 }
 518 
 519                 /* Add values and status to the results */
 520                 if ((nvlist_add_int32(results, HPD_STATUS, status) != 0) ||
 521                     (nvlist_add_string(results, HPD_OPTIONS, values) != 0)) {
 522                         dprintf("cmd_private: nvlist add failed.\n");
 523                         nvlist_free(results);
 524                         free(values);
 525                         return (ENOMEM);
 526                 }
 527 
 528                 /* The values string is no longer needed */
 529                 free(values);
 530 
 531                 *resultsp = results;
 532         }
 533 
 534         return (status);
 535 }
 536 
 537 /*
 538  * get_seqnum()
 539  *
 540  *      Allocate the next unique sequence number for a results buffer.
 541  */
 542 static uint64_t
 543 get_seqnum(void)
 544 {
 545         uint64_t seqnum;
 546 
 547         (void) pthread_mutex_lock(&buffer_lock);
 548 
 549         seqnum = buffer_seqnum++;
 550 
 551         (void) pthread_mutex_unlock(&buffer_lock);
 552 
 553         return (seqnum);
 554 }
 555 
 556 /*
 557  * add_buffer()
 558  *
 559  *      Link a results buffer into the list containing all buffers.
 560  */
 561 static void
 562 add_buffer(uint64_t seqnum, char *buf)
 563 {
 564         i_buffer_t      *node;
 565 
 566         if ((node = (i_buffer_t *)malloc(sizeof (i_buffer_t))) == NULL) {
 567                 /* The consequence is a memory leak. */
 568                 log_err("Cannot allocate results buffer: %s\n",
 569                     strerror(errno));
 570                 return;
 571         }
 572 
 573         node->seqnum = seqnum;
 574         node->buffer = buf;
 575 
 576         (void) pthread_mutex_lock(&buffer_lock);
 577 
 578         node->next = buffer_list;
 579         buffer_list = node;
 580 
 581         (void) pthread_mutex_unlock(&buffer_lock);
 582 }
 583 
 584 /*
 585  * free_buffer()
 586  *
 587  *      Remove a results buffer from the list containing all buffers.
 588  */
 589 static void
 590 free_buffer(uint64_t seqnum)
 591 {
 592         i_buffer_t      *node, *prev;
 593 
 594         (void) pthread_mutex_lock(&buffer_lock);
 595 
 596         prev = NULL;
 597         node = buffer_list;
 598 
 599         while (node) {
 600                 if (node->seqnum == seqnum) {
 601                         dprintf("Free buffer %lld\n", seqnum);
 602                         if (prev) {
 603                                 prev->next = node->next;
 604                         } else {
 605                                 buffer_list = node->next;
 606                         }
 607                         free(node->buffer);
 608                         free(node);
 609                         break;
 610                 }
 611                 prev = node;
 612                 node = node->next;
 613         }
 614 
 615         (void) pthread_mutex_unlock(&buffer_lock);
 616 }
 617 
 618 /*
 619  * audit_session()
 620  *
 621  *      Initialize an audit session.
 622  */
 623 static int
 624 audit_session(ucred_t *ucred, adt_session_data_t **sessionp)
 625 {
 626         adt_session_data_t      *session;
 627 
 628         if (adt_start_session(&session, NULL, 0) != 0) {
 629                 log_err("Cannot start audit session.\n");
 630                 return (-1);
 631         }
 632 
 633         if (adt_set_from_ucred(session, ucred, ADT_NEW) != 0) {
 634                 log_err("Cannot set audit session from ucred.\n");
 635                 (void) adt_end_session(session);
 636                 return (-1);
 637         }
 638 
 639         *sessionp = session;
 640         return (0);
 641 }
 642 
 643 /*
 644  * audit_changestate()
 645  *
 646  *      Audit a 'changestate' door command.
 647  */
 648 static void
 649 audit_changestate(ucred_t *ucred, char *auth, char *path, char *connection,
 650     int new_state, int old_state, int result)
 651 {
 652         adt_session_data_t      *session;
 653         adt_event_data_t        *event;
 654         int                     pass_fail, fail_reason;
 655 
 656         if (audit_session(ucred, &session) != 0)
 657                 return;
 658 
 659         if ((event = adt_alloc_event(session, ADT_hotplug_state)) == NULL) {
 660                 (void) adt_end_session(session);
 661                 return;
 662         }
 663 
 664         if (result == 0) {
 665                 pass_fail = ADT_SUCCESS;
 666                 fail_reason = ADT_SUCCESS;
 667         } else {
 668                 pass_fail = ADT_FAILURE;
 669                 fail_reason = result;
 670         }
 671 
 672         event->adt_hotplug_state.auth_used = auth;
 673         event->adt_hotplug_state.device_path = path;
 674         event->adt_hotplug_state.connection = connection;
 675         event->adt_hotplug_state.new_state = state_str(new_state);
 676         event->adt_hotplug_state.old_state = state_str(old_state);
 677 
 678         /* Put the event */
 679         if (adt_put_event(event, pass_fail, fail_reason) != 0)
 680                 log_err("Cannot put audit event.\n");
 681 
 682         adt_free_event(event);
 683         (void) adt_end_session(session);
 684 }
 685 
 686 /*
 687  * audit_setprivate()
 688  *
 689  *      Audit a 'set private' door command.
 690  */
 691 static void
 692 audit_setprivate(ucred_t *ucred, char *auth, char *path, char *connection,
 693     char *options, int result)
 694 {
 695         adt_session_data_t      *session;
 696         adt_event_data_t        *event;
 697         int                     pass_fail, fail_reason;
 698 
 699         if (audit_session(ucred, &session) != 0)
 700                 return;
 701 
 702         if ((event = adt_alloc_event(session, ADT_hotplug_set)) == NULL) {
 703                 (void) adt_end_session(session);
 704                 return;
 705         }
 706 
 707         if (result == 0) {
 708                 pass_fail = ADT_SUCCESS;
 709                 fail_reason = ADT_SUCCESS;
 710         } else {
 711                 pass_fail = ADT_FAILURE;
 712                 fail_reason = result;
 713         }
 714 
 715         event->adt_hotplug_set.auth_used = auth;
 716         event->adt_hotplug_set.device_path = path;
 717         event->adt_hotplug_set.connection = connection;
 718         event->adt_hotplug_set.options = options;
 719 
 720         /* Put the event */
 721         if (adt_put_event(event, pass_fail, fail_reason) != 0)
 722                 log_err("Cannot put audit event.\n");
 723 
 724         adt_free_event(event);
 725         (void) adt_end_session(session);
 726 }
 727 
 728 /*
 729  * state_str()
 730  *
 731  *      Convert a state from integer to string.
 732  */
 733 static char *
 734 state_str(int state)
 735 {
 736         switch (state) {
 737         case DDI_HP_CN_STATE_EMPTY:
 738                 return ("EMPTY");
 739         case DDI_HP_CN_STATE_PRESENT:
 740                 return ("PRESENT");
 741         case DDI_HP_CN_STATE_POWERED:
 742                 return ("POWERED");
 743         case DDI_HP_CN_STATE_ENABLED:
 744                 return ("ENABLED");
 745         case DDI_HP_CN_STATE_PORT_EMPTY:
 746                 return ("PORT-EMPTY");
 747         case DDI_HP_CN_STATE_PORT_PRESENT:
 748                 return ("PORT-PRESENT");
 749         case DDI_HP_CN_STATE_OFFLINE:
 750                 return ("OFFLINE");
 751         case DDI_HP_CN_STATE_ATTACHED:
 752                 return ("ATTACHED");
 753         case DDI_HP_CN_STATE_MAINTENANCE:
 754                 return ("MAINTENANCE");
 755         case DDI_HP_CN_STATE_ONLINE:
 756                 return ("ONLINE");
 757         default:
 758                 return ("UNKNOWN");
 759         }
 760 }