1 /* $OpenBSD: usbhidaction.c,v 1.15 2011/03/07 14:59:06 jasper Exp $ */ 2 /* $NetBSD: usbhidaction.c,v 1.7 2002/01/18 14:38:59 augustss Exp $ */ 3 4 /* 5 * Copyright (c) 2000, 2002 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Lennart Augustsson <lennart@augustsson.net>. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <ctype.h> 37 #include <err.h> 38 #include <fcntl.h> 39 #include <limits.h> 40 #include <unistd.h> 41 #include <sys/types.h> 42 #include <sys/ioctl.h> 43 #include <dev/usb/usb.h> 44 #include <dev/usb/usbhid.h> 45 #include <usbhid.h> 46 #include <util.h> 47 #include <syslog.h> 48 #include <signal.h> 49 #include <paths.h> 50 51 int verbose = 0; 52 int isdemon = 0; 53 54 volatile sig_atomic_t reparse = 0; 55 56 struct command { 57 struct command *next; 58 int line; 59 60 struct hid_item item; 61 int value; 62 char anyvalue; 63 char *name; 64 char *action; 65 }; 66 struct command *commands; 67 68 #define SIZE 4000 69 70 void usage(void); 71 struct command *parse_conf(const char *, report_desc_t, int, int); 72 void docmd(struct command *, int, const char *, int, char **); 73 void freecommands(struct command *); 74 75 /* ARGSUSED */ 76 static void 77 sighup(int signo) 78 { 79 reparse = 1; 80 } 81 82 int 83 main(int argc, char **argv) 84 { 85 const char *conf = NULL; 86 const char *dev = NULL; 87 int fd, ch, sz, n, val, i; 88 int demon, ignore; 89 report_desc_t repd; 90 char buf[100]; 91 char devnamebuf[PATH_MAX]; 92 struct command *cmd; 93 int reportid; 94 95 demon = 1; 96 ignore = 0; 97 while ((ch = getopt(argc, argv, "c:df:iv")) != -1) { 98 switch(ch) { 99 case 'c': 100 conf = optarg; 101 break; 102 case 'd': 103 demon ^= 1; 104 break; 105 case 'i': 106 ignore++; 107 break; 108 case 'f': 109 dev = optarg; 110 break; 111 case 'v': 112 demon = 0; 113 verbose++; 114 break; 115 case '?': 116 default: 117 usage(); 118 } 119 } 120 argc -= optind; 121 argv += optind; 122 123 if (conf == NULL || dev == NULL) 124 usage(); 125 126 if (hid_start(NULL) == -1) 127 errx(1, "hid_init"); 128 129 if (dev[0] != '/') { 130 snprintf(devnamebuf, sizeof(devnamebuf), "/dev/%s%s", 131 isdigit(dev[0]) ? "uhid" : "", dev); 132 dev = devnamebuf; 133 } 134 135 if (demon && conf[0] != '/') 136 errx(1, "config file must have an absolute path, %s", conf); 137 138 fd = open(dev, O_RDWR); 139 if (fd < 0) 140 err(1, "%s", dev); 141 142 /* Avoid passing the device file descriptor to executed commands */ 143 if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 144 err(1, "fcntl(F_SETFD, FD_CLOEXEC)"); 145 146 if (ioctl(fd, USB_GET_REPORT_ID, &reportid) < 0) 147 reportid = -1; 148 repd = hid_get_report_desc(fd); 149 if (repd == NULL) 150 err(1, "hid_get_report_desc() failed"); 151 152 commands = parse_conf(conf, repd, reportid, ignore); 153 154 sz = hid_report_size(repd, hid_input, reportid); 155 156 if (verbose) 157 printf("report size %d\n", sz); 158 if (sz > sizeof buf) 159 errx(1, "report too large"); 160 161 (void)signal(SIGHUP, sighup); 162 163 if (demon) { 164 if (daemon(0, 0) < 0) 165 err(1, "daemon()"); 166 pidfile(NULL); 167 isdemon = 1; 168 } 169 170 for(;;) { 171 n = read(fd, buf, sz); 172 if (verbose > 2) { 173 printf("read %d bytes:", n); 174 for (i = 0; i < n; i++) 175 printf(" %02x", buf[i]); 176 printf("\n"); 177 } 178 if (n < 0) { 179 if (verbose) 180 err(1, "read"); 181 else 182 exit(1); 183 } 184 #if 0 185 if (n != sz) { 186 err(2, "read size"); 187 } 188 #endif 189 for (cmd = commands; cmd; cmd = cmd->next) { 190 val = hid_get_data(buf, &cmd->item); 191 if (cmd->value == val || cmd->anyvalue) 192 docmd(cmd, val, dev, argc, argv); 193 } 194 if (reparse) { 195 struct command *cmds = 196 parse_conf(conf, repd, reportid, ignore); 197 if (cmds) { 198 freecommands(commands); 199 commands = cmds; 200 } 201 reparse = 0; 202 } 203 } 204 205 exit(0); 206 } 207 208 void 209 usage(void) 210 { 211 extern char *__progname; 212 213 fprintf(stderr, "usage: %s [-div] -c config-file -f device arg ...\n", 214 __progname); 215 exit(1); 216 } 217 218 static int 219 peek(FILE *f) 220 { 221 int c; 222 223 c = getc(f); 224 if (c != EOF) 225 ungetc(c, f); 226 return c; 227 } 228 229 struct command * 230 parse_conf(const char *conf, report_desc_t repd, int reportid, int ignore) 231 { 232 FILE *f; 233 char *p; 234 int line; 235 char buf[SIZE], name[SIZE], value[SIZE], action[SIZE]; 236 char usage[SIZE], coll[SIZE]; 237 struct command *cmd, *cmds; 238 struct hid_data *d; 239 struct hid_item h; 240 int u, lo, hi, range; 241 242 f = fopen(conf, "r"); 243 if (f == NULL) 244 err(1, "%s", conf); 245 246 cmds = NULL; 247 for (line = 1; ; line++) { 248 if (fgets(buf, sizeof buf, f) == NULL) 249 break; 250 if (buf[0] == '#' || buf[0] == '\n') 251 continue; 252 p = strchr(buf, '\n'); 253 while (p && isspace(peek(f))) { 254 if (fgets(p, sizeof buf - strlen(buf), f) == NULL) 255 break; 256 p = strchr(buf, '\n'); 257 } 258 if (p) 259 *p = 0; 260 if (sscanf(buf, "%s %s %[^\n]", name, value, action) != 3) { 261 if (isdemon) { 262 syslog(LOG_WARNING, "config file `%s', line %d" 263 ", syntax error: %s", conf, line, buf); 264 freecommands(cmds); 265 fclose(f); 266 return (NULL); 267 } else { 268 errx(1, "config file `%s', line %d" 269 ", syntax error: %s", conf, line, buf); 270 } 271 } 272 273 cmd = malloc(sizeof *cmd); 274 if (cmd == NULL) 275 err(1, "malloc failed"); 276 cmd->next = cmds; 277 cmds = cmd; 278 cmd->line = line; 279 280 if (strcmp(value, "*") == 0) { 281 cmd->anyvalue = 1; 282 } else { 283 cmd->anyvalue = 0; 284 if (sscanf(value, "%d", &cmd->value) != 1) { 285 if (isdemon) { 286 syslog(LOG_WARNING, 287 "config file `%s', line %d, " 288 "bad value: %s", 289 conf, line, value); 290 freecommands(cmds); 291 fclose(f); 292 return (NULL); 293 } else { 294 errx(1, "config file `%s', line %d, " 295 "bad value: %s", 296 conf, line, value); 297 } 298 } 299 } 300 301 coll[0] = 0; 302 d = hid_start_parse(repd, 1 << hid_input, reportid); 303 if (d == NULL) 304 err(1, "hid_start_parse failed"); 305 while (hid_get_item(d, &h)) { 306 if (verbose > 2) 307 printf("kind=%d usage=%x\n", h.kind, h.usage); 308 if (h.flags & HIO_CONST) 309 continue; 310 switch (h.kind) { 311 case hid_input: 312 if (h.usage_minimum != 0 || 313 h.usage_maximum != 0) { 314 lo = h.usage_minimum; 315 hi = h.usage_maximum; 316 range = 1; 317 } else { 318 lo = h.usage; 319 hi = h.usage; 320 range = 0; 321 } 322 for (u = lo; u <= hi; u++) { 323 snprintf(usage, sizeof usage, "%s:%s", 324 hid_usage_page(HID_PAGE(u)), 325 hid_usage_in_page(u)); 326 if (verbose > 2) 327 printf("usage %s\n", usage); 328 if (!strcasecmp(usage, name)) 329 goto foundhid; 330 if (coll[0]) { 331 snprintf(usage, sizeof usage, 332 "%s.%s:%s", coll+1, 333 hid_usage_page(HID_PAGE(u)), 334 hid_usage_in_page(u)); 335 if (verbose > 2) 336 printf("usage %s\n", 337 usage); 338 if (!strcasecmp(usage, name)) 339 goto foundhid; 340 } 341 } 342 break; 343 case hid_collection: 344 snprintf(coll + strlen(coll), 345 sizeof coll - strlen(coll), ".%s:%s", 346 hid_usage_page(HID_PAGE(h.usage)), 347 hid_usage_in_page(h.usage)); 348 break; 349 case hid_endcollection: 350 if (coll[0]) 351 *strrchr(coll, '.') = 0; 352 break; 353 default: 354 break; 355 } 356 } 357 hid_end_parse(d); 358 if (ignore) { 359 if (verbose) 360 warnx("ignore item '%s'", name); 361 /* pop and free this ignored item */ 362 cmds = cmd->next; 363 free(cmd); 364 continue; 365 } 366 if (isdemon) { 367 syslog(LOG_WARNING, "config file `%s', line %d, HID " 368 "item not found: `%s'", conf, line, name); 369 freecommands(cmds); 370 fclose(f); 371 return (NULL); 372 } else { 373 errx(1, "config file `%s', line %d, HID item " 374 "not found: `%s'", conf, line, name); 375 } 376 377 foundhid: 378 hid_end_parse(d); 379 cmd->item = h; 380 cmd->name = strdup(name); 381 cmd->action = strdup(action); 382 if (range) { 383 if (cmd->value == 1) 384 cmd->value = u - lo; 385 else 386 cmd->value = -1; 387 } 388 389 if (verbose) 390 printf("PARSE:%d %s, %d, '%s'\n", cmd->line, name, 391 cmd->value, cmd->action); 392 } 393 fclose(f); 394 return (cmds); 395 } 396 397 void 398 docmd(struct command *cmd, int value, const char *hid, int argc, char **argv) 399 { 400 char cmdbuf[SIZE], *p, *q; 401 size_t len; 402 int n, r; 403 pid_t pid; 404 405 if (cmd->action == NULL) { 406 if (verbose) 407 printf("no action for device %s value %d\n", 408 hid, value); 409 return; 410 } 411 for (p = cmd->action, q = cmdbuf; *p && q < &cmdbuf[SIZE-1]; ) { 412 if (*p == '$') { 413 p++; 414 len = &cmdbuf[SIZE-1] - q; 415 if (isdigit(*p)) { 416 n = strtol(p, &p, 10) - 1; 417 if (n >= 0 && n < argc) { 418 strncpy(q, argv[n], len); 419 q += strlen(q); 420 } 421 } else if (*p == 'V') { 422 p++; 423 snprintf(q, len, "%d", value); 424 q += strlen(q); 425 } else if (*p == 'N') { 426 p++; 427 strncpy(q, cmd->name, len); 428 q += strlen(q); 429 } else if (*p == 'H') { 430 p++; 431 strncpy(q, hid, len); 432 q += strlen(q); 433 } else if (*p) { 434 *q++ = *p++; 435 } 436 } else { 437 *q++ = *p++; 438 } 439 } 440 *q = 0; 441 442 pid = fork(); 443 if (pid == -1) 444 warn("fork failed"); 445 else if (pid == 0) { 446 setpgid(0, 0); 447 if (verbose) 448 printf("executing '%s'\n", cmdbuf); 449 r = execl(_PATH_BSHELL, "sh", "-c", cmdbuf, NULL); 450 err(1, "execl"); 451 } 452 } 453 454 void 455 freecommands(struct command *cmd) 456 { 457 struct command *next; 458 459 while (cmd) { 460 next = cmd->next; 461 free(cmd); 462 cmd = next; 463 } 464 } 465