Added first stab of pnd exec support
[pandora-libraries.git] / apps / pndnotifyd.c
index 2197e87..3cc1686 100644 (file)
@@ -4,33 +4,50 @@
  *
  */
 
-// TODO: for daemon mode, need to detach from the terminal
 // TODO: Catch HUP and reparse config
+// TODO: Should perhaps direct all printf's through a vsprintf handler to avoid redundant "if ! g_daemon_mode"
+// TODO: During daemon mode, should perhaps syslog or log errors
+// TODO: Removing stale .desktop checks that .desktop was created by libpnd; see 'TBD' below
 
-#include <stdio.h> // for stdio
-#include <unistd.h> // for exit()
-#include <stdlib.h> // for exit()
+#include <stdio.h>     // for stdio
+#include <unistd.h>    // for exit()
+#include <stdlib.h>    // for exit()
 #include <string.h>
-#include <time.h> // for time()
-#include <ctype.h> // for isdigit()
+#include <time.h>      // for time()
+#include <ctype.h>     // for isdigit()
+#include <sys/types.h> // for umask
+#include <sys/stat.h>  // for umask
+#include <dirent.h>    // for opendir()
 
 #include "pnd_conf.h"
 #include "pnd_container.h"
 #include "pnd_apps.h"
 #include "pnd_notify.h"
 #include "../lib/pnd_pathiter.h"
