pndnotifyd: fix some crashes
[pandora-libraries.git] / apps / pndnotifyd.c
index 134bcc5..aa79185 100644 (file)
@@ -15,6 +15,7 @@
 #include <sys/stat.h>  // for umask
 #include <dirent.h>    // for opendir()
 #include <signal.h>    // for sigaction
+#include <errno.h>     // for errno
 
 #include "pnd_conf.h"
 #include "pnd_container.h"
@@ -28,6 +29,7 @@
 #include "pnd_desktop.h"
 #include "pnd_logger.h"
 #include "pnd_dbusnotify.h"
+#include "pnd_pndfiles.h"
 
 // this piece of code was simpler once; but need to grow it a bit and in a rush
 // moving all these to globals rather than refactor the code a bit; tsk tsk..
@@ -57,6 +59,7 @@ char *desktop_appspath = NULL;
 char *menu_dotdesktoppath = NULL;
 char *menu_iconpath = NULL;
 char *menu_appspath = NULL;
+char *info_dotdesktoppath = NULL;
 // pnd runscript
 char *run_searchpath; // searchpath to find pnd_run.sh
 char *run_script;     // name of pnd_run.sh script from config
@@ -67,9 +70,11 @@ char g_username [ 128 ]; // since we have to wait for login (!!), store username
 // notifier handle
 pnd_notify_handle nh = 0;
 pnd_dbusnotify_handle dbh = 0;
+unsigned char g_info_p = 0; // spit out info .desktops
 
 // constants
 #define PNDNOTIFYD_LOGLEVEL "pndnotifyd.loglevel"
+#define PNDLOCKNAME "pndnotifyd-disco.lock"
 
 // decl's
 void consume_configuration ( void );
@@ -194,7 +199,7 @@ int main ( int argc, char *argv[] ) {
 
     // umask
     umask ( 022 ); // emitted files can be rwxr-xr-x
-    
+
   } // set up daemon
 
   // wait for a user to be logged in - we should probably get hupped when a user logs in, so we can handle
