1
2 /*
3 *
4 * br (BottleRocket)
5 *
6 * Control software for the X10(R) FireCracker wireless computer
7 * interface kit.
8 *
9 * (c) 1999 Ashley Clark (aclark@ghoti.org) and Tymm Twillman (tymm@acm.org)
10 * Free Software. LGPL applies.
11 * No warranties expressed or implied.
12 *
13 * Have fun with it and if you do anything really cool, send an email and let us
14 * know.
15 *
16 */
17
18 #define VERSION "0.04c"
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <unistd.h>
25 #include <sys/ioctl.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <stdlib.h>
31 #include <ctype.h>
32 #include <limits.h>
33 #include <string.h>
34
35 #ifdef HAVE_FEATURES_H
36 #include <features.h>
37 #endif
38
39 #ifdef HAVE_ERRNO_H
40 #include <errno.h>
41 #endif
42
43 #include "br_cmd.h"
44
45 #ifdef HAVE_ISSETUGID
46
47 /*
48 * Thanks to Warner Losh for info on how to do this better
49 */
50
51 #define ISSETID() (issetugid())
52 #else
53 #define ISSETID() (getuid() != geteuid() || getgid() != getegid())
54 #endif
55
56 #ifdef HAVE_GETOPT_LONG
57 #include <getopt.h>
58 #endif
59
60 #define DIMRANGE 12
61 #define MAX_COMMANDS 512
62
63 #define HOUSENAME(house) (((house < 0) || (house > 15)) ? \
64 '?':"ABCDEFGHIJKLMNOP"[house])
65 #define DEVNAME(dev) (((dev < 0) || (dev > 16)) ? 0 : dev + 1)
66 #define CINFO_CLR(cinfo) memset(cinfo, 0, sizeof(br_control_info))
67 #define SAFE_FILENO(fd) ((fd != STDIN_FILENO) && (fd != STDOUT_FILENO) \
68 && (fd != STDERR_FILENO))
69
70 /*
71 * Could have device info/commands dynamically allocated, but that's too much
72 * trouble, and even this allows for really really obnoxious command lines.
73 */
74
75 typedef struct {
76 int inverse;
77 int repeat;
78 char *port;
79 int fd;
80 int numcmds;
81 int devs[MAX_COMMANDS];
82 char houses[MAX_COMMANDS];
83 int dimlevels[MAX_COMMANDS];
84 int cmds[MAX_COMMANDS];
85 } br_control_info;
86
87 int Verbose = 0;
88 char *MyName = "br";
89
usage()90 void usage()
91 {
92 fprintf(stderr, "BottleRocket version %s\n", VERSION);
93 fprintf(stderr, "\n");
94 fprintf(stderr, "Usage: %s [<options>][<housecode>(<list>) "
95 "<native command> ...]\n\n", MyName);
96 fprintf(stderr, " Options:\n");
97 #ifdef HAVE_GETOPT_LONG
98 fprintf(stderr, " -v, --verbose\t\t\tadd v's to increase verbosity\n");
99 fprintf(stderr, " -x, --port=PORT\t\tset port to use\n");
100 fprintf(stderr, " -c, --house=[A-P]\t\tuse alternate house code "
101 "(default \"A\")\n");
102 fprintf(stderr, " -n, --on=LIST\t\t\tturn on devices in LIST\n");
103 fprintf(stderr, " -f, --off=LIST\t\tturn off devices in LIST\n");
104 fprintf(stderr, " -N, --ON\t\t\tturn on all devices in housecode\n");
105 fprintf(stderr, " -F, --OFF\t\t\tturn off all devices in housecode\n");
106 fprintf(stderr, " -d, --dim=LEVEL[,LIST]\tdim devices in housecode to "
107 " relative LEVEL\n");
108 fprintf(stderr, " -B, --lamps_on\t\tturn all lamps in housecode on\n");
109 fprintf(stderr, " -D, --lamps_off\t\tturn all lamps in housecode off\n");
110 fprintf(stderr, " -r, --repeat=NUM\t\trepeat commands NUM times "
111 "(0 = ~ forever)\n");
112 fprintf(stderr, " -h, --help\t\t\tthis help\n\n");
113 #else
114 fprintf(stderr, " -v\tverbose (add v's to increase verbosity)\n");
115 fprintf(stderr, " -x\tset port to use\n");
116 fprintf(stderr, " -c\tuse alternate house code (default \"A\")\n");
117 fprintf(stderr, " -n\tturn on devices\n");
118 fprintf(stderr, " -f\tturn off devices\n");
119 fprintf(stderr, " -N\tturn all devices in housecode on\n");
120 fprintf(stderr, " -F\tturn all devices in housecode off\n");
121 fprintf(stderr, " -d\tdim devices in housecode to relative dimlevel\n");
122 fprintf(stderr, " -B\tturn all lamps in housecode on\n");
123 fprintf(stderr, " -D\tturn all lamps in housecode off\n");
124 fprintf(stderr, " -r\trepeat commands <repeats> times (0 basically "
125 "means don't stop)\n");
126 fprintf(stderr, " -h\tthis help\n\n");
127 #endif
128 fprintf(stderr, "<list>\t\tis a comma separated list of devices "
129 "(no spaces),\n");
130 fprintf(stderr, "\t\teach ranging from 1 to 16\n");
131 fprintf(stderr, "<dimlevel>\tis an integer from %d to %d "
132 "(0 means no change)\n", -DIMRANGE, DIMRANGE);
133 fprintf(stderr, "<housecode>\tis a letter between A and P\n");
134 fprintf(stderr, "<native cmd>\tis one of ON, OFF, DIM, BRIGHT, "
135 "ALL_ON, ALL_OFF,\n");
136 fprintf(stderr, "\t\tLAMPS_ON or LAMPS_OFF\n\n");
137 fprintf(stderr, "For native commands, <list> should only be specified "
138 "for ON or OFF.\n\n");
139
140 }
141
142
checkimmutableport(char * port_source)143 int checkimmutableport(char *port_source)
144 {
145 /*
146 * Check to see if the user is allowed to specify an alternate serial port
147 */
148
149 if (!ISSETID())
150 return 0;
151
152 fprintf(stderr, "%s: You are not authorized to change the X10 port!\n",
153 MyName);
154 fprintf(stderr, "%s: Invalid port assignment %s.\n", MyName, port_source);
155
156 errno = EINVAL;
157
158 return -1;
159 }
160
161
gethouse(char * house)162 int gethouse(char *house)
163 {
164 /*
165 * Grab a house code from the command line
166 */
167
168 int c;
169
170
171 c = toupper(house[0]) - 'A';
172 if ((strlen(house) > 1) || (c < 0) || (c > 15)) {
173 fprintf(stderr, "%s: House code must be in range [A-P]\n", MyName);
174 errno = EINVAL;
175 return -1;
176 }
177
178 return c;
179 }
180
181
getdim(char * list,int * dim)182 int getdim(char *list, int *dim)
183 {
184 /*
185 * Get devices that should be dimmed from the command line, and how
186 * much to dim them
187 */
188
189 char *end;
190 int dev;
191 int devs = 0;
192
193 *dim = strtol(list, &end, 0);
194
195 /*
196 * May have more dimlevels when I get a chance to play with variable
197 * dimming
198 */
199
200 if (((*end != '\0') && (*end != ','))
201 || (*dim < -DIMRANGE)
202 || (*dim > DIMRANGE))
203 {
204 fprintf(stderr, "%s: For dimming either specify just a dim level or "
205 "a comma\n",MyName);
206 fprintf(stderr, "separated list containing the dim level and the "
207 "devices to dim.\n");
208 fprintf(stderr, "%s: Valid dimlevels are numbers between %d and %d.\n",
209 MyName, -DIMRANGE, DIMRANGE);
210 errno = EINVAL;
211 return -1;
212 }
213
214 list = end;
215
216 while (*list++) {
217 dev = strtol(list, &end, 0);
218
219 if ((dev > 16)
220 || (dev < 1)
221 || ((*end != '\0') && (*end != ',')))
222 {
223 fprintf(stderr, "%s: Devices must be in the range of 1-16.\n",
224 MyName);
225 errno = EINVAL;
226 return -1;
227 }
228
229 devs |= 1 << (dev - 1);
230
231 list = end;
232 }
233
234 return devs;
235 }
236
237
getdevs(char * list)238 int getdevs(char *list)
239 {
240 /*
241 * Get a list of devices for an operation to be performed on from
242 * the command line
243 */
244
245 int devs = 0;
246 int dev;
247 char *end;
248
249
250 do {
251 dev = strtol(list, &end, 0);
252
253 if ((dev > 16)
254 || (dev < 1)
255 || ((*end != '\0') && (*end != ',')))
256 {
257 fprintf(stderr, "%s: Devices must be in the range of 1-16\n",
258 MyName);
259 errno = EINVAL;
260 return -1;
261 }
262
263 /*
264 * Set the bit corresponding to the given device
265 */
266
267 devs |= 1 << (dev - 1);
268
269 list = end;
270 } while (*list++); /* Skip the , */
271
272 return devs;
273 }
274
275
br_getunit(char * arg,int * house,int * devs)276 int br_getunit(char *arg, int *house, int *devs)
277 {
278 /*
279 * Get units to be accessed from the command line in native BottleRocket style
280 */
281
282 if (strlen(arg) < 2) {
283 errno = EINVAL;
284 return -1;
285 }
286
287 if ((*devs = getdevs(arg + 1)) < 0)
288 return -1;
289
290 *(arg + 1) = '\0';
291
292 if ((*house = gethouse(arg)) < 0)
293 return -1;
294
295 return 0;
296 }
297
298
br_native_getcmd(char * arg)299 int br_native_getcmd(char *arg)
300 {
301 /*
302 * Convert a native BottleRocket command to the appropriate token
303 */
304
305 if (!strcasecmp(arg, "ON"))
306 return ON;
307
308 if (!strcasecmp(arg, "OFF"))
309 return OFF;
310
311 if (!strcasecmp(arg, "DIM"))
312 return DIM;
313
314 if (!strcasecmp(arg, "BRIGHT"))
315 return BRIGHT;
316
317 if (!strcasecmp(arg, "ALL_ON"))
318 return ALL_ON;
319
320 if (!strcasecmp(arg, "ALL_OFF"))
321 return ALL_OFF;
322
323 if (!strcasecmp(arg, "LAMPS_ON"))
324 return ALL_LAMPS_ON;
325
326 if (!strcasecmp(arg, "LAMPS_OFF"))
327 return ALL_LAMPS_OFF;
328
329 fprintf(stderr, "%s: Command must be one of ON, OFF, DIM, BRIGHT, "
330 "ALL_ON, ALL_OFF, LAMPS_ON or LAMPS_OFF.\n", MyName);
331 errno = EINVAL;
332
333 return -1;
334 }
335
336
process_list(int fd,int house,int devs,int cmd)337 int process_list(int fd, int house, int devs, int cmd)
338 {
339 /*
340 * Process and execute on/off commands on a cluster of devices
341 */
342
343 unsigned short unit;
344 int i;
345
346 /* apply cmd to devices in list */
347
348 for (i = 0; i < 16; i++) {
349 if (devs & (1 << i)) {
350 unit = (unsigned char)((house << 4) | i);
351 if (Verbose)
352 printf("%s: Turning %s appliance %c%d\n", MyName,
353 (cmd == ON) ? "on":"off", HOUSENAME(house), DEVNAME(i));
354
355 if (x10_br_out(fd, unit, (unsigned char)cmd) < 0)
356 return -1;
357 }
358 }
359
360 return 0;
361 }
362
363
process_dim(int fd,int house,int devs,int dim)364 int process_dim(int fd, int house, int devs, int dim)
365 {
366 /*
367 * Process and execute dimming commands on a housecode or a cluster of
368 * devices
369 */
370
371 register int i;
372 int unit = (unsigned char)(house << 4);
373 int cmd = (dim < 0) ? DIM:BRIGHT;
374 int tmpdim;
375
376
377 dim = (dim < 0) ? -dim:dim;
378 tmpdim = dim;
379
380 if (!devs) {
381 if (Verbose)
382 printf("%s: %s lamps in house %c by %d.\n", MyName,
383 (cmd == BRIGHT) ? "Brightening":"Dimming", HOUSENAME(house), dim);
384 for (; tmpdim; tmpdim--) {
385 if (x10_br_out(fd, unit, (unsigned char)cmd) < 0)
386 return -1;
387 }
388 } else {
389 for (i = 0; i < 16; i++) {
390 if (devs & (1 << i)) {
391 if (Verbose)
392 printf("%s: %s lamp %c%d by %d.\n", MyName,
393 (cmd == BRIGHT) ? "Brightening":"Dimming",
394 HOUSENAME(house), DEVNAME(i), dim);
395 /* Send an ON cmd to select the device this may change later */
396 if (x10_br_out(fd, unit | i, ON) < 0)
397 return -1;
398
399 for (; tmpdim > 0; tmpdim--)
400 if (x10_br_out(fd, unit, (unsigned char)cmd) < 0)
401 return -1;
402 tmpdim = dim;
403 }
404 }
405 }
406
407 return 0;
408 }
409
open_port(br_control_info * cinfo)410 int open_port(br_control_info *cinfo)
411 {
412 /*
413 * Open the serial port that a FireCracker device is (we expect) on
414 */
415
416 int tmperrno;
417
418 if (Verbose >= 2)
419 printf("%s: Opening serial port %s.\n", MyName, cinfo->port);
420
421 /*
422 * Oh, yeah. Don't need O_RDWR for ioctls. This is safer.
423 */
424
425 if ((cinfo->fd = open(cinfo->port, O_RDONLY | O_NONBLOCK)) < 0) {
426 tmperrno = errno;
427 fprintf(stderr, "%s: Error (%s) opening %s.\n", MyName,
428 strerror(errno), cinfo->port);
429 errno = tmperrno;
430 return -1;
431 }
432
433 /*
434 * If we end up with a reserved fd, don't mess with it. Just to make sure.
435 */
436
437 if (!SAFE_FILENO(cinfo->fd)) {
438 close(cinfo->fd);
439 errno = EBADF;
440 return -1;
441 }
442
443 return 0;
444 }
445
close_port(br_control_info * cinfo)446 int close_port(br_control_info *cinfo)
447 {
448 /*
449 * Close the serial port when we're done using it
450 */
451
452 if (Verbose >= 2)
453 printf("%s: Closing serial port.\n", MyName);
454
455 close(cinfo->fd);
456
457 return 0;
458 }
459
460
addcmd(br_control_info * cinfo,int cmd,int house,int devs,int dimlevel)461 int addcmd(br_control_info *cinfo, int cmd, int house, int devs, int dimlevel)
462 {
463 /*
464 * Add a command, plus devices for it to act on and other info, to the
465 * list of commands to be executed
466 */
467
468 if (cinfo->numcmds >= MAX_COMMANDS) {
469 fprintf(stderr, "%s: Too many commands specified.\n", MyName);
470 errno = EINVAL;
471 return -1;
472 }
473
474 cinfo->cmds[cinfo->numcmds] = cmd;
475 cinfo->devs[cinfo->numcmds] = devs;
476 cinfo->dimlevels[cinfo->numcmds] = dimlevel;
477 cinfo->houses[cinfo->numcmds] = house;
478
479 cinfo->numcmds++;
480
481 return 0;
482 }
483
br_execute(br_control_info * cinfo)484 int br_execute(br_control_info *cinfo)
485 {
486 /*
487 * Run through a list of commands and execute them
488 */
489
490 register int i;
491 register int repeat = cinfo->repeat;
492 int inverse = cinfo->inverse;
493 int cmd;
494
495 if (Verbose >= 2)
496 printf("%s: Executing %d commands; repeat = %d, inverse = %d\n",
497 MyName, cinfo->numcmds, repeat, inverse);
498
499 /*
500 * Pete and Repeat go into a store, Pete comes out...
501 */
502
503 for (; repeat > 0; repeat--) {
504 for (i = 0; i < cinfo->numcmds; i++)
505 {
506 cmd = cinfo->cmds[i];
507 if ((cmd == ON) || (cmd == OFF)) {
508 cmd = (inverse >= 0) ? cmd : (cmd == OFF) ? ON:OFF;
509
510 if (process_list(cinfo->fd, cinfo->houses[i],
511 cinfo->devs[i], cmd) < 0)
512 {
513 return -1;
514 }
515 } else if ((cmd == ALL_ON) || (cmd == ALL_OFF)) {
516 cmd = (inverse >= 0) ? cmd : (cmd == ALL_OFF) ? ALL_ON:ALL_OFF;
517
518 if (x10_br_out(cinfo->fd, cinfo->houses[i] << 4, cmd) < 0)
519 return -1;
520
521 } else if ((cmd == ALL_LAMPS_ON) || (cmd == ALL_LAMPS_OFF)) {
522 cmd = (inverse >= 0) ? cmd : (cmd == ALL_LAMPS_OFF) ?
523 ALL_LAMPS_ON:ALL_LAMPS_OFF;
524
525 if (x10_br_out(cinfo->fd, cinfo->houses[i] << 4, cmd) < 0)
526 return -1;
527
528 } else if (cmd == DIM) {
529 if (process_dim(cinfo->fd, cinfo->houses[i], cinfo->devs[i],
530 (inverse >= 0) ?
531 cinfo->dimlevels[i]:-cinfo->dimlevels[i]) < 0)
532 {
533 return -1;
534 }
535 }
536 }
537
538 if (inverse) inverse = 0 - inverse;
539 }
540
541 return 0;
542 }
543
native_br(br_control_info * cinfo,int argc,char * argv[],int optind)544 int native_br(br_control_info *cinfo, int argc, char *argv[], int optind)
545 {
546 /*
547 * Get arguments from the command line in native BottleRocket style
548 */
549
550 int cmd;
551 int house = 0;
552 int devs = 0;
553 int i;
554
555 if (argc - optind < 2) {
556 usage();
557 errno = EINVAL;
558 return -1;
559 }
560
561 /*
562 * Two arguments at a time; device and command
563 */
564
565 for (i = optind; i < argc - 1; i += 2) {
566 cmd = br_native_getcmd(argv[i + 1]);
567
568 switch(cmd) {
569 case ON:
570 if (br_getunit(argv[i], &house, &devs) < 0)
571 return -1;
572 if (addcmd(cinfo, ON, house, devs, 0) < 0)
573 return -1;
574 break;
575
576 case OFF:
577 if (br_getunit(argv[i], &house, &devs) < 0)
578 return -1;
579 if (addcmd(cinfo, OFF, house, devs, 0) < 0)
580 return -1;
581 break;
582
583 case DIM:
584 if ((house = gethouse(argv[i])) < 0)
585 return -1;
586 if (addcmd(cinfo, DIM, house, 0, -1) < 0)
587 return -1;
588 break;
589
590 case BRIGHT:
591 if ((house = gethouse(argv[i])) < 0)
592 return -1;
593 if (addcmd(cinfo, DIM, house, 0, 1) < 0)
594 return -1;
595 break;
596
597 case ALL_ON:
598 if ((house = gethouse(argv[i])) < 0)
599 return -1;
600 if (addcmd(cinfo, ALL_ON, house, 0, 1) < 0)
601 return -1;
602 break;
603
604 case ALL_OFF:
605 if ((house = gethouse(argv[i])) < 0)
606 return -1;
607 if (addcmd(cinfo, ALL_OFF, house, 0, 1) < 0)
608 return -1;
609 break;
610
611 case ALL_LAMPS_ON:
612 if ((house = gethouse(argv[i])) < 0)
613 return -1;
614 if (addcmd(cinfo, ALL_LAMPS_ON, house, 0, 1) < 0)
615 return -1;
616 break;
617
618 case ALL_LAMPS_OFF:
619 if ((house = gethouse(argv[i])) < 0)
620 return -1;
621 if (addcmd(cinfo, ALL_LAMPS_OFF, house, 0, 1) < 0)
622 return -1;
623 break;
624
625 default: /* How the hell did we end up here? */
626 errno = EINVAL;
627 return -1;
628 }
629 }
630
631 if (i != argc) {
632 usage();
633 errno = EINVAL;
634 return -1;
635 }
636
637 return 0;
638 }
639
main(int argc,char ** argv)640 int main(int argc, char **argv)
641 {
642 char *port_source = "at compile time";
643 char *tmp_port;
644 int opt;
645 int house = 0;
646 int repeat;
647 int devs;
648 int dimlevel = 0;
649 br_control_info *cinfo = NULL;
650 int tmperrno;
651
652 #ifdef HAVE_GETOPT_LONG
653 int opt_index;
654 static struct option long_options[] = {
655 {"help", no_argument, 0, 'h'},
656 {"port", required_argument, 0, 'x'},
657 {"repeat", required_argument, 0, 'r'},
658 {"on", required_argument, 0, 'n'},
659 {"off", required_argument, 0, 'f'},
660 {"ON", no_argument, 0, 'N'},
661 {"OFF", no_argument, 0, 'F'},
662 {"dim", required_argument, 0, 'd'},
663 {"lamps_on", no_argument, 0, 'B'},
664 {"lamps_off", no_argument, 0, 'D'},
665 {"inverse", no_argument, 0, 'i'},
666 {"house", required_argument, 0, 'c'},
667 {"verbose", no_argument, 0, 'v'},
668 {0, 0, 0, 0}
669 };
670 #endif
671
672 #define OPT_STRING "x:hvr:ic:n:Nf:Fd:BD"
673
674 /*
675 * It's possible to do an exec without even passing argv[0];
676 * this is just to make sure we don't make any bad
677 * output decisions without accounting for that
678 */
679
680 if (argc)
681 MyName = argv[0];
682
683 if (argc < 2) {
684 usage();
685 exit(EINVAL);
686 }
687
688 if ((cinfo = malloc(sizeof(br_control_info))) == NULL) {
689 tmperrno = errno;
690 fprintf(stderr, "%s: Error (%s) allocating memory.\n", MyName,
691 strerror(errno));
692 exit(tmperrno);
693 }
694
695 CINFO_CLR(cinfo);
696 cinfo->port = X10_PORTNAME;
697 cinfo->repeat = 1;
698
699 if ((tmp_port = getenv("X10_PORTNAME"))) {
700 port_source = "in the environment variable X10_PORTNAME";
701 if (!checkimmutableport(port_source))
702 cinfo->port = tmp_port;
703 }
704
705 #ifdef HAVE_GETOPT_LONG
706 while ((opt = getopt_long(argc, argv, OPT_STRING, long_options, &opt_index)) != -1)
707 {
708 #else
709 while ((opt = getopt(argc, argv, OPT_STRING)) != -1) {
710 #endif
711 switch (opt) {
712 case 'x':
713 port_source = "on the command line";
714 if (checkimmutableport(port_source) < 0)
715 exit(errno);
716 cinfo->port = optarg;
717 break;
718 case 'r':
719 repeat = atoi(optarg);
720 if (!repeat && !isdigit(*optarg)) {
721 fprintf(stderr, "%s: Invalid repeat value specified.\n",
722 MyName);
723 exit(EINVAL);
724 }
725 cinfo->repeat = (repeat ? repeat:INT_MAX);
726 break;
727 case 'v':
728 if (Verbose++ == 10)
729 fprintf(stderr, "\nGet a LIFE already. "
730 "I've got enough v's, thanks.\n\n");
731 break;
732 case 'i':
733 cinfo->inverse++; /* no this isn't documented.
734 * your free gift for reading the source.
735 */
736 break;
737 case 'c':
738 if ((house = gethouse(optarg)) < 0)
739 exit(errno);
740 break;
741 case 'n':
742 if ((devs = getdevs(optarg)) < 0)
743 exit(errno);
744 if (addcmd(cinfo, ON, house, devs, 0) < 0)
745 exit(errno);
746 break;
747 case 'N':
748 if (addcmd(cinfo, ALL_ON, house, 0, 0) < 0)
749 exit(errno);
750 break;
751 case 'f':
752 if ((devs = getdevs(optarg)) < 0)
753 exit(errno);
754 if (addcmd(cinfo, OFF, house, devs, 0) < 0)
755 exit(errno);
756 break;
757 case 'F':
758 if (addcmd(cinfo, ALL_OFF, house, 0, 0) < 0)
759 exit(errno);
760 break;
761 case 'd':
762 if ((devs = getdim(optarg, &dimlevel)) < 0)
763 exit(errno);
764 if (addcmd(cinfo, DIM, house, devs, dimlevel) < 0)
765 exit(errno);
766 break;
767 case 'B':
768 if (addcmd(cinfo, ALL_LAMPS_ON, house, 0, 0) < 0)
769 exit(errno);
770 break;
771 case 'D':
772 if (addcmd(cinfo, ALL_LAMPS_OFF, house, 0, 0) < 0)
773 exit(errno);
774 break;
775 case 'h':
776 usage();
777 exit(0);
778 default:
779 usage();
780 exit(EINVAL);
781 }
782 }
783
784 if (argc > optind) {
785 /*
786 * Must be using the native BottleRocket command line...
787 */
788
789 if (native_br(cinfo, argc, argv, optind) < 0)
790 exit(errno);
791 }
792
793 if (open_port(cinfo) < 0)
794 exit(errno);
795
796 if (br_execute(cinfo) < 0)
797 exit(errno);
798
799 if (close_port(cinfo) < 0)
800 exit(errno);
801
802 free(cinfo);
803
804 return 0;
805 }
806