Added -l option to pndevmapperd, same as in pndnotifyd
[pandora-libraries.git] / apps / pndevmapperd.c
1
2 /* pndevmapperd exists to watch for a few interesting events and to launch scripts when they occur
3  * ie: when the lid closes, should invoke power-mode toggle sh-script
4  */
5
6 // woot for writing code while sick.
7
8 // this code begs a rewrite, but should work fine; its just arranged goofily
9 // -> I mean, why the racial divide between keys and other events
10
11 #include <stdio.h> /* for printf, NULL */
12 #include <stdlib.h> /* for free */
13 #include <string.h> /* for strdup */
14 #include <unistd.h>    // for exit()
15 #include <sys/types.h> // for umask
16 #include <sys/stat.h>  // for umask
17 #include <fcntl.h> // for open(2)
18 #include <errno.h> // for errno
19 #include <time.h> // for time(2)
20 #include <ctype.h> // for isdigit
21
22 #include <linux/input.h> // for keys
23 //#include "../../kernel-rip/input.h" // for keys
24
25 #include "pnd_conf.h"
26 #include "pnd_container.h"
27 #include "pnd_apps.h"
28 #include "pnd_discovery.h"
29 #include "pnd_locate.h"
30 #include "pnd_pndfiles.h"
31 #include "pnd_pxml.h"
32 #include "pnd_logger.h"
33 #include "pnd_utility.h"
34 #include "pnd_notify.h"
35
36 // daemon and logging
37 //
38 unsigned char g_daemon_mode = 0;
39 unsigned int g_minimum_separation = 1;
40
41 typedef enum {
42   pndn_debug = 0,
43   pndn_rem,          // will set default log level to here, so 'debug' is omitted
44   pndn_warning,
45   pndn_error,
46   pndn_none
47 } pndnotify_loglevels_e;
48
49 // key/event definition
50 //
51 typedef struct {
52   int keycode;
53   char *keyname;
54 } keycode_t;
55
56 keycode_t keycodes[] = {
57   { KEY_A, "a" },
58   { KEY_MENU, "pandora" },
59   { KEY_POWER, "power" },
60   { -1, NULL }
61 };
62
63 typedef struct {
64   int type;
65   int code;
66   char *name;
67 } generic_event_t;
68
69 generic_event_t generics[] = {
70   { EV_SW, 0, "lid-toggle" }, // expecting value 1 (lid close) or 0 (lid open)
71   { -1, -1, NULL }
72 };
73
74 // event-to-sh mapping
75 //
76 typedef struct {
77
78   unsigned char key_p; // 1 if its a key, otherwise an event
79
80   /* template information
81    */
82   void *reqs;          // scancode/etc for the event in question
83
84   char *script;        // script to invoke
85   //unsigned int hold_min; // minimum hold-time to trigger
86
87   /* state
88    */
89   time_t last_trigger_time;
90   time_t keydown_time;
91
92 } evmap_t;
93
94 #define MAXEVENTS 255
95 evmap_t g_evmap [ MAXEVENTS ];
96 unsigned int g_evmap_max = 0;
97
98 /* get to it
99  */
100 void dispatch_key ( int keycode, int val );
101 void dispatch_event ( int code, int val );
102
103 static void usage ( char *argv[] ) {
104   printf ( "%s [-d]\n", argv [ 0 ] );
105   printf ( "-d\tDaemon mode; detach from terminal, chdir to /tmp, suppress output. Optional.\n" );
106   printf ( "-l#\tLog-it; -l is 0-and-up (or all), and -l2 means 2-and-up (not all); l[0-3] for now. Log goes to /tmp/pndevmapperd.log\n" );
107   printf ( "Signal: HUP the process to force reload of configuration and reset the notifier watch paths\n" );
108   return;
109 }
110
111 int main ( int argc, char *argv[] ) {
112   int i;
113   int logall = -1; // -1 means normal logging rules; >=0 means log all!
114
115   for ( i = 1; i < argc; i++ ) {
116
117     if ( argv [ i ][ 0 ] == '-' && argv [ i ][ 1 ] == 'd' ) {
118       //printf ( "Going daemon mode. Silent running.\n" );
119       g_daemon_mode = 1;
120     } else if ( argv [ i ][ 0 ] == '-' && argv [ i ][ 1 ] == 'l' ) {
121
122       if ( isdigit ( argv [ i ][ 2 ] ) ) {
123         unsigned char x = atoi ( argv [ i ] + 2 );
124         if ( x >= 0 &&
125              x < pndn_none )
126         {
127           logall = x;
128         }
129       } else {
130         logall = 0;
131       }
132     } else {
133       usage ( argv );
134       exit ( 0 );
135     }
136
137   } // for
138
139   /* enable logging?
140    */
141   pnd_log_set_pretext ( "pndevmapperd" );
142   pnd_log_set_flush ( 1 );
143
144   if ( logall == -1 ) {
145     // standard logging; non-daemon versus daemon
146
147     if ( g_daemon_mode ) {
148       // nada
149     } else {
150       pnd_log_set_filter ( pndn_rem );
151       pnd_log_to_stdout();
152     }
153
154   } else {
155     FILE *f;
156
157     f = fopen ( "/tmp/pndevmapperd.log", "w" );
158
159     if ( f ) {
160       pnd_log_set_filter ( logall );
161       pnd_log_to_stream ( f );
162       pnd_log ( pndn_rem, "logall mode - logging to /tmp/pndevmapperd.log\n" );
163     }
164
165     if ( logall == pndn_debug ) {
166       pnd_log_set_buried_logging ( 1 ); // log the shit out of it
167       pnd_log ( pndn_rem, "logall mode 0 - turned on buried logging\n" );
168     }
169
170   } // logall
171
172   pnd_log ( pndn_rem, "log level starting as %u", pnd_log_get_filter() );
173
174   // basic daemon set up
175   if ( g_daemon_mode ) {
176
177     // set a CWD somewhere else
178     chdir ( "/tmp" );
179
180     // detach from terminal
181     if ( ( i = fork() ) < 0 ) {
182       pnd_log ( pndn_error, "ERROR: Couldn't fork()\n" );
183       exit ( i );
184     }
185     if ( i ) {
186       exit ( 0 ); // exit parent
187     }
188     setsid();
189
190     // umask
191     umask ( 022 ); // emitted files can be rwxr-xr-x
192     
193   } // set up daemon
194
195   /* hmm, seems to not like working right after boot.. do we depend on another daemon or
196    * on giving kernel time to init something, or ... wtf?
197    * -- lets give the system some time to wake up
198    */
199   { // delay
200
201     // this one works for pndnotifyd, which actually needs INOTIFYH..
202     //
203
204     // check if inotify is awake yet; if not, try waiting for awhile to see if it does
205     pnd_log ( pndn_rem, "Starting INOTIFY test; should be instant, but may take awhile...\n" );
206
207     if ( ! pnd_notify_wait_until_ready ( 120 /* seconds */ ) ) {
208       pnd_log ( pndn_error, "ERROR: INOTIFY refuses to be useful and quite awhile has passed. Bailing out.\n" );
209       return ( -1 );
210     }
211
212     pnd_log ( pndn_rem, "INOTIFY seems to be useful, whew.\n" );
213
214     // pndnotifyd also waits for user to log in .. pretty excessive, especially since
215     // what if user wants to close the lid while at the log in screen? for now play the
216     // odds as thats pretty unliekly usage scenariom but is clearly not acceptible :/
217     //
218
219     // wait for a user to be logged in - we should probably get hupped when a user logs in, so we can handle
220     // log-out and back in again, with SDs popping in and out between..
221     pnd_log ( pndn_rem, "Checking to see if a user is logged in\n" );
222     char tmp_username [ 128 ];
223     while ( 1 ) {
224       if ( pnd_check_login ( tmp_username, 127 ) ) {
225         break;
226       }
227       pnd_log ( pndn_debug, "  No one logged in yet .. spinning.\n" );
228       sleep ( 2 );
229     } // spin
230     pnd_log ( pndn_rem, "Looks like user '%s' is in, continue.\n", tmp_username );
231
232   } // delay
233
234   /* inhale config or die trying
235    */
236   char *configpath;
237
238   // attempt to fetch a sensible default searchpath for configs
239   configpath = pnd_conf_query_searchpath();
240
241   // attempt to fetch the apps config. since it finds us the runscript
242   pnd_conf_handle evmaph;
243
244   evmaph = pnd_conf_fetch_by_id ( pnd_conf_evmap, configpath );
245
246   if ( ! evmaph ) {
247     // couldn't locate conf, just bail
248     pnd_log ( pndn_error, "ERROR: Couldn't locate conf file\n" );
249     exit ( -1 );
250   }
251
252   /* iterate across conf, stocking the event map
253    */
254   void *n = pnd_box_get_head ( evmaph );
255
256   while ( n ) {
257     char *k = pnd_box_get_key ( n );
258     //printf ( "key %s\n", k );
259
260     if ( strncmp ( k, "keys.", 5 ) == 0 ) {
261       k += 5;
262
263       // keys should really push push generic-events onto the table, since they;'re just a special case of them
264       // to make things easier to read
265
266       // figure out which keycode we're talking about
267       keycode_t *p = keycodes;
268       while ( p -> keycode != -1 ) {
269         if ( strcasecmp ( p -> keyname, k ) == 0 ) {
270           break;
271         }
272         p++;
273       }
274
275       if ( p -> keycode != -1 ) {
276         g_evmap [ g_evmap_max ].key_p = 1;    // its a key, not an event
277         g_evmap [ g_evmap_max ].reqs = p;     // note the keycode
278         g_evmap [ g_evmap_max ].script = n;   // note the script to activate in response
279         pnd_log ( pndn_rem, "Registered key %s [%d] to script %s\n", p -> keyname, p -> keycode, (char*) n );
280         g_evmap_max++;
281       } else {
282         pnd_log ( pndn_warning, "WARNING! Key '%s' is not handled by pndevmapperd yet! Skipping.", k );
283       }
284
285     } else if ( strncmp ( k, "events.", 7 ) == 0 ) {
286       k += 7;
287
288       // yes, key events could really be defined in this generic sense, and really we could just let people
289       // put the code and so on right in the conf, but trying to keep it easy on people; maybe should
290       // add a 'generic' section to conf file and just let folks redefine random events that way
291       // Really, it'd be nice if the /dev/input/events could spit out useful text, and just use scripts
292       // to respond without a daemon per se; for that matter, pnd-ls and pnd-map pnd-dotdesktopemitter
293       // should just exist as scripts rather than daemons, but whose counting?
294
295       // figure out which keycode we're talking about
296       generic_event_t *p = generics;
297       while ( p -> code != -1 ) {
298         if ( strcasecmp ( p -> name, k ) == 0 ) {
299           break;
300         }
301         p++;
302       }
303
304       if ( p -> code != -1 ) {
305         g_evmap [ g_evmap_max ].key_p = 0;    // its an event, not a key
306         g_evmap [ g_evmap_max ].reqs = p;     // note the keycode
307         g_evmap [ g_evmap_max ].script = n;   // note the script to activate in response
308         pnd_log ( pndn_rem, "Registered generic event %s [%d] to script %s\n", p -> name, p -> code, (char*) n );
309         g_evmap_max++;
310       } else {
311         pnd_log ( pndn_warning, "WARNING! Generic event '%s' is not handled by pndevmapperd yet! Skipping.", k );
312       }
313
314     } else if ( strncmp ( k, "pndevmapperd.", 7 ) == 0 ) {
315       // not consumed here, skip silently
316
317     } else {
318       // uhhh
319       pnd_log ( pndn_warning, "Unknown config key '%s'; skipping.\n", k );
320     }
321
322     n = pnd_box_get_next ( n );
323   } // while
324
325   if ( pnd_conf_get_as_int ( evmaph, "pndevmapperd.loglevel" ) != PND_CONF_BADNUM ) {
326     pnd_log_set_filter ( pnd_conf_get_as_int ( evmaph, "pndevmapperd.loglevel" ) );
327     pnd_log ( pndn_rem, "config file causes loglevel to change to %u", pnd_log_get_filter() );
328   }
329
330   if ( pnd_conf_get_as_int ( evmaph, "pndevmapperd.minimum_separation" ) != PND_CONF_BADNUM ) {
331     g_minimum_separation = pnd_conf_get_as_int ( evmaph, "pndevmapperd.minimum_separation" );
332     pnd_log ( pndn_rem, "config file causes minimum_separation to change to %u", g_minimum_separation );
333   }
334
335   /* do we have anything to do?
336    */
337   if ( ! g_evmap_max ) {
338     // uuuh, nothing to do?
339     pnd_log ( pndn_warning, "WARNING! No events configured to watch, so just spinning wheels...\n" );
340     exit ( -1 );
341   } // spin
342
343   /* actually try to do something useful
344    */
345
346   // stolen in part from notaz :)
347
348   // try to locate the appropriate devices
349   int id;
350   int fds [ 5 ] = { -1, -1, -1, -1, -1 }; // 0 = keypad, 1 = gpio keys
351   int imaxfd = 0;
352
353   for ( id = 0; ; id++ ) {
354     char fname[64];
355     char name[256] = { 0, };
356     int fd;
357
358     snprintf ( fname, sizeof ( fname ), "/dev/input/event%i", id );
359     fd = open ( fname, O_RDONLY );
360
361     if ( fd == -1 ) {
362       break;
363     }
364
365     ioctl (fd, EVIOCGNAME(sizeof(name)), name );
366
367     if ( strcmp ( name, "omap_twl4030keypad" ) == 0 ) {
368       fds [ 0 ] = fd;
369     } else if ( strcmp ( name, "gpio-keys" ) == 0) {
370       fds [ 1 ] = fd;
371     } else if ( strcmp ( name, "AT Translated Set 2 keyboard" ) == 0) { // for vmware, my dev environment
372       fds [ 0 ] = fd;
373     } else {
374       pnd_log ( pndn_rem, "Ignoring unknown device '%s'\n", name );
375       close ( fd );
376       continue;
377     }
378
379     if (imaxfd < fd) imaxfd = fd;
380   } // for
381
382   if ( fds [ 0 ] == -1 ) {
383     pnd_log ( pndn_warning, "WARNING! Couldn't find keypad device\n" );
384   }
385
386   if ( fds [ 1 ] == -1 ) {
387     pnd_log ( pndn_warning, "WARNING! couldn't find button device\n" );
388   }
389
390   if ( fds [ 0 ] == -1 && fds [ 1 ] == -1 ) {
391     pnd_log ( pndn_error, "ERROR! Couldn't find either device; exiting!\n" );
392     exit ( -2 );
393   }
394
395   /* loop forever, watching for events
396    */
397
398   while ( 1 ) {
399     struct input_event ev[64];
400
401     int fd = -1, rd, ret;
402     fd_set fdset;
403
404     FD_ZERO ( &fdset );
405
406     for (i = 0; i < 2; i++) {
407       if ( fds [ i ] != -1 ) {
408         FD_SET( fds [ i ], &fdset );
409       }
410     }
411
412     ret = select ( imaxfd + 1, &fdset, NULL, NULL, NULL );
413
414     if ( ret == -1 ) {
415       pnd_log ( pndn_error, "ERROR! select(2) failed with: %s\n", strerror ( errno ) );
416       break;
417     }
418
419     for ( i = 0; i < 2; i++ ) {
420       if ( fds [ i ] != -1 && FD_ISSET ( fds [ i ], &fdset ) ) {
421         fd = fds [ i ];
422       }
423     }
424
425     /* buttons or keypad */
426     rd = read ( fd, ev, sizeof(struct input_event) * 64 );
427     if ( rd < (int) sizeof(struct input_event) ) {
428       pnd_log ( pndn_error, "ERROR! read(2) input_event failed with: %s\n", strerror ( errno ) );
429       break;
430     }
431
432     for (i = 0; i < rd / sizeof(struct input_event); i++ ) {
433
434       if ( ev[i].type == EV_SYN ) {
435         continue;
436       } else if ( ev[i].type == EV_KEY ) {
437
438         // do we even know about this key at all?
439         keycode_t *p = keycodes;
440         while ( p -> keycode != -1 ) {
441           if ( p -> keycode == ev [ i ].code ) {
442             break;
443           }
444           p++;
445         }
446
447         // if we do, hand it off to dispatcher to look up if we actually do something with it
448         if ( p -> keycode != -1 ) {
449           pnd_log ( pndn_debug, "Key Event: key %s [%d] value %d\n", p -> keyname, p -> keycode, ev [ i ].value );
450           dispatch_key ( p -> keycode, ev [ i ].value );
451         } else {
452           pnd_log ( pndn_warning, "Unknown Key Event: keycode %d value %d\n",  ev [ i ].code, ev [ i ].value );
453         }
454
455       } else if ( ev[i].type == EV_SW ) {
456
457         // do we even know about this event at all?
458         generic_event_t *p = generics;
459         while ( p -> code != -1 ) {
460           if ( p -> code == ev [ i ].code ) {
461             break;
462           }
463           p++;
464         }
465
466         // if we do, hand it off to dispatcher to look up if we actually do something with it
467         if ( p -> code != -1 ) {
468           pnd_log ( pndn_debug, "Generic Event: event %s [%d] value %d\n", p -> name, p -> code, ev [ i ].value );
469           dispatch_event ( p -> code, ev [ i ].value );
470         } else {
471           pnd_log ( pndn_warning, "Unknown Generic Event: code %d value %d\n",  ev [ i ].code, ev [ i ].value );
472         }
473
474       } else {
475         pnd_log ( pndn_debug, "DEBUG: Unexpected event type %i received\n", ev[i].type );
476         continue;
477       } // type?
478
479     } // for
480
481   } // while
482
483   for (i = 0; i < 2; i++) {
484     if ( i != 2 && fds [ i ] != -1 ) {
485       close (fds [ i ] );
486     }
487   }
488
489   return ( 0 );
490 } // main
491
492 // this should really register the keystate and time, and then another func to monitor
493 // time-passage and check the registered list and act on the event..
494 void dispatch_key ( int keycode, int val ) {
495   unsigned int i;
496
497   // val decodes as:
498   // 1 - down (pressed)
499   // 2 - down again (hold)
500   // 0 - up (released)
501
502   for ( i = 0; i < g_evmap_max; i++ ) {
503
504     if ( ( g_evmap [ i ].key_p ) &&
505          ( ((keycode_t*) (g_evmap [ i ].reqs)) -> keycode == keycode ) &&
506          ( g_evmap [ i ].script ) )
507     {
508
509       // is this a keydown or a keyup?
510       if ( val == 1 ) {
511         // keydown
512         g_evmap [ i ].keydown_time = time ( NULL );
513
514       } else if ( val == 0 ) {
515         // keyup
516
517         char holdtime [ 128 ];
518         sprintf ( holdtime, "%d", (int)( time(NULL) - g_evmap [ i ].keydown_time ) );
519
520         if ( time ( NULL ) - g_evmap [ i ].last_trigger_time >= g_minimum_separation ) {
521           int x;
522
523           g_evmap [ i ].last_trigger_time = time ( NULL );
524
525           pnd_log ( pndn_rem, "Will attempt to invoke: %s %s\n", g_evmap [ i ].script, holdtime );
526
527           if ( ( x = fork() ) < 0 ) {
528             pnd_log ( pndn_error, "ERROR: Couldn't fork()\n" );
529             exit ( -3 );
530           }
531
532           if ( x == 0 ) {
533             execl ( g_evmap [ i ].script, g_evmap [ i ].script, holdtime, (char*)NULL );
534             pnd_log ( pndn_error, "ERROR: Couldn't exec(%s)\n", g_evmap [ i ].script );
535             exit ( -4 );
536           }
537
538         } else {
539           pnd_log ( pndn_rem, "Skipping invokation.. falls within minimum_separation threshold\n" );
540         }
541
542       } // key up or down?
543
544       return;
545     } // found matching event for keycode
546
547   } // while
548
549   return;
550 }
551
552 void dispatch_event ( int code, int val ) {
553   unsigned int i;
554
555   // LID val decodes as:
556   // 1 - closing
557   // 0 - opening
558
559   for ( i = 0; i < g_evmap_max; i++ ) {
560
561     if ( ( g_evmap [ i ].key_p == 0 ) &&
562          ( ((generic_event_t*) (g_evmap [ i ].reqs)) -> code == code ) &&
563          ( g_evmap [ i ].script ) )
564     {
565
566       // just hand the code to the script (ie: 0 or 1 to script)
567       if ( time ( NULL ) - g_evmap [ i ].last_trigger_time >= g_minimum_separation ) {
568         int x;
569         char value [ 100 ];
570
571         sprintf ( value, "%d", val );
572
573         g_evmap [ i ].last_trigger_time = time ( NULL );
574
575         pnd_log ( pndn_rem, "Will attempt to invoke: %s %s\n", g_evmap [ i ].script, value );
576
577         if ( ( x = fork() ) < 0 ) {
578           pnd_log ( pndn_error, "ERROR: Couldn't fork()\n" );
579           exit ( -3 );
580         }
581
582         if ( x == 0 ) {
583           execl ( g_evmap [ i ].script, g_evmap [ i ].script, value, (char*)NULL );
584           pnd_log ( pndn_error, "ERROR: Couldn't exec(%s)\n", g_evmap [ i ].script );
585           exit ( -4 );
586         }
587
588       } else {
589         pnd_log ( pndn_rem, "Skipping invokation.. falls within minimum_separation threshold\n" );
590       }
591
592       return;
593     } // found matching event for keycode
594
595   } // while
596
597   return;
598 }