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