/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include <stdio.h> #include <stdarg.h> #include <sys/param.h> #include <locale.h> #include <dirent.h> #include <fcntl.h> #include <door.h> #include <errno.h> #include <sys/mman.h> #include <getopt.h> #include <assert.h> #include <sys/stat.h> #include <sys/usb/usba/wusba_io.h> #include <sys/usb/clients/wusb_ca/wusb_ca.h> #include "crypto_util.h" #include "wusbd.h" /* * EXIT STATUS * The following exit values are returned: * 0 Successful operation * 1 Error: the operation failed. * 2 Usage error. */ #define WUSB_EXIT_SUCCESS 0 #define WUSB_EXIT_FAILURE 1 #define WUSB_EXIT_USAGE 2 #define ASSO_CABLE_NAME "wusb_ca" #define WUSB_MAX_LEN 255 #define WUSB_HOSTID_LEN 2 #define WUSB_DEVID_LEN 6 /* wusba admin list options */ #define WUSB_FIELD_WIDTH 20 #define WUSB_LIST_HOST 0x01 #define WUSB_LIST_DEV 0x02 #define WUSB_LIST_HD (WUSB_LIST_HOST | WUSB_LIST_DEV) #define WUSB_LIST_ID 0x04 #define WUSB_LIST_TYPE 0x08 #define WUSB_LIST_STATE 0x10 #define WUSB_LIST_ALL (WUSB_LIST_ID | WUSB_LIST_TYPE | WUSB_LIST_STATE) /* cable device list */ typedef struct dev_list { char path[MAXPATHLEN]; struct dev_list *next; } dev_list_t; static wusb_device_info_t *dev_lists = NULL; static uint32_t cnt = 0; /* log and debug helpers */ static void wusb_prt(const char *, ...); static void wusb_usage(const char *, ...); static void wusb_fail(const char *, ...); static void wusb_opterr(int, int); /* load host/dev list helpers */ static void wusb_load_list(wusb_device_info_t **, uint32_t *cnt); static void wusb_free_list(wusb_device_info_t *); /* door call helpers */ static int wusb_door_req(int, door_arg_t *, char *, int); static void wusb_door_free(door_arg_t *); static uint16_t wusb_door_result(door_arg_t *da); /* check auths */ static void wusb_check_auth(const char *); /* usr input funcs */ static void user_confirm(char *); static void user_input(char *, char *, int); /* string translation helpers */ static uint32_t str2id(char *); static void usage(); /* list */ static const struct option wusb_list_opts[] = { { "host", no_argument, NULL, 'h'}, { "device", no_argument, NULL, 'd'}, { "output", required_argument, NULL, 'o'}, {0, 0, 0, 0} }; static const char *WUSB_LIST_HEADER[] = { "ID", /* host-id or dev-id */ "STATE", /* host or device states */ "TYPE", /* host or deivce types */ NULL }; static void do_list(int, char **); static void do_list_args(int, char **, char *); static void parse_subopts(char *, const char *); static int parse_option(char *, const char *); static void wusb_prt_titles(char); static void wusb_prt_lists(char, wusb_device_info_t *); static int find_dev_id(uint8_t, uint16_t); static void parse_dev_id(const char *, wusb_dev_ctrl_t *); static void parse_host_id(char *, uint8_t *); /* associate */ static struct option wusb_asso_opts[] = { { "host", required_argument, NULL, 'h'}, { "cable", no_argument, NULL, 'c'}, { "numeric", required_argument, NULL, 'n'}, { "force", no_argument, NULL, 'f'}, { "onetime", no_argument, NULL, 'o'}, { 0, 0, 0, 0} }; static void do_associate(int, char **); static void do_asso_args(int, char **, wusb_asso_ctrl_t *); static int input_host_id(uint8_t *); static void input_dev_id(wusb_dev_ctrl_t *); #ifdef NUMERIC_ENABLED static int input_asso_type(uint8_t *); #endif static int select_cable_device(char *); /* remove dev */ static struct option wusb_rmdev_opts[] = { { "host", required_argument, NULL, 'h'}, { "device", required_argument, NULL, 'd'}, { "force", no_argument, NULL, 'f'}, { 0, 0, 0, 0} }; /* remove/enable/disable host */ static struct option wusb_host_opts[] = { { "host", required_argument, NULL, 'h'}, { "force", no_argument, NULL, 'f'}, { 0, 0, 0, 0} }; static void do_host(int, char **, int); static void do_host_args(int, char **, int, wusb_dev_ctrl_t *); static void do_remove_host(int, char **); static void do_enable_host(int, char **); static void do_disable_host(int, char **); static void do_remove_dev(int, char **); static void do_remove_dev_args(int, char **, wusb_dev_ctrl_t *); /* error message maps */ struct errormsgs { int code; char *errmsg; } wusb_errors[] = { { WUSBADM_OK, "success" }, { WUSBADM_AUTH_FAILURE, "permisson denied" }, { WUSBADM_NO_HOST, "host does not exist" }, { WUSBADM_NO_DEVICE, "device does not exist" }, { WUSBADM_CCSTORE_ACC, "fail to access CC store" }, { WUSBADM_NO_SUPPORT, "command not supported" }, { WUSBADM_INVAL_HOSTID, "invalid host id" }, { WUSBADM_INVAL_DEVID, "invalid device id" }, { WUSBADM_HOST_NOT_ATTACH, "host not attached" }, { WUSBADM_FAILURE, "unknown error"} }; char * wusb_strerror(int err) { if (err < 0 || err > WUSBADM_FAILURE) { return (wusb_errors[WUSBADM_FAILURE].errmsg); } return (wusb_errors[err].errmsg); } /* * wusbadm cmd line tool is used for used to administrate the wireless usb * host and wireless usb devices. * list - List the device and host status * associated - Setup assocaition between host and devices. * remove-dev - Remove the assocation of device * remove-host - Remove the host information and all the devices assocaiton to * the host * enable-host - Enable a host to be ready to accept wireless devices * disable-host - Disable a host, host will not accpet connections */ int main(int argc, char **argv) { int i; static struct { const char *cmd; void (*func)(int, char **); const char *auth; } cmd_list[] = { { "list", do_list, WUSB_AUTH_READ}, { "associate", do_associate, WUSB_AUTH_MODIFY}, { "remove-dev", do_remove_dev, WUSB_AUTH_MODIFY}, { "remove-host", do_remove_host, WUSB_AUTH_HOST}, { "enable-host", do_enable_host, WUSB_AUTH_HOST}, { "disable-host", do_disable_host, WUSB_AUTH_HOST}, { NULL, NULL} }; (void) setlocale(LC_ALL, ""); #ifndef TEXT_DOMAIN #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); if (argc <= 1) { usage(); exit(WUSB_EXIT_USAGE); } /* start wusb damemon */ if (strcmp(argv[1], "--daemon") == 0) { (void) daemonize(); exit(WUSB_EXIT_SUCCESS); } /* wusbadm entry */ for (i = 0; cmd_list[i].cmd; i++) { if (strcmp(cmd_list[i].cmd, argv[1]) == 0) { break; } } if (!cmd_list[i].cmd) { wusb_usage("unknown option %s", argv[1]); } wusb_check_auth(cmd_list[i].auth); wusb_load_list(&dev_lists, &cnt); cmd_list[i].func(argc - 1, &argv[1]); wusb_free_list(dev_lists); return (WUSB_EXIT_SUCCESS); } static void usage() { wusb_prt("\nUsage:\twusbadm sub-command args ...\n\n"); wusb_prt("\tlist [-h | -d] [-o field[,...]]\n"); wusb_prt("\tassociate [-h host-id] [[-c [-f]] | -n] [-o]\n"); wusb_prt("\tremove-dev [[-d dev-id] | [-h host-id]] [-f]\n"); wusb_prt("\tremove-host [-h host-id] [-f]\n"); wusb_prt("\tenable-host [-h host-id]\n"); wusb_prt("\tdisable-host [-h host-id] [-f]\n"); wusb_prt("\n"); } /* * list command routine. * wusbadmin list [-h | -d] [-o field[,...]] * 1. parse the options * 2. load host/deivce info from daemon * 3. print titles accoding to list options * 4. print host/deivce list one by one */ static void do_list(int argc, char **argv) { char fields = 0x0; int i; /* parse the list options */ do_list_args(argc, argv, &fields); /* print list title */ wusb_prt_titles(fields); /* print out the result */ for (i = 0; i < cnt; i++) { wusb_prt_lists(fields, &dev_lists[i]); } } /* * associate command routine * wusbadmin associate [-h host-id] [[-c [-f] | -n] [-o] * 1. Parse the options and get user input * 2. Send the asso infor the daemon */ static void do_associate(int argc, char **argv) { door_arg_t da; wusb_asso_ctrl_t asso_ctrl; uint16_t rval = 0; /* Get association options */ bzero(&asso_ctrl, sizeof (wusb_asso_ctrl_t)); do_asso_args(argc, argv, &asso_ctrl); /* open door file */ (void) wusb_door_req(WUSB_DCMD_ASSOCIATE, &da, (char *)&asso_ctrl, sizeof (wusb_asso_ctrl_t)); /* association result */ rval = wusb_door_result(&da); wusb_door_free(&da); if (rval != WUSBADM_OK) { wusb_fail("%s", wusb_strerror(rval)); } } /* * remove-dev command routine * remove-dev [[-d dev-id] | [-h host-id]] [-f] * 1. parse options/user input * 2. send message to daemon. * dev-id != 0 means remove one dev * dev-id == 0 means remove all dev with a host */ static void do_remove_dev(int argc, char **argv) { wusb_dev_ctrl_t devctrl; door_arg_t da; uint16_t rval = WUSBADM_OK; /* parse options */ bzero(&devctrl, sizeof (wusb_dev_ctrl_t)); do_remove_dev_args(argc, argv, &devctrl); /* send command to daemon */ (void) wusb_door_req(WUSB_DCMD_REMOVE_DEV, &da, (char *)&devctrl, sizeof (wusb_dev_ctrl_t)); rval = wusb_door_result(&da); wusb_door_free(&da); if (rval != WUSBADM_OK) { wusb_fail("%s", wusb_strerror(rval)); } } /* * Send the LOAD_CC request to daemon. Daemon will allocate memory and put * all CCs in that block of memory. We need to free the memory here. * CCs are in data array format. */ static void wusb_load_list(wusb_device_info_t **cc_list, uint32_t *cnt) { door_arg_t da; size_t buflen = 0; uint32_t num = 0; uint16_t rval = WUSBADM_OK; /* send command to daemon */ (void) wusb_door_req(WUSB_DCMD_LIST_DATA, &da, 0, 0); rval = wusb_door_result(&da); if (rval != WUSBADM_OK) { wusb_door_free(&da); wusb_fail("%s", wusb_strerror(rval)); } /* number of the devinfo list */ (void) memcpy(&num, da.data_ptr+sizeof (uint16_t), sizeof (uint32_t)); if (num) { buflen = (num) * sizeof (wusb_device_info_t); if ((*cc_list = malloc(buflen)) == NULL) { wusb_door_free(&da); wusb_fail("list: malloc buffer failed"); } (void) memcpy(*cc_list, da.data_ptr + sizeof (uint32_t) + sizeof (uint16_t), buflen); } *cnt = num; /* unmap the buffer */ wusb_door_free(&da); } static void wusb_free_list(wusb_device_info_t *cc_list) { if (cc_list) { free(cc_list); } cnt = 0; } /* * This is a wrapper of door call for wusb adm tool. * Mandatory: * cmd - wusb admin command (WUSB_DCMD_*). * da - door call arg. * Optional: * databuf - data send to daemon. * size - data buf size. */ static int wusb_door_req(int cmd, door_arg_t *da, char *databuf, int size) { wusb_door_call_t dcall; int fd = -1; bzero(&dcall, sizeof (wusb_door_call_t)); dcall.cmdss = cmd; /* copy data buffer */ if (databuf) { (void) memcpy(dcall.buf, databuf, size); } /* set rbuf to 0, unmap the data buf later */ bzero(da, sizeof (door_arg_t)); da->data_ptr = (char *)&dcall; da->data_size = sizeof (wusb_door_call_t); da->rbuf = 0; da->rsize = 0; /* open door file */ if ((fd = open(DOOR_FILE, O_RDONLY)) < 0) { wusb_fail("daemon not started"); } /* make door call */ if (door_call(fd, da) != 0) { (void) close(fd); wusb_fail("daemon out of service:%s", strerror(errno)); } (void) close(fd); if (da->data_size == 0) { wusb_fail("no data from daemon"); } return (WUSBA_SUCCESS); } /* * After each door call return, the first 2 bytes of the data * returned is encoded as the door call result from daemon. * This is a wrapper to get the door call result */ uint16_t wusb_door_result(door_arg_t *da) { uint16_t rval = 0; (void) memcpy(&rval, da->data_ptr, sizeof (uint16_t)); return (rval); } /* * Unmap the buffer after door call. * It is mandatory after any wusb_door_call since we set the rbuf to NULL * in the wusb_door_call. So any buffer returned is from the client proces. * See door_call(3C) for more infor */ static void wusb_door_free(door_arg_t *da) { (void) munmap(da->rbuf, da->rsize); } /* * wusbadmin remove-host routine * remove-host [-h host-id] [-f] */ static void do_remove_host(int argc, char **argv) { do_host(argc, argv, WUSB_DCMD_REMOVE_HOST); } /* * wusbadmin enable-host routine * enable-host [-h host-id] */ static void do_enable_host(int argc, char **argv) { do_host(argc, argv, WUSB_DCMD_ENABLE_HOST); } /* * wusbadmin disable-host routine * disable-host [-h host-id] [-f] */ static void do_disable_host(int argc, char **argv) { do_host(argc, argv, WUSB_DCMD_DISABLE_HOST); } /* * wusb do host routine. The wrapper for all host related * subcommand (enable-host, disable-host, remove-host). * 1. parser options/user input * 2. send wusb command to daemon */ static void do_host(int argc, char **argv, int cmd) { wusb_dev_ctrl_t hostctrl; door_arg_t da; uint16_t rval = 0; /* parse options */ bzero(&hostctrl, sizeof (wusb_dev_ctrl_t)); do_host_args(argc, argv, cmd, &hostctrl); /* door call to daemon */ (void) wusb_door_req(cmd, &da, (char *)&hostctrl, sizeof (wusb_dev_ctrl_t)); rval = wusb_door_result(&da); wusb_door_free(&da); if (rval != WUSBADM_OK) { wusb_fail("%s", wusb_strerror(rval)); } } /* * wusb list option parser * wusbadmin list [-h | -d] [-o field[,...]] */ static void do_list_args(int argc, char **argv, char *option) { char fields = 0x0; int c; while ((c = getopt_long(argc, argv, ":hdo:", wusb_list_opts, NULL)) != -1) { switch (c) { case 'h': if (fields & WUSB_LIST_HOST) { wusb_usage("too many -h specified"); } if (fields & WUSB_LIST_DEV) { wusb_usage("-h and -d used together"); } fields |= WUSB_LIST_HOST; break; case 'd': if (fields & WUSB_LIST_HOST) { wusb_usage("-h and -d used together"); } if (fields & WUSB_LIST_DEV) { wusb_usage("too many -d specified"); } fields |= WUSB_LIST_DEV; break; case 'o': if (strlen(optarg) > 63) { wusb_usage("options too long"); } (void) parse_option(&fields, optarg); break; default: wusb_opterr(optopt, c); break; } } if (optind < argc) { wusb_usage("unrecognized options:%s", argv[optind++]); } /* if no option specified,print out all fields */ fields |= (fields & WUSB_LIST_HD)? 0x00:WUSB_LIST_HD; fields |= (fields & WUSB_LIST_ALL)? 0x00:WUSB_LIST_ALL; *option = fields; } /* * Print the header for list subcommand. * Each title is right aligned with length of WUSB_FIELD_WIDTH * The following titles will be printed if the relative tags * marked in the fields option. * ID STATE TYPE */ static void wusb_prt_titles(char fields) { int i = 0; char option; for (option = WUSB_LIST_ID; option <= WUSB_LIST_STATE; option <<= 1, i++) { if (fields & option) { wusb_prt("%-*s", WUSB_FIELD_WIDTH, WUSB_LIST_HEADER[i]); } } (void) putchar('\n'); } /* * Append the host-id / dev-id to the output buf. * host-id - 2 digits number (XX) * dev-id - 5 digits number (XX.XXX) * See wusbadm (1M) for more */ static void append_id(char *buf, wusb_device_info_t *devinfo) { char tmp[WUSB_MAX_LEN] = {'\0'}; if (devinfo->dev) { (void) snprintf(tmp, WUSB_MAX_LEN, "%02d.%03d", devinfo->host, devinfo->dev); } else { (void) snprintf(tmp, WUSB_MAX_LEN, "%02d", devinfo->host); } (void) snprintf(buf, WUSB_MAX_LEN, "%s%-*s", buf, WUSB_FIELD_WIDTH, tmp); } /* * Append state to the output buf. * host - enabled/disabled * device - connected/disconnected * See wusbadm (1M) for more */ static void append_state(char *buf, wusb_device_info_t *devinfo) { const char *WUSB_DEV_STATE_MSG[] = { "disconnected", /* WUSB_STATE_UNCONNTED */ "connected", /* WUSB_STATE_CONNTING */ "connected", /* WUSB_STATE_UNAUTHENTICATED */ "connected", /* WUSB_STATE_DEFAULT */ "connected", /* WUSB_STATE_ADDRESSED */ "connected", /* WUSB_STATE_CONFIGURED */ "connected", /* WUSB_STATE_SLEEPING */ "connected", /* WUSB_STATE_RECONNTING */ NULL }; const char *WUSB_HOST_STATE_MSG[] = { "disconnected", /* WUSB_HC_DISCONNTED */ "disabled", /* WUSB_HC_STOPPED */ "enabled", /* WUSB_HC_STARTED */ "disabled", /* WUSB_HC_CH_STOPPED */ NULL }; char tmp[WUSB_MAX_LEN] = {'\0'}; if (devinfo->dev) { /* append the state for device */ if (devinfo->stat > WUSB_STATE_RECONNTING) { (void) snprintf(tmp, WUSB_MAX_LEN, "%s", "unknown"); } else { (void) snprintf(tmp, WUSB_MAX_LEN, "%s", WUSB_DEV_STATE_MSG[devinfo->stat]); } } else { /* append the state for host */ if (devinfo->stat > WUSB_HC_CH_STOPPED) { (void) snprintf(tmp, WUSB_MAX_LEN, "%s", "unknown"); } else { (void) snprintf(tmp, WUSB_MAX_LEN, "%s", WUSB_HOST_STATE_MSG[devinfo->stat]); } } (void) snprintf(buf, WUSB_MAX_LEN, "%s%-*s", buf, WUSB_FIELD_WIDTH, tmp); } /* * Appenend host/dev type to the ouput buf string * Currently map the file name to specific types * TODO: how to define the type */ static void append_type(char *buf, wusb_device_info_t *devinfo) { (void) snprintf(buf, WUSB_MAX_LEN, "%s%-*s", buf, WUSB_FIELD_WIDTH, devinfo->type); } /* * This is core func to print wireless device list on systems. * Print the devinfo list entry with option field */ static void wusb_prt_lists(char fields, wusb_device_info_t *devinfo) { char buf[WUSB_MAX_LEN+1] = {'\0'}; int i = 0; char opt = 0; void (*append_funcs[])(char *, wusb_device_info_t *) = { append_id, append_state, append_type, NULL }; /* check if dev or host need to be print out */ if ((devinfo->dev && !(fields & WUSB_LIST_DEV)) || (!devinfo->dev && !(fields & WUSB_LIST_HOST))) { return; } /* Append all the enabled fields to the output buf */ for (i = 0, opt = WUSB_LIST_ID; opt <= WUSB_LIST_STATE; opt <<= 1, i++) { if (fields & opt) { append_funcs[i](buf, devinfo); } } wusb_prt("%s\n", buf); } /* * wusb association option parser * wusbadmin association [-h host-id] [[-c [-f] | -n] [-o] * Note:Only cable association is supported now */ static void do_asso_args(int argc, char **argv, wusb_asso_ctrl_t *asso_ctrl) { int c; int force = 0; while ((c = getopt_long(argc, argv, ":h:cfno", wusb_asso_opts, 0)) != -1) { switch (c) { case 'h': parse_host_id(optarg, &(asso_ctrl->host)); break; case 'c': asso_ctrl->type |= ASSO_TYPE_CABLE; break; case 'n': asso_ctrl->type |= ASSO_TYPE_NUMERIC; break; case 'f': force = 1; break; case 'o': asso_ctrl->onetime = 1; break; default: wusb_opterr(optopt, c); break; } } if (optind < argc) { wusb_usage("unrecognized options:%s", argv[optind++]); } /* TODO: support cable association */ if (asso_ctrl->type & ASSO_TYPE_NUMERIC) { wusb_fail("Numeric association not supported"); } /* get user input host id */ if (!asso_ctrl->host) { (void) input_host_id(&asso_ctrl->host); } /* get user input association type */ if (!asso_ctrl->type) { asso_ctrl->type |= ASSO_TYPE_CABLE; /* Todo: Will be enabled after Numberic Assocation support */ #ifdef NUMERIC_ENABLED (void) input_asso_type(&asso_ctrl->type); #endif } /* get user input cable device to associate */ if (asso_ctrl->type == ASSO_TYPE_CABLE) { (void) select_cable_device(asso_ctrl->path); } /* confirm with user to continue or not */ if (!force) { wusb_prt("Associate device (%s) with host (%02d) via cable\n", asso_ctrl->path, asso_ctrl->host); user_confirm("Continue "); } } /* * Convert a string to an id (host-id/dev-id/cable-dev-id) * Fail if 0 returned, since each id is indexed from 1. * Widely used to handle user input ids. */ static uint32_t str2id(char *arg) { uint32_t id = 0; /* check the string and generate int result */ while (*arg) { if (*arg < '0' || *arg > '9') { return (0); } id = id*10+(*arg-'0'); arg++; } return (id); } static void parse_host_id(char *arg, uint8_t *host) { int len = strlen(arg); if ((len > WUSB_HOSTID_LEN) || (len == 0)) { wusb_fail("host-id should be 2 digits"); } if ((*host = str2id(arg)) == 0) { wusb_fail("invalid host id:%s", arg); } if (find_dev_id(*host, 0) < 0) { wusb_fail("host-id does not exist: %02d ", *host); } return; } /* * Get the host from user input. * 1. list all the host id from the daemon * 2. Ask user to input the host id * 3. Check host id and return */ static int input_host_id(uint8_t *host) { char fields = WUSB_LIST_HOST | WUSB_LIST_ALL; char buf[WUSB_MAX_LEN] = {'\0'}; int i = 0; /* show avaialbe host id to usr */ wusb_prt_titles(fields); for (i = 0; i < cnt; i++) { wusb_prt_lists(fields, &dev_lists[i]); } /* get user input of host id */ user_input("Please select 2 digits host-id:", buf, WUSB_MAX_LEN-1); parse_host_id(buf, host); return (WUSBA_SUCCESS); } static void input_dev_id(wusb_dev_ctrl_t *devctrl) { char fields = WUSB_LIST_DEV | WUSB_LIST_ALL; char buf[WUSB_MAX_LEN] = {'\0'}; int i = 0; /* show avaialbe host id to usr */ wusb_prt_titles(fields); for (i = 0; i < cnt; i++) { wusb_prt_lists(fields, &dev_lists[i]); } /* get user input of host id */ user_input("Please select dev-id:", buf, WUSB_MAX_LEN-1); parse_dev_id(buf, devctrl); } static int find_dev_id(uint8_t host, uint16_t dev) { int rval = WUSBA_FAILURE; int i; for (i = 0; i < cnt; i++) { if ((dev_lists[i].dev == dev) && (dev_lists[i].host == host)) { rval = WUSBA_SUCCESS; break; } } return (rval); } /* * Select assocation type. * - Cable * - Numeric Not supported */ #ifdef NUMERIC_ENABLED static int input_asso_type(uint8_t *asso_type) { char buf[15] = {'\0'}; user_input("Select association type (c/n) :", buf, 14); if (strcasecmp(buf, "c") == 0) { *asso_type = ASSO_TYPE_CABLE; } else if (strcasecmp(buf, "n") == 0) { *asso_type = ASSO_TYPE_NUMERIC; } else { wusb_usage("invalid association type"); } return (WUSBA_SUCCESS); } #endif /* * Create a list contains all the cable devices on the system */ static void init_cable_devices(dev_list_t **dev_lists, int *num) { struct dirent *entry = NULL; dev_list_t *_devlist = NULL; DIR *dirp = opendir(WUSB_HOST_PATH); char filename[MAXPATHLEN] = {'\0'}; *num = 0; /* * walk on all the filename in the /dev/usb, check the filename * to see if it is a cable asso filename and add it to the devinfo * list if so */ if (!dirp) { wusb_fail("cable device not available"); } while ((entry = readdir(dirp)) != NULL) { /* searching for cable node */ if (strstr(entry->d_name, ASSO_CABLE_NAME) == NULL) { continue; } (void) snprintf(filename, MAXPATHLEN, "%s/%s", WUSB_HOST_PATH, entry->d_name); /* add the filename to the dev list */ if (_devlist == NULL) { _devlist = malloc(sizeof (dev_list_t)); *dev_lists = _devlist; } else { _devlist->next = malloc(sizeof (dev_list_t)); _devlist = _devlist->next; } /* this need to be freed */ (void) snprintf(_devlist->path, MAXPATHLEN, "%s", filename); _devlist->next = NULL; /* increase the list number */ (*num)++; } (void) closedir(dirp); } /* Free the devlist created for cable device */ static void free_devlist(dev_list_t *dev_list) { dev_list_t *head = dev_list; while (head) { head = dev_list->next; free(dev_list); dev_list = head; } } /* find the cable dev with the user-inputed index */ static dev_list_t * get_cable_dev(dev_list_t *dev_list, int index) { int i = 1; while ((i != index) && dev_list) { dev_list = dev_list->next; i++; } return (dev_list); } /* print the cable devlist with index */ static void show_devlist(dev_list_t *dev_list) { /* show all the cable devices to user */ int index = 1; wusb_prt("Cable devices on the system:\n"); while (dev_list) { wusb_prt("%03d. %s\n", index, dev_list->path); dev_list = dev_list->next; index++; } } /* * when doing association, all the cable devices on the system * should be print out to the user */ static int select_cable_device(char *device) { /* cable association */ char buf[32]; int cableid = 1; dev_list_t *head = NULL; dev_list_t *tmp = NULL; int devnum = 0; /* get all the cable dev on the system */ init_cable_devices(&head, &devnum); /* Get the device name as user input */ if (!head) { wusb_fail("no cable devices found "); } if (devnum != 1) { show_devlist(head); /* get the user input of the cable dev index */ user_input("Select cable device to associate:", buf, 19); if (strlen(buf) != 3) { wusb_fail("cable device id should be 3 digits"); } cableid = str2id(buf); /* check user iput */ if ((cableid <= 0) || (cableid > devnum)) { free_devlist(head); wusb_fail("invalid cable device "); } } else { /* if only one dev exist, use it without asking user */ cableid = 1; } /* find the device to associate */ tmp = get_cable_dev(head, cableid); (void) snprintf(device, MAXPATHLEN, "%s", tmp->path); /* free the list */ free_devlist(head); return (WUSBA_SUCCESS); } /* * Parse the -o option for wusbadm list */ static int parse_option(char *fields, const char *optarg) { char *lasts = NULL, *token = NULL; char buf[64] = { '\0' }; (void) snprintf(buf, 64, "%s", optarg); if ((token = strtok_r(buf, ",", &lasts)) != 0) { parse_subopts(fields, token); while ((token = strtok_r(NULL, ",", &lasts))) { parse_subopts(fields, token); } } return (WUSBA_SUCCESS); } /* * wusbadmin list * parse the sub option extracted from -o options */ static void parse_subopts(char *fields, const char *str) { int i; char opt; for (i = 0, opt = WUSB_LIST_ID; opt <= WUSB_LIST_STATE; i++) { if (strcasecmp(str, WUSB_LIST_HEADER[i]) == 0) { *fields |= opt; break; } opt = opt << 1; } if (opt > WUSB_LIST_STATE) { wusb_usage("unrecognized options:%s", str); } } /* * Device id parser for remove-dev * dev id is 5 digits with format XX.XXX */ void parse_dev_id(const char *arg, wusb_dev_ctrl_t *devctrl) { char buf[WUSB_DEVID_LEN+1] = {'\0'}; char *tmp = NULL; if (strlen(arg) > WUSB_DEVID_LEN) goto fail; (void) snprintf(buf, WUSB_DEVID_LEN+1, "%s", arg); if ((tmp = strchr(buf, '.')) == NULL) goto fail; /* get host id */ *tmp = '\0'; if ((devctrl->host = str2id(buf)) == 0) { goto fail; } /* get device id */ if ((devctrl->dev = str2id(tmp+1)) == 0) { goto fail; } if (find_dev_id(devctrl->host, devctrl->dev) < 0) { wusb_fail("dev-id does not exist: %02d.%03d ", devctrl->host, devctrl->dev); } return; fail: wusb_fail("unknown device id:%s", arg); } /* * remove-dev options parser * remove-dev [[-d dev-id] | [-h host-id]] [-f] */ static void do_remove_dev_args(int argc, char **argv, wusb_dev_ctrl_t *devctrl) { int c; int force = 0; bzero(devctrl, sizeof (wusb_dev_ctrl_t)); while ((c = getopt_long(argc, argv, ":h:d:f", wusb_rmdev_opts, NULL)) != -1) { switch (c) { case 'h': if (devctrl->dev) { wusb_usage("-h -d can not be" "used together"); } if (devctrl->host) { wusb_usage("multi -h is not allowed"); } /* get 2 digit host id */ parse_host_id(optarg, &(devctrl->host)); break; case 'd': if (devctrl->dev) { wusb_usage("multi -d is not allowed"); } if (devctrl->host) { wusb_usage("-h -d can not be" "used together"); } /* parse devid */ (void) parse_dev_id(optarg, devctrl); break; case 'f': force = 1; break; default: wusb_opterr(optopt, c); break; } } if (optind < argc) { wusb_usage("unrecognized options:%s", argv[optind++]); } if ((devctrl->host == 0) && (devctrl->dev == 0)) { input_dev_id(devctrl); } /* confirm with user to continue or not */ if (!force) { if (devctrl->dev) { wusb_prt("Remove the device's association information" " of device (%02d.%03d) from system.\nThis device" " can not be connected with the host until it is" " associated again.\n", devctrl->host, devctrl->dev); } else { wusb_prt("Remove the information of all the devices " "associated with host (%02d) from the system\n" "All the devices asociated with the host can not" " be connected with it until they are associated" " again.\n", devctrl->host); } user_confirm("Continue "); } } /* * Confirm with user continue or not * info: the information shown to user before input */ static void user_confirm(char *info) { char yesorno[20]; wusb_prt(info); user_input("(yes/no): ", yesorno, 19); if (strcasecmp(yesorno, "no") == 0) { wusb_fail(""); } if (strcasecmp(yesorno, "n") == 0) { wusb_fail(""); } if (strcasecmp(yesorno, "yes") == 0) { return; } if (strcasecmp(yesorno, "y") == 0) { return; } wusb_fail("illegal input: %s", yesorno); } /* * Get user input * msg(in): infor shown to user before input * length(in): buf size to save uer input * buf(out): user input saved in buffer */ static void user_input(char *msg, char *buf, int length) { int i = 0, b; wusb_prt(msg); /*CONSTCOND*/ while (1) { b = getc(stdin); if (b == '\n' || b == '\0' || b == EOF) { if (i < length) buf[i] = 0; break; } if (i < length) buf[i] = b; i++; } if (i >= length) { buf[length] = 0; } } /* * do host options parser * remove-host [-h host-id] [-f] * enable-host [-h host-id] * disable-host [-h host-id] [-f] */ static void do_host_args(int argc, char **argv, int cmd, wusb_dev_ctrl_t *hostctrl) { int c; int force = 0; while ((c = getopt_long(argc, argv, ":h:f", wusb_host_opts, NULL)) != -1) { switch (c) { case 'h': if (hostctrl->host) { wusb_usage("multi -h is not allowed"); } /* 2 digits host id */ parse_host_id(optarg, &(hostctrl->host)); break; case 'f': /* enable host does not need -f */ if (cmd == WUSB_DCMD_ENABLE_HOST) { wusb_opterr(optopt, c); } force = 1; break; default: wusb_opterr(optopt, c); break; } } if (optind < argc) { wusb_usage("unrecognized options:%s", argv[optind++]); } /* * all the host related command can be used without a specific * host-id, so list all the hosts avalable to users for selection */ if (hostctrl->host == 0) { (void) input_host_id(&(hostctrl->host)); } /* confirm with user to continue or not */ if (!force && (cmd != WUSB_DCMD_ENABLE_HOST)) { switch (cmd) { case WUSB_DCMD_DISABLE_HOST: wusb_prt("Disable host (%02d).\nAll the" " devices connected with the host will be" " disconnected\n", hostctrl->host); break; case WUSB_DCMD_REMOVE_HOST: wusb_prt("Remove host (%02d).\nAll the" " association with the host will be" " removed\n", hostctrl->host); break; default: break; } user_confirm("Continue"); } } static void wusb_check_auth(const char *auth) { uid_t uid = geteuid(); if (chk_auths(uid, auth) < 0) { wusb_fail("%s", wusb_strerror(WUSBADM_AUTH_FAILURE)); } } /* * wusb exit helper funcstion * wusb_fail or wusb_usage */ static void wusb_fail(const char *format, ...) { va_list alist; format = gettext(format); (void) fprintf(stderr, gettext("wusbadm: ")); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); (void) fprintf(stderr, "\n"); wusb_free_list(dev_lists); exit(WUSB_EXIT_FAILURE); } static void wusb_usage(const char *format, ...) { va_list alist; format = gettext(format); (void) fprintf(stderr, gettext("wusbadm: ")); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); (void) fprintf(stderr, "\n"); usage(); wusb_free_list(dev_lists); exit(WUSB_EXIT_USAGE); } /* wusb print helper func */ static void wusb_prt(const char *format, ...) { va_list alist; format = gettext(format); va_start(alist, format); (void) vfprintf(stdout, format, alist); va_end(alist); } /* wusb option failuer func */ static void wusb_opterr(int opt, int opterr) { switch (opterr) { case ':': wusb_usage("option '-%c' requires a value", opt); break; case '?': default: wusb_usage("unrecognized option '-%c'", opt); break; } }