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