xref: /illumos-gate/usr/src/cmd/hotplugd/hotplugd_door.c (revision 4f364e7c95ee7fd9d5bbeddc1940e92405bb0e72)
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 }
761