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