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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <locale.h>
30 #include <libintl.h>
31 #include <alloca.h>
32 #include <getopt.h>
33 #include <libhotplug.h>
34 #include <sys/types.h>
35 #include <sys/sunddi.h>
36 #include <sys/ddi_hp.h>
37
38 #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */
39 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
40 #endif
41
42 /*
43 * Function prototypes.
44 */
45 static int cmd_list(int, char **, const char *);
46 static int cmd_online(int, char **, const char *);
47 static int cmd_offline(int, char **, const char *);
48 static int cmd_enable(int, char **, const char *);
49 static int cmd_disable(int, char **, const char *);
50 static int cmd_poweron(int, char **, const char *);
51 static int cmd_poweroff(int, char **, const char *);
52 static int cmd_getpriv(int, char **, const char *);
53 static int cmd_setpriv(int, char **, const char *);
54 static int cmd_changestate(int, char **, const char *);
55 static void parse_common(int, char **, const char *);
56 static void parse_flags(int, char **, int *, const char *);
57 static void parse_target(int, char **, char **, char **, const char *);
58 static void parse_options(int, char **, char **, const char *);
59 static void bad_option(int, int, const char *);
60 static void usage(const char *);
61 static int list_cb(hp_node_t, void *);
62 static int list_long_cb(hp_node_t, void *);
63 static int error_cb(hp_node_t, void *);
64 static void print_options(const char *);
65 static void print_error(int);
66 static int state_atoi(char *);
67 static char *state_itoa(int);
68 static short valid_target(int);
69
70 /*
71 * Define a conversion table for hotplug states.
72 */
73 typedef struct {
74 int state;
75 char *state_str;
76 short valid_target;
77 } hpstate_t;
78
79 static hpstate_t hpstates[] = {
80 { DDI_HP_CN_STATE_EMPTY, "EMPTY", 0 },
81 { DDI_HP_CN_STATE_PRESENT, "PRESENT", 1 },
82 { DDI_HP_CN_STATE_POWERED, "POWERED", 1 },
83 { DDI_HP_CN_STATE_ENABLED, "ENABLED", 1 },
84 { DDI_HP_CN_STATE_PORT_EMPTY, "PORT-EMPTY", 0 },
85 { DDI_HP_CN_STATE_PORT_PRESENT, "PORT-PRESENT", 1 },
86 { DDI_HP_CN_STATE_OFFLINE, "OFFLINE", 1 },
87 { DDI_HP_CN_STATE_ATTACHED, "ATTACHED", 0 },
88 { DDI_HP_CN_STATE_MAINTENANCE, "MAINTENANCE", 0 },
89 { DDI_HP_CN_STATE_ONLINE, "ONLINE", 1 },
90 { 0, 0, 0 }
91 };
92
93 /*
94 * Define tables of supported subcommands.
95 */
96 typedef struct {
97 char *usage_str;
98 char *cmd_str;
99 int (*func)(int argc, char *argv[], const char *usage_str);
100 } subcmd_t;
101
102 static subcmd_t subcmds[] = {
103 { "list [-l] [-v] [<path> [<connection>]]", "list", cmd_list },
104 { "online <path> <port>", "online", cmd_online },
105 { "offline [-f] [-q] <path> <port>", "offline", cmd_offline },
106 { "enable <path> <connector>", "enable", cmd_enable },
107 { "disable [-f] [-q] <path> <connector>", "disable", cmd_disable },
108 { "poweron <path> <connector>", "poweron", cmd_poweron },
109 { "poweroff [-f] [-q] <path> <connector>", "poweroff", cmd_poweroff },
110 { "get -o <options> <path> <connector>", "get", cmd_getpriv },
111 { "set -o <options> <path> <connector>", "set", cmd_setpriv }
112 };
113
114 static subcmd_t hidden_subcmds[] = {
115 { "changestate [-f] [-q] -s <state> <path> <connection>",
116 "changestate", cmd_changestate }
117 };
118
119 /*
120 * Define tables of command line options.
121 */
122 static const struct option common_opts[] = {
123 { "help", no_argument, 0, '?' },
124 { "version", no_argument, 0, 'V' },
125 { 0, 0, 0, 0 }
126 };
127
128 static const struct option list_opts[] = {
129 { "list-path", no_argument, 0, 'l' },
130 { "verbose", no_argument, 0, 'v' },
131 { 0, 0, 0, 0 }
132 };
133
134 static const struct option flag_opts[] = {
135 { "force", no_argument, 0, 'f' },
136 { "query", no_argument, 0, 'q' },
137 { 0, 0, 0, 0 }
138 };
139
140 static const struct option private_opts[] = {
141 { "options", required_argument, 0, 'o' },
142 { 0, 0, 0, 0 }
143 };
144
145 static const struct option changestate_opts[] = {
146 { "force", no_argument, 0, 'f' },
147 { "query", no_argument, 0, 'q' },
148 { "state", required_argument, 0, 's' },
149 { 0, 0, 0, 0 }
150 };
151
152 /*
153 * Define exit codes.
154 */
155 #define EXIT_OK 0
156 #define EXIT_EINVAL 1 /* invalid arguments */
157 #define EXIT_ENOENT 2 /* path or connection doesn't exist */
158 #define EXIT_FAILED 3 /* operation failed */
159 #define EXIT_UNAVAIL 4 /* service not available */
160
161 /*
162 * Global variables.
163 */
164 static char *prog;
165 static char version[] = "1.0";
166 extern int errno;
167
168 /*
169 * main()
170 *
171 * The main routine determines which subcommand is used,
172 * and dispatches control to the corresponding function.
173 */
174 int
main(int argc,char * argv[])175 main(int argc, char *argv[])
176 {
177 int i, rv;
178
179 (void) setlocale(LC_ALL, "");
180 (void) textdomain(TEXT_DOMAIN);
181
182 if ((prog = strrchr(argv[0], '/')) == NULL)
183 prog = argv[0];
184 else
185 prog++;
186
187 if (argc < 2) {
188 usage(NULL);
189 return (EXIT_EINVAL);
190 }
191
192 parse_common(argc, argv, NULL);
193
194 /* Check the list of defined subcommands. */
195 for (i = 0; i < (sizeof (subcmds) / sizeof (subcmd_t)); i++) {
196 if (strcmp(argv[1], subcmds[i].cmd_str) == 0) {
197 rv = subcmds[i].func(argc - 1, &argv[1],
198 subcmds[i].usage_str);
199 goto finished;
200 }
201 }
202
203 /* Check the list of hidden subcommands. */
204 for (i = 0; i < (sizeof (hidden_subcmds) / sizeof (subcmd_t)); i++) {
205 if (strcmp(argv[1], hidden_subcmds[i].cmd_str) == 0) {
206 rv = hidden_subcmds[i].func(argc - 1, &argv[1],
207 hidden_subcmds[i].usage_str);
208 goto finished;
209 }
210 }
211
212 /* No matching subcommand found. */
213 (void) fprintf(stderr, gettext("ERROR: %s: unknown subcommand '%s'\n"),
214 prog, argv[1]);
215 usage(NULL);
216 exit(EXIT_EINVAL);
217
218 finished:
219 /* Determine exit code */
220 switch (rv) {
221 case 0:
222 break;
223 case EINVAL:
224 return (EXIT_EINVAL);
225 case ENXIO:
226 case ENOENT:
227 return (EXIT_ENOENT);
228 case EBADF:
229 return (EXIT_UNAVAIL);
230 default:
231 return (EXIT_FAILED);
232 }
233
234 return (EXIT_OK);
235 }
236
237 /*
238 * cmd_list()
239 *
240 * Subcommand to list hotplug information.
241 */
242 static int
cmd_list(int argc,char * argv[],const char * usage_str)243 cmd_list(int argc, char *argv[], const char *usage_str)
244 {
245 hp_node_t root;
246 char *path = NULL;
247 char *connection = NULL;
248 boolean_t long_flag = B_FALSE;
249 int flags = 0;
250 int opt;
251
252 /* Parse command line options */
253 parse_common(argc, argv, usage_str);
254 while ((opt = getopt_clip(argc, argv, "lv", list_opts, NULL)) != -1) {
255 switch (opt) {
256 case 'l':
257 long_flag = B_TRUE;
258 break;
259 case 'v':
260 flags |= HPINFOUSAGE;
261 break;
262 default:
263 bad_option(opt, optopt, usage_str);
264 break;
265 }
266 }
267 parse_target(argc, argv, &path, &connection, usage_str);
268
269 /* Default path is "/" */
270 if (path == NULL)
271 path = "/";
272
273 /* Get hotplug information snapshot */
274 if ((root = hp_init(path, connection, flags)) == NULL) {
275 print_error(errno);
276 return (errno);
277 }
278
279 /* Display hotplug information */
280 (void) hp_traverse(root, NULL, long_flag ? list_long_cb : list_cb);
281
282 /* Discard hotplug information snapshot */
283 hp_fini(root);
284
285 return (0);
286 }
287
288 /*
289 * cmd_online()
290 *
291 * Subcommand to online a hotplug port.
292 */
293 static int
cmd_online(int argc,char * argv[],const char * usage_str)294 cmd_online(int argc, char *argv[], const char *usage_str)
295 {
296 hp_node_t root;
297 hp_node_t results = NULL;
298 char *path = NULL;
299 char *connection = NULL;
300 int rv;
301
302 /* Parse command line options */
303 parse_common(argc, argv, usage_str);
304 parse_target(argc, argv, &path, &connection, usage_str);
305
306 /* Path and connection are required */
307 if ((path == NULL) || (connection == NULL)) {
308 (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
309 usage(usage_str);
310 return (EINVAL);
311 }
312
313 /* Get hotplug information snapshot */
314 if ((root = hp_init(path, connection, 0)) == NULL) {
315 print_error(errno);
316 return (errno);
317 }
318
319 /* Verify target is a port */
320 if (hp_type(root) != HP_NODE_PORT) {
321 (void) fprintf(stderr,
322 gettext("ERROR: invalid target (must be a port).\n"));
323 hp_fini(root);
324 return (EINVAL);
325 }
326
327 /* Do state change */
328 rv = hp_set_state(root, 0, DDI_HP_CN_STATE_ONLINE, &results);
329
330 /* Display results */
331 if (rv == EIO) {
332 (void) fprintf(stderr, gettext("ERROR: failed to attach device "
333 "drivers or other internal errors.\n"));
334 } else if (rv != 0) {
335 print_error(rv);
336 }
337 if (results != NULL) {
338 (void) hp_traverse(results, NULL, error_cb);
339 hp_fini(results);
340 }
341
342 /* Discard hotplug information snapshot */
343 hp_fini(root);
344
345 return (rv);
346 }
347
348 /*
349 * cmd_offline()
350 *
351 * Subcommand to offline a hotplug port.
352 */
353 static int
cmd_offline(int argc,char * argv[],const char * usage_str)354 cmd_offline(int argc, char *argv[], const char *usage_str)
355 {
356 hp_node_t root;
357 hp_node_t results = NULL;
358 char *path = NULL;
359 char *connection = NULL;
360 int flags = 0;
361 int rv;
362
363 /* Parse command line options */
364 parse_common(argc, argv, usage_str);
365 parse_flags(argc, argv, &flags, usage_str);
366 parse_target(argc, argv, &path, &connection, usage_str);
367
368 /* Path and connection are required */
369 if ((path == NULL) || (connection == NULL)) {
370 (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
371 usage(usage_str);
372 return (EINVAL);
373 }
374
375 /* Get hotplug information snapshot */
376 if ((root = hp_init(path, connection, 0)) == NULL) {
377 print_error(errno);
378 return (errno);
379 }
380
381 /* Verify target is a port */
382 if (hp_type(root) != HP_NODE_PORT) {
383 (void) fprintf(stderr,
384 gettext("ERROR: invalid target (must be a port).\n"));
385 hp_fini(root);
386 return (EINVAL);
387 }
388
389 /* Do state change */
390 rv = hp_set_state(root, flags, DDI_HP_CN_STATE_OFFLINE, &results);
391
392 /* Display results */
393 print_error(rv);
394 if (results != NULL) {
395 (void) hp_traverse(results, NULL, error_cb);
396 hp_fini(results);
397 }
398
399 /* Discard hotplug information snapshot */
400 hp_fini(root);
401
402 return (rv);
403 }
404
405 /*
406 * cmd_enable()
407 *
408 * Subcommand to enable a hotplug connector.
409 */
410 static int
cmd_enable(int argc,char * argv[],const char * usage_str)411 cmd_enable(int argc, char *argv[], const char *usage_str)
412 {
413 hp_node_t root;
414 hp_node_t results = NULL;
415 char *path = NULL;
416 char *connection = NULL;
417 int rv;
418
419 /* Parse command line options */
420 parse_common(argc, argv, usage_str);
421 parse_target(argc, argv, &path, &connection, usage_str);
422
423 /* Path and connection are required */
424 if ((path == NULL) || (connection == NULL)) {
425 (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
426 usage(usage_str);
427 return (EINVAL);
428 }
429
430 /* Get hotplug information snapshot */
431 if ((root = hp_init(path, connection, 0)) == NULL) {
432 print_error(errno);
433 return (errno);
434 }
435
436 /* Verify target is a connector */
437 if (hp_type(root) != HP_NODE_CONNECTOR) {
438 (void) fprintf(stderr,
439 gettext("ERROR: invalid target (must be a connector).\n"));
440 hp_fini(root);
441 return (EINVAL);
442 }
443
444 /* Do state change */
445 rv = hp_set_state(root, 0, DDI_HP_CN_STATE_ENABLED, &results);
446
447 /* Display results */
448 print_error(rv);
449 if (results != NULL) {
450 (void) hp_traverse(results, NULL, error_cb);
451 hp_fini(results);
452 }
453
454 /* Discard hotplug information snapshot */
455 hp_fini(root);
456
457 return (rv);
458 }
459
460 /*
461 * cmd_disable()
462 *
463 * Subcommand to disable a hotplug connector.
464 */
465 static int
cmd_disable(int argc,char * argv[],const char * usage_str)466 cmd_disable(int argc, char *argv[], const char *usage_str)
467 {
468 hp_node_t root;
469 hp_node_t results = NULL;
470 char *path = NULL;
471 char *connection = NULL;
472 int flags = 0;
473 int rv;
474
475 /* Parse command line options */
476 parse_common(argc, argv, usage_str);
477 parse_flags(argc, argv, &flags, usage_str);
478 parse_target(argc, argv, &path, &connection, usage_str);
479
480 /* Path and connection are required */
481 if ((path == NULL) || (connection == NULL)) {
482 (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
483 usage(usage_str);
484 return (EINVAL);
485 }
486
487 /* Get hotplug information snapshot */
488 if ((root = hp_init(path, connection, 0)) == NULL) {
489 print_error(errno);
490 return (errno);
491 }
492
493 /* Verify target is a connector */
494 if (hp_type(root) != HP_NODE_CONNECTOR) {
495 (void) fprintf(stderr,
496 gettext("ERROR: invalid target (must be a connector).\n"));
497 hp_fini(root);
498 return (EINVAL);
499 }
500
501 /*
502 * Do nothing unless the connector is in the ENABLED state.
503 * Otherwise this subcommand becomes an alias for 'poweron.'
504 */
505 if (hp_state(root) != DDI_HP_CN_STATE_ENABLED) {
506 hp_fini(root);
507 return (0);
508 }
509
510 /* Do state change */
511 rv = hp_set_state(root, flags, DDI_HP_CN_STATE_POWERED, &results);
512
513 /* Display results */
514 print_error(rv);
515 if (results != NULL) {
516 (void) hp_traverse(results, NULL, error_cb);
517 hp_fini(results);
518 }
519
520 /* Discard hotplug information snapshot */
521 hp_fini(root);
522
523 return (rv);
524 }
525
526 /*
527 * cmd_poweron()
528 *
529 * Subcommand to power on a hotplug connector.
530 */
531 static int
cmd_poweron(int argc,char * argv[],const char * usage_str)532 cmd_poweron(int argc, char *argv[], const char *usage_str)
533 {
534 hp_node_t root;
535 hp_node_t results = NULL;
536 char *path = NULL;
537 char *connection = NULL;
538 int rv;
539
540 /* Parse command line options */
541 parse_common(argc, argv, usage_str);
542 parse_target(argc, argv, &path, &connection, usage_str);
543
544 /* Path and connection are required */
545 if ((path == NULL) || (connection == NULL)) {
546 (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
547 usage(usage_str);
548 return (EINVAL);
549 }
550
551 /* Get hotplug information snapshot */
552 if ((root = hp_init(path, connection, 0)) == NULL) {
553 print_error(errno);
554 return (errno);
555 }
556
557 /* Verify target is a connector */
558 if (hp_type(root) != HP_NODE_CONNECTOR) {
559 (void) fprintf(stderr,
560 gettext("ERROR: invalid target (must be a connector).\n"));
561 hp_fini(root);
562 return (EINVAL);
563 }
564
565 /*
566 * Do nothing if the connector is already powered.
567 * Otherwise this subcommand becomes an alias for 'disable.'
568 */
569 if (hp_state(root) >= DDI_HP_CN_STATE_POWERED) {
570 hp_fini(root);
571 return (0);
572 }
573
574 /* Do state change */
575 rv = hp_set_state(root, 0, DDI_HP_CN_STATE_POWERED, &results);
576
577 /* Display results */
578 print_error(rv);
579 if (results != NULL) {
580 (void) hp_traverse(results, NULL, error_cb);
581 hp_fini(results);
582 }
583
584 /* Discard hotplug information snapshot */
585 hp_fini(root);
586
587 return (rv);
588 }
589
590 /*
591 * cmd_poweroff()
592 *
593 * Subcommand to power off a hotplug connector.
594 */
595 static int
cmd_poweroff(int argc,char * argv[],const char * usage_str)596 cmd_poweroff(int argc, char *argv[], const char *usage_str)
597 {
598 hp_node_t root;
599 hp_node_t results = NULL;
600 char *path = NULL;
601 char *connection = NULL;
602 int flags = 0;
603 int rv;
604
605 /* Parse command line options */
606 parse_common(argc, argv, usage_str);
607 parse_flags(argc, argv, &flags, usage_str);
608 parse_target(argc, argv, &path, &connection, usage_str);
609
610 /* Path and connection are required */
611 if ((path == NULL) || (connection == NULL)) {
612 (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
613 usage(usage_str);
614 return (EINVAL);
615 }
616
617 /* Get hotplug information snapshot */
618 if ((root = hp_init(path, connection, 0)) == NULL) {
619 print_error(errno);
620 return (errno);
621 }
622
623 /* Verify target is a connector */
624 if (hp_type(root) != HP_NODE_CONNECTOR) {
625 (void) fprintf(stderr,
626 gettext("ERROR: invalid target (must be a connector).\n"));
627 hp_fini(root);
628 return (EINVAL);
629 }
630
631 /* Do state change */
632 rv = hp_set_state(root, flags, DDI_HP_CN_STATE_PRESENT, &results);
633
634 /* Display results */
635 print_error(rv);
636 if (results != NULL) {
637 (void) hp_traverse(results, NULL, error_cb);
638 hp_fini(results);
639 }
640
641 /* Discard hotplug information snapshot */
642 hp_fini(root);
643
644 return (rv);
645 }
646
647 /*
648 * cmd_getpriv()
649 *
650 * Subcommand to get and display bus private options.
651 */
652 static int
cmd_getpriv(int argc,char * argv[],const char * usage_str)653 cmd_getpriv(int argc, char *argv[], const char *usage_str)
654 {
655 hp_node_t root;
656 char *path = NULL;
657 char *connection = NULL;
658 char *options = NULL;
659 char *results = NULL;
660 int rv;
661
662 /* Parse command line options */
663 parse_common(argc, argv, usage_str);
664 parse_options(argc, argv, &options, usage_str);
665 parse_target(argc, argv, &path, &connection, usage_str);
666
667 /* Options, path, and connection are all required */
668 if ((options == NULL) || (path == NULL) || (connection == NULL)) {
669 (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
670 usage(usage_str);
671 return (EINVAL);
672 }
673
674 /* Get hotplug information snapshot */
675 if ((root = hp_init(path, connection, 0)) == NULL) {
676 print_error(errno);
677 return (errno);
678 }
679
680 /* Verify target is a connector */
681 if (hp_type(root) != HP_NODE_CONNECTOR) {
682 (void) fprintf(stderr,
683 gettext("ERROR: invalid target (must be a connector).\n"));
684 hp_fini(root);
685 return (EINVAL);
686 }
687
688 /* Do the operation */
689 rv = hp_get_private(root, options, &results);
690
691 /* Display results */
692 if (rv == ENOTSUP) {
693 (void) fprintf(stderr,
694 gettext("ERROR: unsupported property name or value.\n"));
695 (void) fprintf(stderr,
696 gettext("(Properties may depend upon connector state.)\n"));
697 } else if (rv != 0) {
698 print_error(rv);
699 }
700 if (results != NULL) {
701 print_options(results);
702 free(results);
703 }
704
705 /* Discard hotplug information snapshot */
706 hp_fini(root);
707
708 return (rv);
709 }
710
711 /*
712 * cmd_setpriv()
713 *
714 * Subcommand to set bus private options.
715 */
716 static int
cmd_setpriv(int argc,char * argv[],const char * usage_str)717 cmd_setpriv(int argc, char *argv[], const char *usage_str)
718 {
719 hp_node_t root;
720 char *path = NULL;
721 char *connection = NULL;
722 char *options = NULL;
723 char *results = NULL;
724 int rv;
725
726 /* Parse command line options */
727 parse_common(argc, argv, usage_str);
728 parse_options(argc, argv, &options, usage_str);
729 parse_target(argc, argv, &path, &connection, usage_str);
730
731 /* Options, path, and connection are all required */
732 if ((options == NULL) || (path == NULL) || (connection == NULL)) {
733 (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
734 usage(usage_str);
735 return (EINVAL);
736 }
737
738 /* Get hotplug information snapshot */
739 if ((root = hp_init(path, connection, 0)) == NULL) {
740 print_error(errno);
741 return (errno);
742 }
743
744 /* Verify target is a connector */
745 if (hp_type(root) != HP_NODE_CONNECTOR) {
746 (void) fprintf(stderr,
747 gettext("ERROR: invalid target (must be a connector).\n"));
748 hp_fini(root);
749 return (EINVAL);
750 }
751
752 /* Do the operation */
753 rv = hp_set_private(root, options, &results);
754
755 /* Display results */
756 if (rv == ENOTSUP) {
757 (void) fprintf(stderr,
758 gettext("ERROR: unsupported property name or value.\n"));
759 (void) fprintf(stderr,
760 gettext("(Properties may depend upon connector state.)\n"));
761 } else if (rv != 0) {
762 print_error(rv);
763 }
764 if (results != NULL) {
765 print_options(results);
766 free(results);
767 }
768
769 /* Discard hotplug information snapshot */
770 hp_fini(root);
771
772 return (rv);
773 }
774
775 /*
776 * cmd_changestate()
777 *
778 * Subcommand to initiate a state change operation. This is
779 * a hidden subcommand to directly set a connector or port to
780 * a specific target state.
781 */
782 static int
cmd_changestate(int argc,char * argv[],const char * usage_str)783 cmd_changestate(int argc, char *argv[], const char *usage_str)
784 {
785 hp_node_t root;
786 hp_node_t results = NULL;
787 char *path = NULL;
788 char *connection = NULL;
789 int state = -1;
790 int flags = 0;
791 int opt, rv;
792
793 /* Parse command line options */
794 parse_common(argc, argv, usage_str);
795 while ((opt = getopt_clip(argc, argv, "fqs:", changestate_opts,
796 NULL)) != -1) {
797 switch (opt) {
798 case 'f':
799 flags |= HPFORCE;
800 break;
801 case 'q':
802 flags |= HPQUERY;
803 break;
804 case 's':
805 if ((state = state_atoi(optarg)) == -1) {
806 (void) printf("ERROR: invalid target state\n");
807 return (EINVAL);
808 }
809 break;
810 default:
811 bad_option(opt, optopt, usage_str);
812 break;
813 }
814 }
815 parse_target(argc, argv, &path, &connection, usage_str);
816
817 /* State, path, and connection are all required */
818 if ((state == -1) || (path == NULL) || (connection == NULL)) {
819 (void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
820 usage(usage_str);
821 return (EINVAL);
822 }
823
824 /* Check that target state is valid */
825 if (valid_target(state) == 0) {
826 (void) fprintf(stderr,
827 gettext("ERROR: invalid target state\n"));
828 return (EINVAL);
829 }
830
831 /* Get hotplug information snapshot */
832 if ((root = hp_init(path, connection, 0)) == NULL) {
833 print_error(errno);
834 return (errno);
835 }
836
837 /* Initiate state change operation on root of snapshot */
838 rv = hp_set_state(root, flags, state, &results);
839
840 /* Display results */
841 print_error(rv);
842 if (results) {
843 (void) hp_traverse(results, NULL, error_cb);
844 hp_fini(results);
845 }
846
847 /* Discard hotplug information snapshot */
848 hp_fini(root);
849
850 return (rv);
851 }
852
853 /*
854 * parse_common()
855 *
856 * Parse command line options that are common to the
857 * entire program, and to each of its subcommands.
858 */
859 static void
parse_common(int argc,char * argv[],const char * usage_str)860 parse_common(int argc, char *argv[], const char *usage_str)
861 {
862 int opt;
863 extern int opterr;
864 extern int optind;
865
866 /* Turn off error reporting */
867 opterr = 0;
868
869 while ((opt = getopt_clip(argc, argv, "?V", common_opts, NULL)) != -1) {
870 switch (opt) {
871 case '?':
872 if (optopt == '?') {
873 usage(usage_str);
874 exit(0);
875 }
876 break;
877 case 'V':
878 (void) printf(gettext("%s: Version %s\n"),
879 prog, version);
880 exit(0);
881 default:
882 break;
883 }
884 }
885
886 /* Reset option index */
887 optind = 1;
888 }
889
890 /*
891 * parse_flags()
892 *
893 * Parse command line flags common to all downward state
894 * change operations (offline, disable, poweoff).
895 */
896 static void
parse_flags(int argc,char * argv[],int * flagsp,const char * usage_str)897 parse_flags(int argc, char *argv[], int *flagsp, const char *usage_str)
898 {
899 int opt;
900 int flags = 0;
901
902 while ((opt = getopt_clip(argc, argv, "fq", flag_opts, NULL)) != -1) {
903 switch (opt) {
904 case 'f':
905 flags |= HPFORCE;
906 break;
907 case 'q':
908 flags |= HPQUERY;
909 break;
910 default:
911 bad_option(opt, optopt, usage_str);
912 break;
913 }
914 }
915
916 *flagsp = flags;
917 }
918
919 /*
920 * parse_options()
921 *
922 * Parse command line options common to the bus private set and
923 * get subcommands.
924 */
925 static void
parse_options(int argc,char * argv[],char ** optionsp,const char * usage_str)926 parse_options(int argc, char *argv[], char **optionsp, const char *usage_str)
927 {
928 int opt;
929
930 while ((opt = getopt_clip(argc, argv, "o:", private_opts,
931 NULL)) != -1) {
932 switch (opt) {
933 case 'o':
934 *optionsp = optarg;
935 break;
936 default:
937 bad_option(opt, optopt, usage_str);
938 break;
939 }
940 }
941 }
942
943 /*
944 * parse_target()
945 *
946 * Parse the target path and connection name from the command line.
947 */
948 static void
parse_target(int argc,char * argv[],char ** pathp,char ** connectionp,const char * usage_str)949 parse_target(int argc, char *argv[], char **pathp, char **connectionp,
950 const char *usage_str)
951 {
952 extern int optind;
953
954 if (optind < argc)
955 *pathp = argv[optind++];
956
957 if (optind < argc)
958 *connectionp = argv[optind++];
959
960 if (optind < argc) {
961 (void) fprintf(stderr, gettext("ERROR: too many arguments.\n"));
962 usage(usage_str);
963 exit(EINVAL);
964 }
965 }
966
967 /*
968 * bad_option()
969 *
970 * Routine to handle bad command line options.
971 */
972 static void
bad_option(int opt,int optopt,const char * usage_str)973 bad_option(int opt, int optopt, const char *usage_str)
974 {
975 switch (opt) {
976 case ':':
977 (void) fprintf(stderr,
978 gettext("ERROR: option '%c' requires an argument.\n"),
979 optopt);
980 break;
981 default:
982 if (optopt == '?') {
983 usage(usage_str);
984 exit(EXIT_OK);
985 }
986 (void) fprintf(stderr,
987 gettext("ERROR: unrecognized option '%c'.\n"), optopt);
988 break;
989 }
990
991 usage(usage_str);
992
993 exit(EXIT_EINVAL);
994 }
995
996 /*
997 * usage()
998 *
999 * Display general usage of the command. Including
1000 * the usage synopsis of each defined subcommand.
1001 */
1002 static void
usage(const char * usage_str)1003 usage(const char *usage_str)
1004 {
1005 int i;
1006
1007 if (usage_str != NULL) {
1008 (void) fprintf(stderr, gettext("Usage: %s %s\n\n"),
1009 prog, usage_str);
1010 return;
1011 }
1012
1013 (void) fprintf(stderr, gettext("Usage: %s <subcommand> [<args>]\n\n"),
1014 prog);
1015
1016 (void) fprintf(stderr, gettext("Subcommands:\n\n"));
1017
1018 for (i = 0; i < (sizeof (subcmds) / sizeof (subcmd_t)); i++)
1019 (void) fprintf(stderr, " %s\n\n", subcmds[i].usage_str);
1020 }
1021
1022 /*
1023 * list_cb()
1024 *
1025 * Callback function for hp_traverse(), to display nodes
1026 * of a hotplug information snapshot. (Short version.)
1027 */
1028 /*ARGSUSED*/
1029 static int
list_cb(hp_node_t node,void * arg)1030 list_cb(hp_node_t node, void *arg)
1031 {
1032 hp_node_t parent;
1033
1034 /* Indent */
1035 for (parent = hp_parent(node); parent; parent = hp_parent(parent))
1036 if (hp_type(parent) == HP_NODE_DEVICE)
1037 (void) printf(" ");
1038
1039 switch (hp_type(node)) {
1040 case HP_NODE_DEVICE:
1041 (void) printf("%s\n", hp_name(node));
1042 break;
1043
1044 case HP_NODE_CONNECTOR:
1045 (void) printf("[%s]", hp_name(node));
1046 (void) printf(" (%s)", state_itoa(hp_state(node)));
1047 (void) printf("\n");
1048 break;
1049
1050 case HP_NODE_PORT:
1051 (void) printf("<%s>", hp_name(node));
1052 (void) printf(" (%s)", state_itoa(hp_state(node)));
1053 (void) printf("\n");
1054 break;
1055
1056 case HP_NODE_USAGE:
1057 (void) printf("{ %s }\n", hp_usage(node));
1058 break;
1059 }
1060
1061 return (HP_WALK_CONTINUE);
1062 }
1063
1064 /*
1065 * list_long_cb()
1066 *
1067 * Callback function for hp_traverse(), to display nodes
1068 * of a hotplug information snapshot. (Long version.)
1069 */
1070 /*ARGSUSED*/
1071 static int
list_long_cb(hp_node_t node,void * arg)1072 list_long_cb(hp_node_t node, void *arg)
1073 {
1074 char path[MAXPATHLEN];
1075 char connection[MAXPATHLEN];
1076
1077 if (hp_type(node) != HP_NODE_USAGE) {
1078 if (hp_path(node, path, connection) != 0)
1079 return (HP_WALK_CONTINUE);
1080 (void) printf("%s", path);
1081 }
1082
1083 switch (hp_type(node)) {
1084 case HP_NODE_CONNECTOR:
1085 (void) printf(" [%s]", connection);
1086 (void) printf(" (%s)", state_itoa(hp_state(node)));
1087 break;
1088
1089 case HP_NODE_PORT:
1090 (void) printf(" <%s>", connection);
1091 (void) printf(" (%s)", state_itoa(hp_state(node)));
1092 break;
1093
1094 case HP_NODE_USAGE:
1095 (void) printf(" { %s }", hp_usage(node));
1096 break;
1097 }
1098
1099 (void) printf("\n");
1100
1101 return (HP_WALK_CONTINUE);
1102 }
1103
1104 /*
1105 * error_cb()
1106 *
1107 * Callback function for hp_traverse(), to display
1108 * error results from a state change operation.
1109 */
1110 /*ARGSUSED*/
1111 static int
error_cb(hp_node_t node,void * arg)1112 error_cb(hp_node_t node, void *arg)
1113 {
1114 hp_node_t child;
1115 char *usage_str;
1116 static char path[MAXPATHLEN];
1117 static char connection[MAXPATHLEN];
1118
1119 if (((child = hp_child(node)) != NULL) &&
1120 (hp_type(child) == HP_NODE_USAGE)) {
1121 if (hp_path(node, path, connection) == 0)
1122 (void) printf("%s:\n", path);
1123 return (HP_WALK_CONTINUE);
1124 }
1125
1126 if ((hp_type(node) == HP_NODE_USAGE) &&
1127 ((usage_str = hp_usage(node)) != NULL))
1128 (void) printf(" { %s }\n", usage_str);
1129
1130 return (HP_WALK_CONTINUE);
1131 }
1132
1133 /*
1134 * print_options()
1135 *
1136 * Parse and display bus private options. The options are
1137 * formatted as a string which conforms to the getsubopt(3C)
1138 * format. This routine only splits the string elements as
1139 * separated by commas, and displays each portion on its own
1140 * separate line of output.
1141 */
1142 static void
print_options(const char * options)1143 print_options(const char *options)
1144 {
1145 char *buf, *curr, *next;
1146 size_t len;
1147
1148 /* Do nothing if options string is empty */
1149 if ((len = strlen(options)) == 0)
1150 return;
1151
1152 /* To avoid modifying the input string, make a copy on the stack */
1153 if ((buf = (char *)alloca(len + 1)) == NULL) {
1154 (void) printf("%s\n", options);
1155 return;
1156 }
1157 (void) strlcpy(buf, options, len + 1);
1158
1159 /* Iterate through each comma-separated name/value pair */
1160 curr = buf;
1161 do {
1162 if ((next = strchr(curr, ',')) != NULL) {
1163 *next = '\0';
1164 next++;
1165 }
1166 (void) printf("%s\n", curr);
1167 } while ((curr = next) != NULL);
1168 }
1169
1170 /*
1171 * print_error()
1172 *
1173 * Common routine to print error numbers in an appropriate way.
1174 * Prints nothing if error code is 0.
1175 */
1176 static void
print_error(int error)1177 print_error(int error)
1178 {
1179 switch (error) {
1180 case 0:
1181 /* No error */
1182 return;
1183 case EACCES:
1184 (void) fprintf(stderr,
1185 gettext("ERROR: operation not authorized.\n"));
1186 break;
1187 case EBADF:
1188 (void) fprintf(stderr,
1189 gettext("ERROR: hotplug service is not available.\n"));
1190 break;
1191 case EBUSY:
1192 (void) fprintf(stderr,
1193 gettext("ERROR: devices or resources are busy.\n"));
1194 break;
1195 case EEXIST:
1196 (void) fprintf(stderr,
1197 gettext("ERROR: resource already exists.\n"));
1198 break;
1199 case EFAULT:
1200 (void) fprintf(stderr,
1201 gettext("ERROR: internal failure in hotplug service.\n"));
1202 break;
1203 case EINVAL:
1204 (void) fprintf(stderr,
1205 gettext("ERROR: invalid arguments.\n"));
1206 break;
1207 case ENOENT:
1208 (void) fprintf(stderr,
1209 gettext("ERROR: there are no connections to display.\n"));
1210 (void) fprintf(stderr,
1211 gettext("(See hotplug(8) for more information.)\n"));
1212 break;
1213 case ENXIO:
1214 (void) fprintf(stderr,
1215 gettext("ERROR: no such path or connection.\n"));
1216 break;
1217 case ENOMEM:
1218 (void) fprintf(stderr,
1219 gettext("ERROR: not enough memory.\n"));
1220 break;
1221 case ENOTSUP:
1222 (void) fprintf(stderr,
1223 gettext("ERROR: operation not supported.\n"));
1224 break;
1225 case EIO:
1226 (void) fprintf(stderr,
1227 gettext("ERROR: hardware or driver specific failure.\n"));
1228 break;
1229 default:
1230 (void) fprintf(stderr, gettext("ERROR: operation failed: %s\n"),
1231 strerror(error));
1232 break;
1233 }
1234 }
1235
1236 /*
1237 * state_atoi()
1238 *
1239 * Convert a hotplug state from a string to an integer.
1240 */
1241 static int
state_atoi(char * state)1242 state_atoi(char *state)
1243 {
1244 int i;
1245
1246 for (i = 0; hpstates[i].state_str != NULL; i++)
1247 if (strcasecmp(state, hpstates[i].state_str) == 0)
1248 return (hpstates[i].state);
1249
1250 return (-1);
1251 }
1252
1253 /*
1254 * state_itoa()
1255 *
1256 * Convert a hotplug state from an integer to a string.
1257 */
1258 static char *
state_itoa(int state)1259 state_itoa(int state)
1260 {
1261 static char unknown[] = "UNKNOWN";
1262 int i;
1263
1264 for (i = 0; hpstates[i].state_str != NULL; i++)
1265 if (state == hpstates[i].state)
1266 return (hpstates[i].state_str);
1267
1268 return (unknown);
1269 }
1270
1271 /*
1272 * valid_target()
1273 *
1274 * Check if a state is a valid target for a changestate command.
1275 */
1276 static short
valid_target(int state)1277 valid_target(int state)
1278 {
1279 int i;
1280
1281 for (i = 0; hpstates[i].state_str != NULL; i++)
1282 if (state == hpstates[i].state)
1283 return (hpstates[i].valid_target);
1284
1285 return (0);
1286 }
1287