2 /* pndnotifyd - a daemon whose job is to monitor searchpaths for app changes (appearing, disappearing, changing).
3 * If a change is found, the discovery code is invoked and apps registered for the launchers to see
7 // TODO: Catch HUP and reparse config
8 // TODO: Should perhaps direct all printf's through a vsprintf handler to avoid redundant "if ! g_daemon_mode"
9 // TODO: During daemon mode, should perhaps syslog or log errors
11 #include <stdio.h> // for stdio
12 #include <unistd.h> // for exit()
13 #include <stdlib.h> // for exit()
14 #define __USE_GNU /* for strcasestr */
16 #include <time.h> // for time()
17 #include <ctype.h> // for isdigit()
18 #include <sys/types.h> // for umask
19 #include <sys/stat.h> // for umask
20 #include <dirent.h> // for opendir()
21 #include <signal.h> // for sigaction
24 #include "pnd_container.h"
26 #include "pnd_notify.h"
27 #include "../lib/pnd_pathiter.h"
28 #include "pnd_discovery.h"
29 #include "pnd_locate.h"
31 #include "pnd_utility.h"
32 #include "pnd_desktop.h"
34 // this piece of code was simpler once; but need to grow it a bit and in a rush
35 // moving all these to globals rather than refactor the code a bit; tsk tsk..
37 // op mode; emitting stdout or no?
38 static unsigned char g_daemon_mode = 0;
45 char *searchpath = NULL;
46 char *dotdesktoppath = NULL;
47 char *iconpath = NULL;
48 char *notifypath = NULL;
50 char *run_searchpath; // searchpath to find pnd_run.sh
51 char *run_script; // name of pnd_run.sh script from config
52 char *pndrun; // full path to located pnd_run.sh
53 char *pndhup = NULL; // full path to located pnd_hup.sh
55 pnd_notify_handle nh = 0;
58 void consume_configuration ( void );
59 void setup_notifications ( void );
60 void sighup_handler ( int n );
62 int main ( int argc, char *argv[] ) {
64 unsigned char scanonlaunch = 1;
65 unsigned int interval_secs = 10;
69 /* iterate across args
71 for ( i = 1; i < argc; i++ ) {
73 if ( argv [ i ][ 0 ] == '-' && argv [ i ][ 1 ] == 'd' ) {
74 //printf ( "Going daemon mode. Silent running.\n" );
76 } else if ( isdigit ( argv [ i ][ 0 ] ) ) {
77 interval_secs = atoi ( argv [ i ] );
78 } else if ( argv [ i ][ 0 ] == '-' && argv [ i ][ 1 ] == 'n' ) {
81 printf ( "%s [-d] [##]\n", argv [ 0 ] );
82 printf ( "-d\tDaemon mode; detach from terminal, chdir to /tmp, suppress output. Optional.\n" );
83 printf ( "-n\tDo not scan on launch; default is to run a scan for apps when %s is invoked.\n", argv [ 0 ] );
84 printf ( "##\tA numeric value is interpreted as number of seconds between checking for filesystem changes. Default %u.\n",
86 printf ( "Signal: HUP the process to force reload of configuration and reset the notifier watch paths\n" );
92 if ( ! g_daemon_mode ) {
93 printf ( "Interval between checks is %u seconds\n", interval_secs );
96 // basic daemon set up
97 if ( g_daemon_mode ) {
99 // set a CWD somewhere else
104 // detach from terminal
105 if ( ( i = fork() ) < 0 ) {
106 printf ( "ERROR: Couldn't fork()\n" );
110 exit ( 0 ); // exit parent
115 umask ( 022 ); // emitted files can be rwxr-xr-x
122 consume_configuration();
127 if ( ! g_daemon_mode ) {
128 printf ( "Apps searchpath is '%s'\n", appspath );
129 printf ( "PXML overrides searchpath is '%s'\n", overridespath );
130 printf ( ".desktop files emit to '%s'\n", dotdesktoppath );
131 printf ( ".desktop icon files emit to '%s'\n", iconpath );
132 printf ( "Notify searchpath is '%s'\n", notifypath );
135 /* set up signal handler
140 struct sigaction siggy;
141 siggy.sa_handler = sighup_handler;
142 siggy.sa_mask = ss; /* implicitly blocks the origin signal */
143 siggy.sa_flags = 0; /* don't need anything */
145 sigaction ( SIGHUP, &siggy, NULL );
150 // if we're gong to scan on launch, it'll set things right up and then set up notifications.
151 // if no scan on launch, we need to set the notif's up now.
152 //if ( ! scanonlaunch ) {
153 setup_notifications();
160 // need to rediscover?
162 pnd_notify_rediscover_p ( nh ) )
164 pnd_box_handle applist;
165 time_t createtime = time ( NULL ); // all 'new' .destops are created at or after this time; prev are old.
167 // if this was a forced scan, lets not do that next iteration
168 if ( scanonlaunch ) {
172 // by this point, the watched directories have notified us that something of relevent
173 // has occurred; we should be clever, but we're not, so just re-brute force the
174 // discovery and spit out .desktop files..
175 if ( ! g_daemon_mode ) {
176 printf ( "------------------------------------------------------\n" );
177 printf ( "Changes within watched paths .. performing re-discover!\n" );
181 applist = pnd_disco_search ( appspath, overridespath );
183 // list the found apps (if any)
185 pnd_disco_t *d = pnd_box_get_head ( applist );
189 if ( ! g_daemon_mode ) {
190 printf ( "Found app: %s\n", pnd_box_get_key ( d ) );
193 // check if icon already exists (from a previous extraction say); if so, we needn't
195 char existingpath [ FILENAME_MAX ];
196 sprintf ( existingpath, "%s/%s.png", iconpath, d -> unique_id );
199 if ( stat ( existingpath, &dirs ) == 0 ) {
200 // icon seems to exist, so just crib the location into the .desktop
202 if ( ! g_daemon_mode ) {
203 printf ( " Found icon already existed, so reusing it! %s\n", existingpath );
209 d -> icon = strdup ( existingpath );
212 // icon seems unreadable or does not exist; lets try to create it..
214 if ( ! g_daemon_mode ) {
215 printf ( " Icon not already present, so trying to write it! %s\n", existingpath );
218 // attempt to create icon files; if successful, alter the disco struct to contain new
219 // path, otherwise leave it alone (since it could be a generic icon reference..)
220 if ( pnd_emit_icon ( iconpath, d ) ) {
221 // success; fix up icon path to new one..
225 d -> icon = strdup ( existingpath );
227 if ( ! g_daemon_mode ) {
228 printf ( " WARN: Couldn't write out icon %s\n", existingpath );
232 } // icon already exists?
234 // create the .desktop file
235 if ( pnd_emit_dotdesktop ( dotdesktoppath, pndrun, d ) ) {
236 // add a watch onto the newly created .desktop?
238 char buffer [ FILENAME_MAX ];
239 sprintf ( buffer, "%s/%s", dotdesktoppath, d -> unique_id );
240 pnd_notify_watch_path ( nh, buffer, PND_NOTIFY_RECURSE );
243 if ( ! g_daemon_mode ) {
244 printf ( "ERROR: Error creating .desktop file for app: %s\n", pnd_box_get_key ( d ) );
249 d = pnd_box_get_next ( d );
255 if ( ! g_daemon_mode ) {
256 printf ( "No applications found in search path\n" );
261 // run a clean up, to remove any dotdesktop files that we didn't
262 // just now create (that seem to have been created by pndnotifyd
263 // previously.) This allows SD eject (or .pnd remove) to remove
264 // an app from the launcher
265 // NOTE: Could opendir and iterate across all .desktop files,
266 // removing any that have Source= something else, and that the
267 // app name is not in the list found in applist box above. But
268 // a cheesy simple way right now is to just remove .desktop files
269 // that have a last mod time prior to the time we stored above.
273 if ( ( dir = opendir ( dotdesktoppath ) ) ) {
274 struct dirent *dirent;
276 char buffer [ FILENAME_MAX ];
278 while ( ( dirent = readdir ( dir ) ) ) {
280 // file is a .desktop?
281 if ( strstr ( dirent -> d_name, ".desktop" ) == NULL ) {
285 // figure out full path
286 sprintf ( buffer, "%s/%s", dotdesktoppath, dirent -> d_name );
288 // file was previously created by libpnd; check Source= line
289 // logic: default to 'yes' (in case we can't open the file for some reason)
290 // if we can open the file, default to no and look for the source flag we added; if
291 // that matches then we know its libpnd created, otherwise assume not.
292 unsigned char source_libpnd = 1;
295 FILE *grep = fopen ( buffer, "r" );
298 while ( fgets ( line, 255, grep ) ) {
299 if ( strcasestr ( line, PND_DOTDESKTOP_SOURCE ) ) {
306 if ( source_libpnd ) {
308 if ( ! g_daemon_mode ) {
309 printf ( "File '%s' appears to have been created by libpnd so candidate for delete: %u\n", buffer, source_libpnd );
314 if ( ! g_daemon_mode ) {
315 printf ( "File '%s' appears NOT to have been created by libpnd, so leave it alone\n", buffer );
318 continue; // skip deleting it
322 if ( stat ( buffer, &dirs ) == 0 ) {
323 if ( dirs.st_mtime >= createtime ) {
325 if ( ! g_daemon_mode ) {
326 printf ( "File '%s' seems 'new', so leave it alone.\n", buffer );
329 continue; // skip deleting it
333 // by this point, the .desktop file must be 'old' and created by pndnotifyd
334 // previously, so can remove it
335 if ( ! g_daemon_mode ) {
336 printf ( "File '%s' seems nolonger relevent; removing it.\n", dirent -> d_name );
340 } // while getting filenames from dir
345 } // purge old .desktop files
347 // if we've got a hup script located, lets invoke it
349 if ( ! g_daemon_mode ) {
350 printf ( "Invoking hup script '%s'.\n", pndhup );
352 pnd_exec_no_wait_1 ( pndhup, NULL );
355 // since its entirely likely new directories have been found (ie: SD with a directory structure was inserted)
356 // we should re-apply watches to catch all these new directories; ie: user might use on-device browser to
357 // drop in new applications, or use the shell to juggle them around, or any number of activities.
358 //setup_notifications();
360 } // need to rediscover?
362 // lets not eat up all the CPU
363 // should use an alarm or select() or something
364 sleep ( interval_secs );
370 pnd_notify_shutdown ( nh );
375 void consume_configuration ( void ) {
377 // attempt to fetch a sensible default searchpath for configs
378 configpath = pnd_conf_query_searchpath();
380 // attempt to fetch the apps config to pick up a searchpath
381 pnd_conf_handle apph;
383 apph = pnd_conf_fetch_by_id ( pnd_conf_apps, configpath );
387 appspath = pnd_conf_get_as_char ( apph, PND_APPS_KEY );
390 appspath = PND_APPS_SEARCHPATH;
393 overridespath = pnd_conf_get_as_char ( apph, PND_PXML_OVERRIDE_KEY );
395 if ( ! overridespath ) {
396 overridespath = PND_PXML_OVERRIDE_SEARCHPATH;
399 notifypath = pnd_conf_get_as_char ( apph, PND_APPS_NOTIFY_KEY );
401 if ( ! notifypath ) {
402 notifypath = PND_APPS_NOTIFYPATH;
406 // couldn't find a useful app search path so use the default
407 appspath = PND_APPS_SEARCHPATH;
408 overridespath = PND_PXML_OVERRIDE_SEARCHPATH;
409 notifypath = PND_APPS_NOTIFYPATH;
412 // attempt to figure out where to drop dotfiles
413 pnd_conf_handle desktoph;
415 desktoph = pnd_conf_fetch_by_id ( pnd_conf_desktop, configpath );
418 dotdesktoppath = pnd_conf_get_as_char ( desktoph, PND_DOTDESKTOP_KEY );
420 if ( ! dotdesktoppath ) {
421 dotdesktoppath = PND_DOTDESKTOP_DEFAULT;
424 iconpath = pnd_conf_get_as_char ( desktoph, PND_DOTDESKTOPICONS_KEY );
427 iconpath = PND_DOTDESKTOPICONS_DEFAULT;
431 dotdesktoppath = PND_DOTDESKTOPICONS_DEFAULT;
434 // try to locate a runscript and optional hupscript
437 run_searchpath = pnd_conf_get_as_char ( apph, PND_PNDRUN_SEARCHPATH_KEY );
438 run_script = pnd_conf_get_as_char ( apph, PND_PNDRUN_KEY );
441 if ( ! run_searchpath ) {
442 run_searchpath = PND_APPS_SEARCHPATH;
443 run_script = PND_PNDRUN_FILENAME;
447 run_searchpath = NULL;
449 pndrun = PND_PNDRUN_DEFAULT;
453 pndrun = pnd_locate_filename ( run_searchpath, run_script );
456 pndrun = strdup ( pndrun ); // so we don't just use the built in buffer; next locate will overwrite it
458 pndrun = PND_PNDRUN_DEFAULT;
463 if ( desktoph && run_searchpath ) {
466 if ( ( t = pnd_conf_get_as_char ( desktoph, PND_PNDHUP_KEY ) ) ) {
467 pndhup = pnd_locate_filename ( run_searchpath, t );
470 pndhup = strdup ( pndhup ); // so we don't just use the built in buffer; next locate will overwrite it
473 #if 0 // don't enable this; if no key in config, we don't want to bother hupping
475 pndhup = pnd_locate_filename ( run_searchpath, PND_PNDHUP_FILENAME );
482 if ( ! g_daemon_mode ) {
483 if ( run_searchpath ) printf ( "Locating pnd run in %s\n", run_searchpath );
484 if ( run_script ) printf ( "Locating pnd runscript as %s\n", run_script );
485 if ( pndrun ) printf ( "pndrun is %s\n", pndrun );
487 printf ( "pndhup is %s\n", pndhup );
489 printf ( "No pndhup found (which is fine.)\n" );
493 /* handle globbing or variable substitution
495 dotdesktoppath = pnd_expand_tilde ( strdup ( dotdesktoppath ) );
496 iconpath = pnd_expand_tilde ( strdup ( iconpath ) );
500 mkdir ( dotdesktoppath, 0777 );
501 mkdir ( iconpath, 0777 );
507 void setup_notifications ( void ) {
508 searchpath = notifypath;
510 // if this is first time through, we can just set it up; for subsequent times
511 // through, we need to close existing fd and re-open it, since we're too lame
512 // to store the list of watches and 'rm' them
515 pnd_notify_shutdown ( nh );
520 // set up a new set of notifies
522 nh = pnd_notify_init();
526 if ( ! g_daemon_mode ) {
527 printf ( "INOTIFY failed to init.\n" );
533 if ( ! g_daemon_mode ) {
534 printf ( "INOTIFY is up.\n" );
541 if ( ! g_daemon_mode ) {
542 printf ( "Watching path '%s' and its descendents.\n", buffer );
545 pnd_notify_watch_path ( nh, buffer, PND_NOTIFY_RECURSE );
553 void sighup_handler ( int n ) {
555 if ( ! g_daemon_mode ) {
556 printf ( "---[ SIGHUP received ]---\n" );
559 // reparse config files
560 consume_configuration();
562 // re set up the notifier watches
563 setup_notifications();