1*33b82b03Srillig /* $NetBSD: drvctl.c,v 1.23 2024/11/03 10:43:26 rillig Exp $ */ 20c34f6cdSdrochner 30c34f6cdSdrochner /* 40c34f6cdSdrochner * Copyright (c) 2004 50c34f6cdSdrochner * Matthias Drochner. All rights reserved. 60c34f6cdSdrochner * 70c34f6cdSdrochner * Redistribution and use in source and binary forms, with or without 80c34f6cdSdrochner * modification, are permitted provided that the following conditions 90c34f6cdSdrochner * are met: 100c34f6cdSdrochner * 1. Redistributions of source code must retain the above copyright 110c34f6cdSdrochner * notice, this list of conditions, and the following disclaimer. 120c34f6cdSdrochner * 2. Redistributions in binary form must reproduce the above copyright 130c34f6cdSdrochner * notice, this list of conditions and the following disclaimer in the 140c34f6cdSdrochner * documentation and/or other materials provided with the distribution. 150c34f6cdSdrochner * 160c34f6cdSdrochner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 170c34f6cdSdrochner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 180c34f6cdSdrochner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 190c34f6cdSdrochner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 200c34f6cdSdrochner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 210c34f6cdSdrochner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 220c34f6cdSdrochner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 230c34f6cdSdrochner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 240c34f6cdSdrochner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 250c34f6cdSdrochner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 260c34f6cdSdrochner * SUCH DAMAGE. 270c34f6cdSdrochner */ 280c34f6cdSdrochner 293b8212e8Sjmcneill #include <inttypes.h> 300f6df043Sdyoung #include <stdbool.h> 310c34f6cdSdrochner #include <stdio.h> 320c34f6cdSdrochner #include <stdlib.h> 330c34f6cdSdrochner #include <unistd.h> 340c34f6cdSdrochner #include <err.h> 350c34f6cdSdrochner #include <fcntl.h> 360c34f6cdSdrochner #include <string.h> 370c34f6cdSdrochner #include <sys/ioctl.h> 380c34f6cdSdrochner #include <sys/drvctlio.h> 390c34f6cdSdrochner 40ef731d9eSthorpej 41ef731d9eSthorpej #define OPEN_MODE(mode) \ 42ef731d9eSthorpej (((mode) == 'd' || (mode) == 'r') ? O_RDWR \ 43ef731d9eSthorpej : O_RDONLY) 440c34f6cdSdrochner 45baa8e84bSjoerg __dead static void usage(void); 46440a8860Spgoyette static void extract_property(prop_dictionary_t, const char *, bool); 47440a8860Spgoyette static void display_object(prop_object_t, bool); 48f398b121Sjmcneill static void list_children(int, char *, bool, bool, int); 490c34f6cdSdrochner 500c34f6cdSdrochner static void 517b1148bcSxtraeme usage(void) 520c34f6cdSdrochner { 530cc54e49Schristos const char *p = getprogname(); 540c34f6cdSdrochner 55702f9dcaSwiz fprintf(stderr, "Usage: %s -r [-a attribute] busdevice [locator ...]\n" 56ef731d9eSthorpej " %s -d device\n" 57f398b121Sjmcneill " %s [-nt] -l [device]\n" 581aa8a761Spgoyette " %s [-n] -p device [property]\n" 59ae1c071dSdyoung " %s -Q device\n" 60ae1c071dSdyoung " %s -R device\n" 610cc54e49Schristos " %s -S device\n", p, p, p, p, p, p, p); 620cc54e49Schristos exit(EXIT_FAILURE); 630c34f6cdSdrochner } 640c34f6cdSdrochner 650c34f6cdSdrochner int 660c34f6cdSdrochner main(int argc, char **argv) 670c34f6cdSdrochner { 68f398b121Sjmcneill bool nflag = false, tflag = false; 690c34f6cdSdrochner int c, mode; 700c34f6cdSdrochner char *attr = 0; 710c34f6cdSdrochner int fd, res; 72ae1c071dSdyoung struct devpmargs paa = { .devname = "", .flags = 0 }; 730c34f6cdSdrochner struct devdetachargs daa; 740c34f6cdSdrochner struct devrescanargs raa; 750c34f6cdSdrochner int *locs, i; 760cc54e49Schristos prop_dictionary_t command_dict, args_dict, results_dict, data_dict; 77ae1c071dSdyoung char *xml; 78aae463daSthorpej int drvctl_error; 790c34f6cdSdrochner 802cd106eeSlukem mode = 0; 810cc54e49Schristos while ((c = getopt(argc, argv, "QRSa:dlnprt")) != -1) { 820c34f6cdSdrochner switch (c) { 83ae1c071dSdyoung case 'Q': 84ae1c071dSdyoung case 'R': 85ae1c071dSdyoung case 'S': 860c34f6cdSdrochner case 'd': 87ae1c071dSdyoung case 'l': 88ef731d9eSthorpej case 'p': 89ae1c071dSdyoung case 'r': 900c34f6cdSdrochner mode = c; 910c34f6cdSdrochner break; 92fbdc9192Sjakllsch case 'a': 93fbdc9192Sjakllsch attr = optarg; 94fbdc9192Sjakllsch break; 95fbdc9192Sjakllsch case 'n': 96fbdc9192Sjakllsch nflag = true; 97fbdc9192Sjakllsch break; 98f398b121Sjmcneill case 't': 99f398b121Sjmcneill tflag = nflag = true; 100f398b121Sjmcneill break; 1010c34f6cdSdrochner case '?': 1020c34f6cdSdrochner default: 1030c34f6cdSdrochner usage(); 1040c34f6cdSdrochner } 1050c34f6cdSdrochner } 1060c34f6cdSdrochner 1070c34f6cdSdrochner argc -= optind; 1080c34f6cdSdrochner argv += optind; 1090c34f6cdSdrochner 1109d6b98aeSjoerg if ((argc < 1 && mode != 'l') || mode == 0) 1110c34f6cdSdrochner usage(); 1120c34f6cdSdrochner 113ef731d9eSthorpej fd = open(DRVCTLDEV, OPEN_MODE(mode), 0); 1140cc54e49Schristos if (fd == -1) 1150cc54e49Schristos err(EXIT_FAILURE, "open %s", DRVCTLDEV); 1160c34f6cdSdrochner 117ae1c071dSdyoung switch (mode) { 118ae1c071dSdyoung case 'Q': 119ae1c071dSdyoung paa.flags = DEVPM_F_SUBTREE; 120ae1c071dSdyoung /*FALLTHROUGH*/ 121ae1c071dSdyoung case 'R': 122ae1c071dSdyoung strlcpy(paa.devname, argv[0], sizeof(paa.devname)); 123ae1c071dSdyoung 124ae1c071dSdyoung if (ioctl(fd, DRVRESUMEDEV, &paa) == -1) 1250cc54e49Schristos err(EXIT_FAILURE, "DRVRESUMEDEV"); 126ae1c071dSdyoung break; 127ae1c071dSdyoung case 'S': 128ae1c071dSdyoung strlcpy(paa.devname, argv[0], sizeof(paa.devname)); 129ae1c071dSdyoung 130ae1c071dSdyoung if (ioctl(fd, DRVSUSPENDDEV, &paa) == -1) 1310cc54e49Schristos err(EXIT_FAILURE, "DRVSUSPENDDEV"); 132ae1c071dSdyoung break; 133ae1c071dSdyoung case 'd': 1340c34f6cdSdrochner strlcpy(daa.devname, argv[0], sizeof(daa.devname)); 1350c34f6cdSdrochner 136ae1c071dSdyoung if (ioctl(fd, DRVDETACHDEV, &daa) == -1) 1370cc54e49Schristos err(EXIT_FAILURE, "DRVDETACHDEV"); 138ae1c071dSdyoung break; 139ae1c071dSdyoung case 'l': 140f398b121Sjmcneill list_children(fd, argc ? argv[0] : NULL, nflag, tflag, 0); 141ae1c071dSdyoung break; 142ae1c071dSdyoung case 'r': 1430c34f6cdSdrochner memset(&raa, 0, sizeof(raa)); 1440c34f6cdSdrochner strlcpy(raa.busname, argv[0], sizeof(raa.busname)); 1450c34f6cdSdrochner if (attr) 1460c34f6cdSdrochner strlcpy(raa.ifattr, attr, sizeof(raa.ifattr)); 1470c34f6cdSdrochner if (argc > 1) { 1480c34f6cdSdrochner locs = malloc((argc - 1) * sizeof(int)); 1490c34f6cdSdrochner if (!locs) 1500cc54e49Schristos err(EXIT_FAILURE, "malloc int[%d]", argc - 1); 1510c34f6cdSdrochner for (i = 0; i < argc - 1; i++) 1520c34f6cdSdrochner locs[i] = atoi(argv[i + 1]); 1530c34f6cdSdrochner raa.numlocators = argc - 1; 1540c34f6cdSdrochner raa.locators = locs; 1550c34f6cdSdrochner } 1560c34f6cdSdrochner 157ae1c071dSdyoung if (ioctl(fd, DRVRESCANBUS, &raa) == -1) 1580cc54e49Schristos err(EXIT_FAILURE, "DRVRESCANBUS"); 159ae1c071dSdyoung break; 160ae1c071dSdyoung case 'p': 161ef731d9eSthorpej command_dict = prop_dictionary_create(); 162ef731d9eSthorpej args_dict = prop_dictionary_create(); 163ef731d9eSthorpej 164aae463daSthorpej prop_dictionary_set_string_nocopy(command_dict, 165aae463daSthorpej "drvctl-command", "get-properties"); 166aae463daSthorpej prop_dictionary_set_string(args_dict, "device-name", 167aae463daSthorpej argv[0]); 168aae463daSthorpej prop_dictionary_set_and_rel(command_dict, "drvctl-arguments", 169ef731d9eSthorpej args_dict); 170ef731d9eSthorpej 171ef731d9eSthorpej res = prop_dictionary_sendrecv_ioctl(command_dict, fd, 1720cc54e49Schristos DRVCTLCOMMAND, &results_dict); 173ef731d9eSthorpej prop_object_release(command_dict); 174ef731d9eSthorpej if (res) 1750cc54e49Schristos errc(EXIT_FAILURE, res, "DRVCTLCOMMAND"); 176ef731d9eSthorpej 177aae463daSthorpej if (prop_dictionary_get_int(results_dict, "drvctl-error", 178aae463daSthorpej &drvctl_error) && 179aae463daSthorpej drvctl_error != 0) { 180aae463daSthorpej errc(EXIT_FAILURE, drvctl_error, "get-properties"); 181ef731d9eSthorpej } 182ef731d9eSthorpej 183ef731d9eSthorpej data_dict = prop_dictionary_get(results_dict, 184ef731d9eSthorpej "drvctl-result-data"); 185ef731d9eSthorpej if (data_dict == NULL) { 1860cc54e49Schristos errx(EXIT_FAILURE, 1870cc54e49Schristos "get-properties: failed to return result data"); 188ef731d9eSthorpej } 189ef731d9eSthorpej 1903b8212e8Sjmcneill if (argc == 1) { 191ef731d9eSthorpej xml = prop_dictionary_externalize(data_dict); 19291e31ba9Sdyoung if (!nflag) { 19391e31ba9Sdyoung printf("Properties for device `%s':\n", 19491e31ba9Sdyoung argv[0]); 19591e31ba9Sdyoung } 19691e31ba9Sdyoung printf("%s", xml); 197ef731d9eSthorpej free(xml); 1983b8212e8Sjmcneill } else { 1993b8212e8Sjmcneill for (i = 1; i < argc; i++) 200440a8860Spgoyette extract_property(data_dict, argv[i], nflag); 2013b8212e8Sjmcneill } 2023b8212e8Sjmcneill 2033b8212e8Sjmcneill prop_object_release(results_dict); 204ae1c071dSdyoung break; 205ae1c071dSdyoung default: 2060cc54e49Schristos errx(EXIT_FAILURE, "unknown command `%c'", mode); 207ae1c071dSdyoung } 2080c34f6cdSdrochner 2090cc54e49Schristos return EXIT_SUCCESS; 2100c34f6cdSdrochner } 2113b8212e8Sjmcneill 2123b8212e8Sjmcneill static void 213440a8860Spgoyette extract_property(prop_dictionary_t dict, const char *prop, bool nflag) 2143b8212e8Sjmcneill { 215440a8860Spgoyette char *s, *p, *cur, *ep = NULL; 2163b8212e8Sjmcneill prop_object_t obj; 2170a7f816bSmlelstv unsigned long ind; 2183b8212e8Sjmcneill 2190a7f816bSmlelstv obj = dict; 2200a7f816bSmlelstv cur = NULL; 2213b8212e8Sjmcneill s = strdup(prop); 2223b8212e8Sjmcneill p = strtok_r(s, "/", &ep); 2233b8212e8Sjmcneill while (p) { 2243b8212e8Sjmcneill cur = p; 2253b8212e8Sjmcneill p = strtok_r(NULL, "/", &ep); 2260a7f816bSmlelstv 2270a7f816bSmlelstv switch (prop_object_type(obj)) { 2280a7f816bSmlelstv case PROP_TYPE_DICTIONARY: 2290a7f816bSmlelstv obj = prop_dictionary_get(obj, cur); 2300a7f816bSmlelstv if (obj == NULL) 231657333c9Smlelstv p = NULL; 2320a7f816bSmlelstv break; 2330a7f816bSmlelstv case PROP_TYPE_ARRAY: 2340a7f816bSmlelstv ind = strtoul(cur, NULL, 0); 2350a7f816bSmlelstv obj = prop_array_get(obj, ind); 2360a7f816bSmlelstv if (obj == NULL) 237657333c9Smlelstv p = NULL; 2380a7f816bSmlelstv break; 2390a7f816bSmlelstv default: 240657333c9Smlelstv fprintf(stderr, "Select neither dict nor array with" 241657333c9Smlelstv " `%s'\n", cur); 242657333c9Smlelstv obj = NULL; 243657333c9Smlelstv p = NULL; 244657333c9Smlelstv break; 2450a7f816bSmlelstv } 2460a7f816bSmlelstv } 2470a7f816bSmlelstv 2480a7f816bSmlelstv if (obj != NULL && cur != NULL) 249440a8860Spgoyette display_object(obj, nflag); 250440a8860Spgoyette 251440a8860Spgoyette free(s); 252440a8860Spgoyette } 253440a8860Spgoyette 254440a8860Spgoyette static void 255440a8860Spgoyette display_object(prop_object_t obj, bool nflag) 256440a8860Spgoyette { 257440a8860Spgoyette char *xml; 258440a8860Spgoyette prop_object_t next_obj; 259440a8860Spgoyette prop_object_iterator_t iter; 260440a8860Spgoyette 2613b8212e8Sjmcneill if (obj == NULL) 2623b8212e8Sjmcneill exit(EXIT_FAILURE); 2633b8212e8Sjmcneill switch (prop_object_type(obj)) { 2643b8212e8Sjmcneill case PROP_TYPE_BOOL: 265440a8860Spgoyette printf("%s\n", prop_bool_true(obj) ? "true" : "false"); 2663b8212e8Sjmcneill break; 2673b8212e8Sjmcneill case PROP_TYPE_NUMBER: 268aae463daSthorpej printf("%" PRId64 "\n", prop_number_signed_value(obj)); 2693b8212e8Sjmcneill break; 2703b8212e8Sjmcneill case PROP_TYPE_STRING: 271aae463daSthorpej printf("%s\n", prop_string_value(obj)); 2723b8212e8Sjmcneill break; 2733b8212e8Sjmcneill case PROP_TYPE_DICTIONARY: 2743b8212e8Sjmcneill xml = prop_dictionary_externalize(obj); 2753b8212e8Sjmcneill printf("%s", xml); 2763b8212e8Sjmcneill free(xml); 2773b8212e8Sjmcneill break; 278440a8860Spgoyette case PROP_TYPE_ARRAY: 279440a8860Spgoyette iter = prop_array_iterator(obj); 280440a8860Spgoyette if (!nflag) 281440a8860Spgoyette printf("Array:\n"); 282440a8860Spgoyette while ((next_obj = prop_object_iterator_next(iter)) != NULL) 283440a8860Spgoyette display_object(next_obj, nflag); 284440a8860Spgoyette break; 2853b8212e8Sjmcneill default: 2860cc54e49Schristos errx(EXIT_FAILURE, "Unhandled type %d", prop_object_type(obj)); 2873b8212e8Sjmcneill } 2883b8212e8Sjmcneill } 289f398b121Sjmcneill 290f398b121Sjmcneill static void 291f398b121Sjmcneill list_children(int fd, char *dvname, bool nflag, bool tflag, int depth) 292f398b121Sjmcneill { 2930cc54e49Schristos struct devlistargs laa = { 2940cc54e49Schristos .l_devname = "", .l_childname = NULL, .l_children = 0 2950cc54e49Schristos }; 296f398b121Sjmcneill size_t children; 297f398b121Sjmcneill int i, n; 298f398b121Sjmcneill 299f398b121Sjmcneill if (dvname == NULL) { 300f398b121Sjmcneill if (depth > 0) 301f398b121Sjmcneill return; 302f398b121Sjmcneill *laa.l_devname = '\0'; 303f398b121Sjmcneill } else { 304f398b121Sjmcneill strlcpy(laa.l_devname, dvname, sizeof(laa.l_devname)); 305f398b121Sjmcneill } 306f398b121Sjmcneill 307f398b121Sjmcneill if (ioctl(fd, DRVLISTDEV, &laa) == -1) 3080cc54e49Schristos err(EXIT_FAILURE, "DRVLISTDEV"); 309f398b121Sjmcneill 310f398b121Sjmcneill children = laa.l_children; 311f398b121Sjmcneill 312f398b121Sjmcneill laa.l_childname = malloc(children * sizeof(laa.l_childname[0])); 313f398b121Sjmcneill if (laa.l_childname == NULL) 3140cc54e49Schristos err(EXIT_FAILURE, "DRVLISTDEV"); 315f398b121Sjmcneill if (ioctl(fd, DRVLISTDEV, &laa) == -1) 3160cc54e49Schristos err(EXIT_FAILURE, "DRVLISTDEV"); 317f398b121Sjmcneill if (laa.l_children > children) 3180cc54e49Schristos err(EXIT_FAILURE, "DRVLISTDEV: number of children grew"); 319f398b121Sjmcneill 320f398b121Sjmcneill for (i = 0; i < (int)laa.l_children; i++) { 321f398b121Sjmcneill for (n = 0; n < depth; n++) 322f398b121Sjmcneill printf(" "); 323f398b121Sjmcneill if (!nflag) { 324f398b121Sjmcneill printf("%s ", 325f398b121Sjmcneill (dvname == NULL) ? "root" : laa.l_devname); 326f398b121Sjmcneill } 327f398b121Sjmcneill printf("%s\n", laa.l_childname[i]); 328f398b121Sjmcneill if (tflag) { 329f398b121Sjmcneill list_children(fd, laa.l_childname[i], nflag, 330f398b121Sjmcneill tflag, depth + 1); 331f398b121Sjmcneill } 332f398b121Sjmcneill } 333f398b121Sjmcneill 334f398b121Sjmcneill free(laa.l_childname); 335f398b121Sjmcneill } 336