@@ -229,6 +234,7 @@ int main ( int argc, char *argv[] ) {
   pnd_log ( pndn_rem, "Apps searchpath is '%s'\n", menu_appspath );
   pnd_log ( pndn_rem, ".desktop files emit to '%s'\n", menu_dotdesktoppath );
   pnd_log ( pndn_rem, ".desktop icon files emit to '%s'\n", menu_iconpath );
+  pnd_log ( pndn_rem, ".desktop info files emit to '%s'\n", info_dotdesktoppath ? info_dotdesktoppath : "n/a" );
 
   /* set up signal handler
    */
@@ -258,18 +264,94 @@ int main ( int argc, char *argv[] ) {
 
   /* daemon main loop
    */
-  unsigned char watch_inotify, watch_dbus;
+  unsigned char watch_inotify = 0, watch_dbus = 0;
+  unsigned char idle = 0;
   while ( 1 ) {
+    int retcode;
+
+    // collect all events to avoid multiple PND rescans
+    do {
+      struct timeval timeout_1s = { 1, 0};
+      int fd_inotify = -1, fd_dbus = -1;
+      struct timeval *timeout = NULL;
+      fd_set rfds, efds;
+      int fd_max = -1;
+
+      if ( dbh )
+        fd_dbus = pnd_dbusnotify_fd ( dbh );
+      if ( nh )
+        fd_inotify = pnd_notify_fd ( nh );
+
+      FD_ZERO ( &rfds );
+      FD_ZERO ( &efds );
+
+      if ( fd_dbus != -1 ) {
+        FD_SET ( fd_dbus, &rfds );
+        FD_SET ( fd_dbus, &efds );
+        if ( fd_dbus > fd_max )
+          fd_max = fd_dbus;
+      }
+
+      if ( fd_inotify != -1 ) {
+        FD_SET ( fd_inotify, &rfds );
+        FD_SET ( fd_inotify, &efds );
+        if ( fd_inotify > fd_max )
+          fd_max = fd_inotify;
+      }
+
+      if ( fd_max == -1 ) {
+        pnd_log ( pndn_error, "ERROR: notify: nothing to watch\n" );
+        retcode = -1;
+        break;
+      }
+
+      // the idle timeout is needed for inotify as some file updates produce several events
+      // with a delay in between (length depends on things like file size and system load), like
+      // - IN_CREATE, IN_CLOSE_WRITE (cp, mv from other fs, etc)
+      // - IN_MOVED_TO, IN_CLOSE_WRITE (PNDManager)
+      // - multiple file copies, deletes, etc
+      timeout = idle ? NULL : &timeout_1s;
+
+      retcode = select ( fd_max + 1, &rfds, NULL, &efds, timeout );
+
+      if ( retcode < 0 ) {
+        pnd_log ( pndn_error, "ERROR: notify: select failed: %d\n", errno );
+        if ( errno == EINTR )
+          retcode = 0;
+
+        break;
+      }
+      idle = ( retcode == 0 );
 
-    watch_dbus = 0;
-    watch_inotify = 0;
+      if ( dbh && FD_ISSET ( fd_dbus, &efds ) ) {
+        pnd_log ( pndn_error, "ERROR: notify: dbus fd error\n" );
+        pnd_dbusnotify_shutdown ( dbh );
+        dbh = NULL;
+      }
+
+      if ( nh && FD_ISSET ( fd_inotify, &efds ) ) {
+        pnd_log ( pndn_error, "ERROR: notify: inotify fd error\n" );
+        pnd_notify_shutdown ( nh );
+        nh = NULL;
+      }
 
-    if ( dbh ) {
-      watch_dbus = pnd_dbusnotify_rediscover_p ( dbh );
+      if ( dbh && FD_ISSET ( fd_dbus, &rfds ) ) {
+        watch_dbus |= pnd_dbusnotify_rediscover_p ( dbh );
+        if ( watch_dbus && nh ) {
+          // will restart inotify (see below), drop all events
+          pnd_notify_shutdown ( nh );
+          nh = NULL;
+        }
+      }
+
+      if ( nh && FD_ISSET ( fd_inotify, &rfds ) ) {
+        watch_inotify |= pnd_notify_rediscover_p ( nh );
+      }
     }
+    while ( !idle );
 
-    if ( ! watch_dbus && nh ) {
-      watch_inotify = pnd_notify_rediscover_p ( nh );
+    if ( retcode < 0 ) {
+      break;
     }
 
     // need to rediscover?
@@ -294,13 +376,12 @@ int main ( int argc, char *argv[] ) {
 
       if ( watch_dbus ) {
        pnd_log ( pndn_rem, "dbusnotify detected an event\n" );
-       pnd_notify_shutdown ( nh );
-       nh = 0;
       }
 
-      if ( time ( NULL ) - createtime <= 2 ) {
-       pnd_log ( pndn_rem, "Rediscovery request comes to soon after previous discovery; skipping.\n" );
+      if ( time ( NULL ) - createtime < interval_secs ) {
+       pnd_log ( pndn_rem, "Rediscovery request comes to soon after previous discovery; delaying.\n" );
        sleep ( interval_secs );
+       idle = 0;
        continue;
       }
 
@@ -311,6 +392,13 @@ int main ( int argc, char *argv[] ) {
       /* run the discovery
        */
 
+      // do some 'locking'
+      pnd_log ( pndn_rem, "creating lockfile %s", PNDLOCKNAME );
+      if ( ! pnd_lock ( PNDLOCKNAME ) ) {
+       // problem .. well, too bad, we need to do this .. proceed!
+      }
+
+      // scan..
       pnd_log ( pndn_rem, "  Scanning desktop paths----------------------------\n" );
       if ( ! perform_discoveries ( desktop_appspath, overridespath, desktop_dotdesktoppath, desktop_iconpath ) ) {
        pnd_log ( pndn_rem, "    No applications found in desktop search path\n" );
@@ -323,6 +411,10 @@ int main ( int argc, char *argv[] ) {
        }
       }
 
+      // unlock
+      pnd_log ( pndn_rem, "clearing lockfile %s", PNDLOCKNAME );
+      pnd_unlock ( PNDLOCKNAME );
+
       // if we've got a hup script located, lets invoke it
       if ( pndhup ) {
        pnd_log ( pndn_rem, "Invoking hup script '%s'.\n", pndhup );
@@ -336,23 +428,20 @@ int main ( int argc, char *argv[] ) {
        setup_notifications();
       }
 
-    } // need to rediscover?
+      watch_inotify = watch_dbus = 0;
 
-    // lets not eat up all the CPU
-    // should use an alarm or select() or something -- I mean really, why aren't I putting interval_secs into
-    // the select() call above in pnd_notify_whatsitcalled()? -- but lets not break this right before release shall we
-    // NOTE: Oh right, I remember now -- inotify will spam when a card is inserted, and it will not be instantaneoous..
-    // the events will dribble in over a second. So this sleep is a lame trick that generally works. I really should
-    // do select(), and then when it returns just spin for a couple seconds slurping up events until no more and a thresh-hold
-    // time is hit, but this will do for now. I suck.
-    sleep ( interval_secs );
+    } // need to rediscover?
 
   } // while
 
   /* shutdown
    */
-  pnd_notify_shutdown ( nh );
-  pnd_dbusnotify_shutdown ( dbh );
+  if ( nh ) {
+    pnd_notify_shutdown ( nh );
+  }
+  if ( dbh ) {
+    pnd_dbusnotify_shutdown ( dbh );
+  }
 
   return ( 0 );
 }
@@ -401,6 +490,7 @@ void consume_configuration ( void ) {
     desktop_dotdesktoppath = pnd_conf_get_as_char ( desktoph, PND_DESKTOP_DOTDESKTOP_PATH_KEY );
     desktop_iconpath = pnd_conf_get_as_char ( desktoph, PND_DESKTOP_ICONS_PATH_KEY );
     desktop_appspath = pnd_conf_get_as_char ( desktoph, PND_DESKTOP_SEARCH_KEY );
+    info_dotdesktoppath = pnd_conf_get_as_char ( desktoph, "info.dotdesktoppath" );
   }
 
   if ( ! desktop_dotdesktoppath ) {
@@ -422,6 +512,11 @@ void consume_configuration ( void ) {
     menu_appspath = pnd_conf_get_as_char ( desktoph, PND_MENU_SEARCH_KEY );
   }
 
+  // info
+  if ( desktoph ) {
+    g_info_p = pnd_conf_get_as_int_d ( desktoph, "info.emit_info", 0 );
+  }
+
   /* try to locate a runscript and optional hupscript
    */
 
@@ -557,6 +652,11 @@ void consume_configuration ( void ) {
   mkdir ( desktop_dotdesktoppath, 0777 );
   mkdir ( desktop_iconpath, 0777 );
 
+  if ( info_dotdesktoppath ) {
+    info_dotdesktoppath = pnd_expand_tilde ( strdup ( info_dotdesktoppath ) );
+    mkdir ( info_dotdesktoppath, 0777 );
+  }
+
   if ( menu_dotdesktoppath ) {
     menu_dotdesktoppath = pnd_expand_tilde ( strdup ( menu_dotdesktoppath ) );
     mkdir ( menu_dotdesktoppath, 0777 );
@@ -639,10 +739,12 @@ void sigint_handler ( int n ) {
 
   if ( dbh ) {
     pnd_dbusnotify_shutdown ( dbh );
+    dbh = NULL;
   }
 
   if ( nh ) {
     pnd_notify_shutdown ( nh );
+    nh = NULL;
   }
 
   return;
@@ -679,17 +781,37 @@ void process_discoveries ( pnd_box_handle applist, char *emitdesktoppath, char *
 
       pnd_log ( pndn_debug, "  Icon not already present, so trying to write it! %s\n", existingpath );
 
+      // handle same-path icon override for davec :)
+      char ovrfile [ PATH_MAX ];
+      char *fixpxml;
+      sprintf ( ovrfile, "%s/%s", d -> object_path, d -> object_filename );
+      fixpxml = strcasestr ( ovrfile, PND_PACKAGE_FILEEXT );
+      if ( fixpxml ) {
+       strcpy ( fixpxml, ".png" );
+       fixpxml = NULL;
+       struct stat statbuf;
+       if ( stat ( ovrfile, &statbuf ) == 0 ) {
+         d -> icon = strdup ( ovrfile );
+         fixpxml = ovrfile; // !NULL will be the trigger to skip emittinf desktop from .pnd
+       } // stat
+      } // ovr?
+
       // attempt to create icon files; if successful, alter the disco struct to contain new
       // path, otherwise leave it alone (since it could be a generic icon reference..)
-      if ( pnd_emit_icon ( emiticonpath, d ) ) {
-       // success; fix up icon path to new one..
-       if ( d -> icon ) {
-         free ( d -> icon );
+      if ( fixpxml == NULL ) {
+       // don't have an same-path override icon, so go fetch something from pnd file
+
+       if ( pnd_emit_icon ( emiticonpath, d ) ) {
+         // success; fix up icon path to new one..
+         if ( d -> icon ) {
+           free ( d -> icon );
+         }
+         d -> icon = strdup ( existingpath );
+       } else {
+         pnd_log ( pndn_debug, "  WARN: Couldn't write out icon %s\n", existingpath );
        }
-       d -> icon = strdup ( existingpath );
-      } else {
-       pnd_log ( pndn_debug, "  WARN: Couldn't write out icon %s\n", existingpath );
-      }
+
+      } // got ovr icon already?
 
     } // icon already exists?
 
@@ -705,6 +827,13 @@ void process_discoveries ( pnd_box_handle applist, char *emitdesktoppath, char *
       pnd_log ( pndn_rem, "ERROR: Error creating .desktop file for app: %s\n", pnd_box_get_key ( d ) );
     }
 
+    // info .desktop
+    if ( g_info_p && info_dotdesktoppath ) {
+      if ( ! pnd_emit_dotinfo ( info_dotdesktoppath, pndrun, d ) ) {
+       pnd_log ( pndn_rem, "ERROR: Error creating info .desktop file for app: %s\n", pnd_box_get_key ( d ) );
+      }
+    }
+
     // does this object request any mkdir's?
     if ( d -> mkdir_sp ) {