1/*======================================================================
2
3    PCMCIA device control program
4
5    cardctl.c 1.70 2004/04/09 03:54:53
6
7    The contents of this file are subject to the Mozilla Public
8    License Version 1.1 (the "License"); you may not use this file
9    except in compliance with the License. You may obtain a copy of
10    the License at http://www.mozilla.org/MPL/
11
12    Software distributed under the License is distributed on an "AS
13    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14    implied. See the License for the specific language governing
15    rights and limitations under the License.
16
17    The initial developer of the original code is David A. Hinds
18    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
19    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
20
21    Alternatively, the contents of this file may be used under the
22    terms of the GNU General Public License version 2 (the "GPL"), in
23    which case the provisions of the GPL are applicable instead of the
24    above.  If you wish to allow the use of your version of this file
25    only under the terms of the GPL and not to allow others to use
26    your version of this file under the MPL, indicate your decision
27    by deleting the provisions above and replace them with the notice
28    and other provisions required by the GPL.  If you do not delete
29    the provisions above, a recipient may use your version of this
30    file under either the MPL or the GPL.
31
32======================================================================*/
33#if (defined(__BEOS__) || defined(__HAIKU__))
34#include <OS.h>
35#endif
36
37#include <sys/types.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42#include <fcntl.h>
43#include <errno.h>
44#include <ctype.h>
45#include <stdarg.h>
46#include <sys/time.h>
47#include <sys/ioctl.h>
48#include <sys/stat.h>
49#include <sys/wait.h>
50#include <sys/stat.h>
51
52#include <pcmcia/version.h>
53#include <pcmcia/config.h>
54#include <pcmcia/cs_types.h>
55#include <pcmcia/cs.h>
56#include <pcmcia/cistpl.h>
57#include <pcmcia/ds.h>
58
59/*====================================================================*/
60
61#ifdef ETC
62static char *configpath = ETC;
63#else
64static char *configpath = "/etc/pcmcia";
65#endif
66
67static char *scheme, *stabfile;
68
69/*====================================================================*/
70#if (!defined(__BEOS__) && !defined(__HAIKU__))
71static int major = 0;
72
73static int lookup_dev(char *name)
74{
75    FILE *f;
76    int n;
77    char s[32], t[32];
78
79    f = fopen("/proc/devices", "r");
80    if (f == NULL)
81	return -errno;
82    while (fgets(s, 32, f) != NULL) {
83	if (sscanf(s, "%d %s", &n, t) == 2)
84	    if (strcmp(name, t) == 0)
85		break;
86    }
87    fclose(f);
88    if (strcmp(name, t) == 0)
89	return n;
90    else
91	return -ENODEV;
92} /* lookup_dev */
93#endif
94
95/*====================================================================*/
96
97static int open_sock(int sock)
98{
99#if (defined(__BEOS__) || defined(__HAIKU__))
100    char fn[B_OS_NAME_LENGTH];
101    sprintf(fn, "/dev/bus/pcmcia/sock/%d", sock);
102    return open(fn, O_RDONLY);
103#else
104    static char *paths[] = {
105	"/var/lib/pcmcia", "/var/run", "/dev", "/tmp", NULL
106    };
107    int fd;
108    char **p, fn[64];
109    dev_t dev = makedev(major, sock);
110
111    for (p = paths; *p; p++) {
112	sprintf(fn, "%s/cc-%d", *p, getpid());
113	if (mknod(fn, (S_IFCHR|S_IREAD|S_IWRITE), dev) == 0) {
114	    fd = open(fn, O_RDONLY);
115	    unlink(fn);
116	    if (fd >= 0)
117		return fd;
118	    if (errno == ENODEV)
119		break;
120	}
121    }
122    return -1;
123#endif
124} /* open_sock */
125
126/*====================================================================*/
127
128static void print_status(cs_status_t *status)
129{
130    char *v = "5";
131    if (status->Function == 0) {
132	printf("  ");
133	if (status->CardState & CS_EVENT_3VCARD)
134	    v = "3.3";
135	else if (status->CardState & CS_EVENT_XVCARD)
136	    v = "X.X";
137	if (status->CardState & CS_EVENT_CB_DETECT)
138	    printf("%sV CardBus card", v);
139	else if (status->CardState & CS_EVENT_CARD_DETECT)
140	    printf("%sV 16-bit PC Card", v);
141	else
142	    printf("no card");
143	if (status->CardState & CS_EVENT_PM_SUSPEND)
144	    printf(" [suspended]");
145	printf("\n");
146    }
147    if ((status->CardState & CS_EVENT_PM_SUSPEND) ||
148	!(status->CardState & CS_EVENT_CARD_DETECT))
149	return;
150    printf("  function %d: ", status->Function);
151    printf("%s", (status->CardState & CS_EVENT_READY_CHANGE)
152	   ? "[ready]" : "[busy]");
153    if (status->CardState & CS_EVENT_WRITE_PROTECT)
154	printf(", [wp]");
155    if (status->CardState & CS_EVENT_BATTERY_DEAD)
156	printf(", [bat dead]");
157    if (status->CardState & CS_EVENT_BATTERY_LOW)
158	printf(", [bat low]");
159    if (status->CardState & CS_EVENT_REQUEST_ATTENTION)
160	printf(", [req attn]");
161    printf("\n");
162} /* print_status */
163
164/*====================================================================*/
165
166static void print_config(config_info_t *config)
167{
168    if (config->Function == 0) {
169	printf("  Vcc %.1fV  Vpp1 %.1fV  Vpp2 %.1fV\n",
170	       config->Vcc/10.0, config->Vpp1/10.0, config->Vpp2/10.0);
171	if (!(config->Attributes & CONF_VALID_CLIENT))
172	    return;
173	printf("  interface type is ");
174	switch (config->IntType) {
175	case INT_MEMORY:
176	    printf("\"memory-only\"\n"); break;
177	case INT_MEMORY_AND_IO:
178	    printf("\"memory and I/O\"\n"); break;
179	case INT_CARDBUS:
180	    printf("\"cardbus\"\n"); break;
181	}
182	if (config->AssignedIRQ != 0) {
183	    printf("  irq %d", config->AssignedIRQ);
184	    switch (config->IRQAttributes & IRQ_TYPE) {
185	    case IRQ_TYPE_EXCLUSIVE:
186		printf(" [exclusive]"); break;
187	    case IRQ_TYPE_TIME:
188		printf(" [multiplexed]"); break;
189	    case IRQ_TYPE_DYNAMIC_SHARING:
190		printf(" [shared]"); break;
191	    }
192	    if (config->IRQAttributes & IRQ_PULSE_ALLOCATED)
193		printf(" [pulse]");
194	    else
195		printf(" [level]");
196	    if (!(config->Attributes & CONF_ENABLE_IRQ))
197		printf(" [disabled]");
198	    printf("\n");
199	}
200	if (config->Attributes & CONF_ENABLE_DMA)
201	    printf("  DMA mode is enabled\n");
202	if (config->Attributes & CONF_ENABLE_SPKR)
203	    printf("  speaker output is enabled\n");
204
205    }
206
207    if (!(config->Attributes & CONF_VALID_CLIENT))
208	return;
209
210    printf("  function %d:\n", config->Function);
211
212    if (config->CardValues) {
213	printf("    config base %#06x\n", config->ConfigBase);
214	printf("    ");
215	if (config->CardValues & CV_OPTION_VALUE)
216	    printf("  option 0x%02x", config->Option);
217	if (config->CardValues & CV_STATUS_VALUE)
218	    printf(" status 0x%02x", config->Status);
219	if (config->CardValues & CV_PIN_REPLACEMENT)
220	    printf(" pin 0x%02x", config->Pin);
221	if (config->CardValues & CV_COPY_VALUE)
222	    printf(" copy 0x%02x", config->Copy);
223	if (config->CardValues & CV_EXT_STATUS)
224	    printf(" ext 0x%02x", config->ExtStatus);
225	printf("\n");
226    }
227
228    if (config->NumPorts1 > 0) {
229	printf("    io %#06x-%#06x", config->BasePort1,
230	       config->BasePort1 + config->NumPorts1 - 1);
231	if (config->IntType == INT_CARDBUS) {
232	    printf(" [32bit]\n");
233	} else {
234	    if (config->Attributes1 & IO_SHARED)
235		printf(" [shared]");
236	    if (config->Attributes1 & IO_FORCE_ALIAS_ACCESS)
237		printf(" [alias]");
238	    switch (config->Attributes1 & IO_DATA_PATH_WIDTH) {
239	    case IO_DATA_PATH_WIDTH_8:
240		printf(" [8bit]\n"); break;
241	    case IO_DATA_PATH_WIDTH_16:
242		printf(" [16bit]\n"); break;
243	    case IO_DATA_PATH_WIDTH_AUTO:
244		printf(" [auto]\n"); break;
245	    }
246	}
247    }
248    if (config->NumPorts2 > 0) {
249	printf("    io %#06x-%#06x", config->BasePort2,
250	       config->BasePort2 + config->NumPorts2 - 1);
251	if (config->Attributes2 & IO_SHARED)
252	    printf(" [shared]");
253	if (config->Attributes2 & IO_FORCE_ALIAS_ACCESS)
254	    printf(" [alias]");
255	switch (config->Attributes2 & IO_DATA_PATH_WIDTH) {
256	case IO_DATA_PATH_WIDTH_8:
257	    printf(" [8bit]\n"); break;
258	case IO_DATA_PATH_WIDTH_16:
259	    printf(" [16bit]\n"); break;
260	case IO_DATA_PATH_WIDTH_AUTO:
261	    printf(" [auto]\n"); break;
262	}
263    }
264} /* print_config */
265
266/*====================================================================*/
267
268static void print_windows(int fd)
269{
270    ds_ioctl_arg_t arg1, arg2;
271    int ret;
272    win_req_t *win = &arg1.win_info.window;
273    memreq_t *req = &arg2.win_info.map;
274
275    ret = ioctl(fd, DS_GET_FIRST_WINDOW, &arg1);
276    while (ret == 0) {
277	arg2.win_info.handle = arg1.win_info.handle;
278	ioctl(fd, DS_GET_MEM_PAGE, &arg2);
279	printf("  memory 0x%04x-0x%04x @ 0x%08lx",
280	       req->CardOffset, req->CardOffset+win->Size-1,
281	       win->Base);
282	if (win->Attributes & WIN_MEMORY_TYPE_AM)
283	    printf(" [attr]");
284	if (!(win->Attributes & WIN_ENABLE))
285	    printf(" [disabled]");
286	if (win->Attributes & WIN_USE_WAIT)
287	    printf(" [wait]");
288	switch (win->Attributes & WIN_DATA_WIDTH) {
289	case WIN_DATA_WIDTH_8:
290	    printf(" [8bit]\n"); break;
291	case WIN_DATA_WIDTH_16:
292	    printf(" [16bit]\n"); break;
293	case WIN_DATA_WIDTH_32:
294	    printf(" [32bit]\n"); break;
295	}
296	ret = ioctl(fd, DS_GET_NEXT_WINDOW, &arg1);
297    }
298}
299
300/*====================================================================*/
301
302static int get_tuple(int fd, cisdata_t code, ds_ioctl_arg_t *arg)
303{
304    arg->tuple.DesiredTuple = code;
305    arg->tuple.Attributes = TUPLE_RETURN_COMMON;
306    arg->tuple.TupleOffset = 0;
307    if ((ioctl(fd, DS_GET_FIRST_TUPLE, arg) == 0) &&
308	(ioctl(fd, DS_GET_TUPLE_DATA, arg) == 0) &&
309	(ioctl(fd, DS_PARSE_TUPLE, arg) == 0))
310	return 0;
311    else
312	return -1;
313}
314
315static void print_ident(int fd)
316{
317    ds_ioctl_arg_t arg;
318    cistpl_vers_1_t *vers = &arg.tuple_parse.parse.version_1;
319    cistpl_manfid_t *manfid = &arg.tuple_parse.parse.manfid;
320    cistpl_funcid_t *funcid = &arg.tuple_parse.parse.funcid;
321    config_info_t config;
322    int i;
323    static char *fn[] = {
324	"multifunction", "memory", "serial", "parallel",
325	"fixed disk", "video", "network", "AIMS", "SCSI"
326    };
327
328    if (get_tuple(fd, CISTPL_VERS_1, &arg) == 0) {
329	printf("  product info: ");
330	for (i = 0; i < vers->ns; i++)
331	    printf("%s\"%s\"", (i>0) ? ", " : "",
332		   vers->str+vers->ofs[i]);
333	printf("\n");
334    } else {
335	printf("  no product info available\n");
336    }
337    if (get_tuple(fd, CISTPL_MANFID, &arg) == 0)
338	printf("  manfid: 0x%04x, 0x%04x\n",
339	       manfid->manf, manfid->card);
340    if (get_tuple(fd, CISTPL_FUNCID, &arg) == 0)
341	printf("  function: %d (%s)\n", funcid->func,
342	       fn[funcid->func]);
343    config.Function = config.ConfigBase = 0;
344    if ((ioctl(fd, DS_GET_CONFIGURATION_INFO, &config) == 0) &&
345	(config.IntType == INT_CARDBUS) && config.ConfigBase)
346	printf("  PCI id: 0x%04x, 0x%04x\n",
347	       config.ConfigBase & 0xffff,
348	       config.ConfigBase >> 16);
349}
350
351static void print_info(int fd)
352{
353    ds_ioctl_arg_t arg;
354    cistpl_vers_1_t *vers = &arg.tuple_parse.parse.version_1;
355    cistpl_manfid_t *manfid = &arg.tuple_parse.parse.manfid;
356    cistpl_funcid_t *funcid = &arg.tuple_parse.parse.funcid;
357    config_info_t config;
358    int i;
359
360    vers->ns = 0;
361    get_tuple(fd, CISTPL_VERS_1, &arg);
362    for (i = 0; i < 4; i++)
363	printf("PRODID_%d=\"%s\"\n", i+1,
364	       (i < vers->ns) ? vers->str+vers->ofs[i] : "");
365    *manfid = (cistpl_manfid_t) { 0, 0 };
366    get_tuple(fd, CISTPL_MANFID, &arg);
367    printf("MANFID=%04x,%04x\n", manfid->manf, manfid->card);
368    *funcid = (cistpl_funcid_t) { 0xff, 0xff };
369    get_tuple(fd, CISTPL_FUNCID, &arg);
370    printf("FUNCID=%d\n", funcid->func);
371    config.Function = config.ConfigBase = 0;
372}
373
374/*====================================================================*/
375
376typedef enum cmd_t {
377    C_STATUS, C_CONFIG, C_IDENT, C_INFO, C_SUSPEND,
378    C_RESUME, C_RESET, C_EJECT, C_INSERT
379} cmd_t;
380
381static char *cmdname[] = {
382    "status", "config", "ident", "info", "suspend",
383    "resume", "reset", "eject", "insert"
384};
385
386#define NCMD (sizeof(cmdname)/sizeof(char *))
387
388static int do_cmd(int fd, int cmd)
389{
390    int i, ret;
391    cs_status_t status;
392    config_info_t config;
393
394    ret = 0;
395    switch (cmd) {
396
397    case C_STATUS:
398	for (i = 0; i < 4; i++) {
399	    status.Function = i;
400	    if (ioctl(fd, DS_GET_STATUS, &status) == 0)
401		print_status(&status);
402	    else {
403		if (i == 0) {
404		    if (errno == ENODEV)
405			printf("  no card\n");
406		    else
407			perror("ioctl()");
408		}
409		break;
410	    }
411	}
412	break;
413
414    case C_CONFIG:
415	for (i = 0; i < 4; i++) {
416	    config.Function = i;
417	    if (ioctl(fd, DS_GET_CONFIGURATION_INFO, &config) == 0)
418		print_config(&config);
419	    else {
420		if (i == 0) printf("  not configured\n");
421		break;
422	    }
423	    print_windows(fd);
424	}
425	break;
426
427    case C_IDENT:
428	print_ident(fd);
429	break;
430
431    case C_INFO:
432	print_info(fd);
433	break;
434
435    case C_SUSPEND:
436	ret = ioctl(fd, DS_SUSPEND_CARD);
437	break;
438
439    case C_RESUME:
440	ret = ioctl(fd, DS_RESUME_CARD);
441	break;
442
443    case C_RESET:
444	ret = ioctl(fd, DS_RESET_CARD);
445	break;
446
447    case C_EJECT:
448	ret = ioctl(fd, DS_EJECT_CARD);
449	break;
450
451    case C_INSERT:
452	ret = ioctl(fd, DS_INSERT_CARD);
453	break;
454    }
455    return ret;
456}
457
458/*======================================================================
459
460    A utility function to scan /var/run/stab and apply a specified action
461    to each device, in turn.  If any command returns a non-zero exit
462    code, execute() returns -1.
463
464======================================================================*/
465
466typedef struct stab_t {
467    int		socket, instance, status;
468    char	class[33], driver[33], dev[33];
469} stab_t;
470
471static stab_t stab[256];
472static int nstab;
473
474static int fetch_stab(void)
475{
476    char s[133];
477    FILE *f;
478
479    f = fopen(stabfile, "r");
480    if (f == NULL)
481	return -1;
482    for (nstab = 0; fgets(s, 132, f); ) {
483	if (s[0] != 'S') {
484	    sscanf(s, "%d\t%s\t%s\t%d\t%s",
485		   &stab[nstab].socket, stab[nstab].class,
486		   stab[nstab].driver, &stab[nstab].instance,
487		   stab[nstab].dev);
488	    stab[nstab].status = 0;
489	    nstab++;
490	}
491    }
492    fclose(f);
493    return 0;
494}
495
496#if (!defined(__BEOS__) && !defined(__HAIKU__))
497static void eprintf(char *name, char *fmt, ...)
498{
499    va_list args;
500    char s[32];
501    va_start(args, fmt);
502    vsprintf(s, fmt, args);
503    setenv(name, s, 1);
504    va_end(args);
505}
506#endif
507
508static int execute(stab_t *s, char *action, char *scheme)
509{
510    int ret;
511    char cmd[133];
512
513#if (!defined(__BEOS__) && !defined(__HAIKU__))
514    eprintf("SOCKET", "%d", s->socket);
515    eprintf("INSTANCE", "%d", s->instance);
516#endif
517    if (scheme)
518	sprintf(cmd, "./%s %s %s %s", s->class, action, s->dev, scheme);
519    else
520	sprintf(cmd, "./%s %s %s", s->class, action, s->dev);
521    ret = system(cmd);
522    if (!WIFEXITED(ret) || WEXITSTATUS(ret))
523	return -1;
524    return 0;
525}
526
527static int stop_scheme(char *new)
528{
529    int i;
530
531    fprintf(stderr, "checking:");
532    for (i = 0; i < nstab; i++) {
533	fprintf(stderr, " %s", stab[i].dev);
534	stab[i].status = execute(stab+i, "cksum", new);
535	if (stab[i].status &&
536	    (execute(stab+i, "check", NULL) != 0)) break;
537    }
538    fprintf(stderr, "\n");
539    if (i < nstab) {
540	fprintf(stderr, "Device '%s' busy: scheme unchanged.\n",
541		stab[i].dev);
542	return -1;
543    }
544    for (i = 0; i < nstab; i++)
545	if (stab[i].status) execute(stab+i, "stop", NULL);
546    return 0;
547}
548
549static int start_scheme(void)
550{
551    int i, j = 0;
552
553    for (i = 0; i < nstab; i++)
554	if (stab[i].status) j |= execute(stab+i, "start", NULL);
555    return j;
556}
557
558/*======================================================================
559
560    do_scheme() is in charge of checking and updating the current
561    PCMCIA configuration scheme.  The current scheme is kept in a
562    file, /var/run/pcmcia-scheme.  When updating the scheme, we first
563    stop all PCMCIA devices, then update the scheme, then restart.
564
565======================================================================*/
566
567static int do_scheme(char *new)
568{
569    FILE *f;
570    char old[33];
571    int i;
572
573    f = fopen(scheme, "r");
574    if (f && fgets(old, 32, f))
575	old[strlen(old)-1] = '\0';
576    else
577	old[0] = '\0';
578    if (f) fclose(f);
579
580    if (new) {
581
582#ifndef UNSAFE_TOOLS
583	if (getuid() != 0) {
584	    fprintf(stderr, "Only root can select a new scheme.\n");
585	    return -1;
586	}
587#else
588	setuid(geteuid());
589#endif
590
591	/* Sanity checks... */
592	for (i = 0; i < strlen(new); i++)
593	    if (!isalnum(new[i])) break;
594	if ((i != strlen(new)) || (strlen(new) < 1) ||
595	    (strlen(new) > 32)) {
596	    fprintf(stderr, "Bad scheme name.\n");
597	    return -1;
598	}
599	if (strcmp(old, new) == 0) {
600	    fprintf(stderr, "Scheme unchanged.\n");
601	    return 0;
602	}
603
604	if (chdir(configpath) != 0) {
605	    fprintf(stderr, "Could not change to %s.\n", configpath);
606	    return -1;
607	}
608
609	/* Shut down devices in old scheme */
610	if ((fetch_stab() == 0) && (stop_scheme(new) != 0))
611	    return -1;
612
613	/* Update scheme state */
614	if (old[0])
615	    printf("Changing scheme from '%s' to '%s'...\n", old, new);
616	else
617	    printf("Changing scheme to '%s'...\n", new);
618
619	umask(022);
620	f = fopen(scheme, "w");
621	if (f) {
622	    fprintf(f, "%s\n", new);
623	    fclose(f);
624	} else
625	    perror("Could not set scheme.");
626
627	/* Start up devices in new scheme */
628	if (start_scheme() != 0)
629	    fprintf(stderr, "Some devices did not start cleanly.\n");
630
631    } else {
632	if (old[0])
633	    printf("Current scheme: '%s'.\n", old);
634	else
635	    printf("Current scheme: 'default'.\n");
636    }
637    return 0;
638}
639
640/*====================================================================*/
641
642static void usage(char *name)
643{
644    int i;
645    fprintf(stderr, "usage: %s command [socket #]\n", name);
646    fprintf(stderr, "    or %s [-c configpath] [-f scheme]"
647	    " [-s stab] scheme [name]\n", name);
648    fprintf(stderr, "    commands:");
649    for (i = 0; i < NCMD; i++)
650	fprintf(stderr, " %s", cmdname[i]);
651    fprintf(stderr, "\n");
652    exit(EXIT_FAILURE);
653}
654
655/*====================================================================*/
656
657#define MAX_SOCKS 8
658
659int main(int argc, char *argv[])
660{
661    int cmd, fd[MAX_SOCKS], ns, ret, i;
662    int optch, errflg = 0;
663    char *s, *opts = (getuid() == 0) ? "Vc:f:s:" : "V";
664
665    if (access("/var/lib/pcmcia", R_OK) == 0) {
666	scheme = "/var/lib/pcmcia/scheme";
667	stabfile = "/var/lib/pcmcia/stab";
668    } else {
669	scheme = "/var/run/pcmcia-scheme";
670	stabfile = "/var/run/stab";
671    }
672
673    while ((optch = getopt(argc, argv, opts)) != -1) {
674	switch (optch) {
675	case 'V':
676	    fprintf(stderr, "cardctl version " CS_PKG_RELEASE "\n");
677	    return 0;
678	    break;
679	case 'c':
680	    configpath = strdup(optarg); break;
681	case 'f':
682	    scheme = strdup(optarg); break;
683	case 's':
684	    stabfile = strdup(optarg); break;
685	default:
686	    errflg = 1; break;
687	}
688    }
689
690    if (errflg || (argc == optind) || (argc > optind+2))
691	usage(argv[0]);
692
693    if (geteuid() != 0) {
694        fprintf(stderr, "cardctl must be setuid root\n");
695	exit(EXIT_FAILURE);
696    }
697
698#if (!defined(__BEOS__) && !defined(__HAIKU__))
699    major = lookup_dev("pcmcia");
700    if (major < 0) {
701	if (major == -ENODEV)
702	    fprintf(stderr, "no pcmcia driver in /proc/devices\n");
703	else
704	    perror("could not open /proc/devices");
705	exit(EXIT_FAILURE);
706    }
707#endif
708
709    if (strcmp(argv[optind], "scheme") == 0) {
710#ifndef UNSAFE_TOOLS
711	setuid(getuid());
712#endif
713	if (do_scheme((argc == optind+1) ? NULL : argv[optind+1]) == 0)
714	    exit(EXIT_SUCCESS);
715	else
716	    exit(EXIT_FAILURE);
717    }
718
719    for (cmd = 0; cmd < NCMD; cmd++)
720	if (strcmp(argv[optind], cmdname[cmd]) == 0) break;
721    if (cmd == NCMD)
722	usage(argv[0]);
723
724    ret = 0;
725    if (argc == optind+2) {
726	ns = strtol(argv[optind+1], &s, 0);
727	if ((*argv[optind+1] == '\0') || (*s != '\0'))
728	    usage(argv[0]);
729	fd[0] = open_sock(ns);
730	if (fd[0] < 0) {
731	    perror("open_sock()");
732	    exit(EXIT_FAILURE);
733	}
734#ifndef UNSAFE_TOOLS
735	setuid(getuid());
736#endif
737	ret = do_cmd(fd[0], cmd);
738	if (ret != 0)
739	    perror("ioctl()");
740    } else {
741	for (ns = 0; ns < MAX_SOCKS; ns++) {
742	    fd[ns] = open_sock(ns);
743	    if (fd[ns] < 0) break;
744	}
745#ifndef UNSAFE_TOOLS
746	setuid(getuid());
747#endif
748	if (ns == 0) {
749	    perror("open_sock()");
750	    exit(EXIT_FAILURE);
751	}
752	for (ns = 0; (ns < MAX_SOCKS) && (fd[ns] >= 0); ns++) {
753	    if (cmd <= C_IDENT)
754		printf("Socket %d:\n", ns);
755	    i = do_cmd(fd[ns], cmd);
756	    if ((i != 0) && (errno != ENODEV)) {
757		perror("ioctl()");
758		ret = i;
759	    }
760	}
761    }
762    if (ret != 0)
763	exit(EXIT_FAILURE);
764    return 0;
765}
766