+#include "pnd_discovery.h"
+#include "pnd_locate.h"
 
 static unsigned char g_daemon_mode = 0;
 
 int main ( int argc, char *argv[] ) {
   pnd_notify_handle nh;
+  // like discotest
   char *configpath;
   char *appspath;
-  char *searchpath;
+  char *overridespath;
+  // daemon stuff
+  char *searchpath = NULL;
+  char *dotdesktoppath = NULL;
+  // behaviour
+  unsigned char scanonlaunch = 1;
+  unsigned int interval_secs = 20;
+  // pnd runscript
+  char *run_searchpath;
+  char *run_script;
+  char *pndrun;
+  // misc
   int i;
 
-  unsigned int interval_secs = 60;
-
   /* iterate across args
    */
   for ( i = 1; i < argc; i++ ) {
@@ -40,9 +57,12 @@ int main ( int argc, char *argv[] ) {
       g_daemon_mode = 1;
     } else if ( isdigit ( argv [ i ][ 0 ] ) ) {
       interval_secs = atoi ( argv [ i ] );
+    } else if ( argv [ i ][ 0 ] == '-' && argv [ i ][ 1 ] == 'n' ) {
+      scanonlaunch = 0;
     } else {
       printf ( "%s [-d] [##]\n", argv [ 0 ] );
       printf ( "-d\tDaemon mode; detach from terminal, chdir to /tmp, suppress output. Optional.\n" );
+      printf ( "-n\tDo not scan on launch; default is to run a scan for apps when %s is invoked.\n", argv [ 0 ] );
       printf ( "##\tA numeric value is interpreted as number of seconds between checking for filesystem changes. Default %u.\n",
               interval_secs );
       exit ( 0 );
@@ -54,6 +74,29 @@ int main ( int argc, char *argv[] ) {
     printf ( "Interval between checks is %u seconds\n", interval_secs );
   }
 
+  // basic daemon set up
+  if ( g_daemon_mode ) {
+
+    // set a CWD somewhere else
+#if 0
+    chdir ( "/tmp" );
+#endif
+
+    // detach from terminal
+    if ( ( i = fork() ) < 0 ) {
+      printf ( "ERROR: Couldn't fork()\n" );
+      exit ( i );
+    }
+    if ( i ) {
+      exit ( 0 ); // exit parent
+    }
+    setsid();
+
+    // umask
+    umask ( 022 ); // emitted files can be rwxr-xr-x
+    
+  } // set up daemon
+
   /* parse configs
    */
 
@@ -72,13 +115,74 @@ int main ( int argc, char *argv[] ) {
       appspath = PND_APPS_SEARCHPATH;
     }
 
+    overridespath = pnd_conf_get_as_char ( apph, PND_PXML_OVERRIDE_KEY );
+
+    if ( ! overridespath ) {
+      overridespath = PND_PXML_OVERRIDE_SEARCHPATH;
+    }
+
   } else {
     // couldn't find a useful app search path so use the default
     appspath = PND_APPS_SEARCHPATH;
+    overridespath = PND_PXML_OVERRIDE_SEARCHPATH;
+  }
+
+  // attempt to figure out where to drop dotfiles
+  pnd_conf_handle desktoph;
+
+  desktoph = pnd_conf_fetch_by_id ( pnd_conf_desktop, configpath );
+
+  if ( desktoph ) {
+    dotdesktoppath = pnd_conf_get_as_char ( desktoph, PND_DOTDESKTOP_KEY );
+
+    if ( ! dotdesktoppath ) {
+      dotdesktoppath = PND_DOTDESKTOP_DEFAULT;
+    }
+
+  } else {
+    dotdesktoppath = PND_DOTDESKTOP_DEFAULT;
+  }
+
+  // try to locate a runscript
+
+  if ( apph ) {
+    run_searchpath = pnd_conf_get_as_char ( apph, PND_PNDRUN_SEARCHPATH_KEY );
+    run_script = pnd_conf_get_as_char ( apph, PND_PNDRUN_KEY );
+    pndrun = NULL;
+
+    if ( ! run_searchpath ) {
+      run_searchpath = PND_APPS_SEARCHPATH;
+      run_script = PND_PNDRUN_FILENAME;
+    }
+
+  } else {
+    run_searchpath = NULL;
+    run_script = NULL;
+    pndrun = PND_PNDRUN_DEFAULT;
+  }
+
+  if ( ! pndrun ) {
+    pndrun = pnd_locate_filename ( run_searchpath, run_script );
+
+    if ( ! pndrun ) {
+      pndrun = PND_PNDRUN_DEFAULT;
+    }
+
   }
 
+  if ( ! g_daemon_mode ) {
+    if ( run_searchpath ) printf ( "Locating pnd run in %s\n", run_searchpath );
+    if ( run_script ) printf ( "Locating pnd runscript as %s\n", run_script );
+    if ( pndrun ) printf ( "Default pndrun is %s\n", pndrun );
+  }
+
+  /* startup
+   */
+
   if ( ! g_daemon_mode ) {
     printf ( "Apps searchpath is '%s'\n", appspath );
+    printf ( "PXML overrides searchpath is '%s'\n", overridespath );
+    printf ( ".desktop files emit to '%s'\n", dotdesktoppath );
   }
 
   /* set up notifies
@@ -101,12 +205,12 @@ int main ( int argc, char *argv[] ) {
   SEARCHPATH_PRE
   {
 
-    pnd_notify_watch_path ( nh, buffer, PND_NOTIFY_RECURSE );
-
     if ( ! g_daemon_mode ) {
       printf ( "Watching path '%s' and its descendents.\n", buffer );
     }
 
+    pnd_notify_watch_path ( nh, buffer, PND_NOTIFY_RECURSE );
+
   }
   SEARCHPATH_POST
 
@@ -115,21 +219,120 @@ int main ( int argc, char *argv[] ) {
   while ( 1 ) {
 
     // need to rediscover?
-    if ( pnd_notify_rediscover_p ( nh ) ) {
+    if ( scanonlaunch ||
+        pnd_notify_rediscover_p ( nh ) )
+    {
+      pnd_box_handle applist;
+      time_t createtime = time ( NULL ); // all 'new' .destops are created at or after this time; prev are old.
+
+      // if this was a forced scan, lets not do that next iteration
+      if ( scanonlaunch ) {
+       scanonlaunch = 0;
+      }
 
       // by this point, the watched directories have notified us that something of relevent
       // has occurred; we should be clever, but we're not, so just re-brute force the
       // discovery and spit out .desktop files..
       if ( ! g_daemon_mode ) {
-       printf ( "Time to re-discover!\n" );
+       printf ( "Changes within watched paths .. performing re-discover!\n" );
+       printf ( "Path to emit .desktop files to: '%s'\n", dotdesktoppath );
       }
 
-      // lets not eat up all the CPU
-      // should use an alarm or select() or something
-      sleep ( interval_secs );
+      // run the discovery
+      applist = pnd_disco_search ( appspath, overridespath );
+
+      // list the found apps (if any)
+      if ( applist ) {
+       pnd_disco_t *d = pnd_box_get_head ( applist );
+
+       while ( d ) {
+
+         if ( ! g_daemon_mode ) {
+           printf ( "Found app: %s\n", pnd_box_get_key ( d ) );
+         }
+
+         // create the .desktop file
+         if ( pnd_emit_dotdesktop ( dotdesktoppath, pndrun, d ) ) {
+           // add a watch onto the newly created .desktop?
+#if 0
+           char buffer [ FILENAME_MAX ];
+           sprintf ( buffer, "%s/%s", dotdesktoppath, d -> unique_id );
+           pnd_notify_watch_path ( nh, buffer, PND_NOTIFY_RECURSE );
+#endif
+         } else {
+           if ( ! g_daemon_mode ) {
+             printf ( "ERROR: Error creating .desktop file for app: %s\n", pnd_box_get_key ( d ) );
+           }
+         }
+
+         // next!
+         d = pnd_box_get_next ( d );
+
+       } // while applist
+
+      } else {
+
+       if ( ! g_daemon_mode ) {
+         printf ( "No applications found in search path\n" );
+       }
+
+      } // got apps?
+
+      // run a clean up, to remove any dotdesktop files that we didn't
+      // just now create (that seem to have been created by pndnotifyd
+      // previously.) This allows SD eject (or .pnd remove) to remove
+      // an app from the launcher
+      //   NOTE: Could opendir and iterate across all .desktop files,
+      // removing any that have Source= something else, and that the
+      // app name is not in the list found in applist box above. But
+      // a cheesy simple way right now is to just remove .desktop files
+      // that have a last mod time prior to the time we stored above.
+      {
+       DIR *dir;
+
+       if ( ( dir = opendir ( dotdesktoppath ) ) ) {
+         struct dirent *dirent;
+         struct stat dirs;
+         char buffer [ FILENAME_MAX ];
+
+         while ( ( dirent = readdir ( dir ) ) ) {
+
+           // file is a .desktop?
+           if ( strstr ( dirent -> d_name, ".desktop" ) == NULL ) {
+             continue;
+           }
+
+           // file was previously created by libpnd; check Source= line
+           // TBD
+
+           // file is 'new'?
+           sprintf ( buffer, "%s/%s", dotdesktoppath, dirent -> d_name );
+           if ( stat ( buffer, &dirs ) == 0 ) {
+             if ( dirs.st_mtime >= createtime ) {
+               continue;
+             }
+           }
+
+           // by this point, the .desktop file must be 'old' and created by pndnotifyd
+           // previously, so can remove it
+           if ( ! g_daemon_mode ) {
+             printf ( "File '%s' seems to not belong; removing it.\n", dirent -> d_name );
+           }
+           unlink ( buffer );
+
+         } // while getting filenames from dir
+
+         closedir ( dir );
+       }
+
+      } // purge old .desktop files
 
     } // need to rediscover?
 
+    // lets not eat up all the CPU
+    // should use an alarm or select() or something
+    sleep ( interval_secs );
+
   } // while
 
   /* shutdown