SOLIB = libpnd.so.1 # canonicle name
SOLIB1 = libpnd.so.1.0.1 # versioned name
XMLOBJ = lib/tinyxml/tinystr.o lib/tinyxml/tinyxml.o lib/tinyxml/tinyxmlerror.o lib/tinyxml/tinyxmlparser.o
-ALLOBJ = pnd_conf.o pnd_container.o pnd_discovery.o pnd_pxml.o pnd_notify.o pnd_locate.o pnd_tinyxml.o pnd_pndfiles.o pnd_apps.o pnd_utility.o pnd_desktop.o pnd_io_gpio.o
+ALLOBJ = pnd_conf.o pnd_container.o pnd_discovery.o pnd_pxml.o pnd_notify.o pnd_locate.o pnd_tinyxml.o pnd_pndfiles.o pnd_apps.o pnd_utility.o pnd_desktop.o pnd_io_gpio.o pnd_logger.o
-all: ${SOLIB} ${LIB} conftest discotest notifytest pndnotifyd rawpxmltest pndvalidator
+all: ${SOLIB} ${LIB} conftest discotest notifytest pndnotifyd rawpxmltest pndvalidator loggertest pnd_run
clean:
- ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o bin/discotest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pndnotifyd pndnotifyd.o ${SOLIB} testdata/dotdesktop/*.desktop testdata/apps/*.pnd testdata/dotdesktop/*.png deployment/usr/lib/libpnd* deployment/usr/bin/pndnotifyd deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o
+ ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o bin/discotest loggertest.o bin/loggertest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pnd_run pnd_run.o bin/pndnotifyd pndnotifyd.o ${SOLIB} testdata/dotdesktop/*.desktop testdata/apps/*.pnd testdata/dotdesktop/*.png deployment/usr/lib/libpnd* deployment/usr/bin/pndnotifyd deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o
${RM} -rf deployment/media
find . -name "*~*" -exec rm {} \; -print
pndvalidator: pndvalidator.o ${SOLIB1}
${CC} -lstdc++ -o bin/pndvalidator pndvalidator.o ${SOLIB1}
+pnd_run: pnd_run.o ${SOLIB1}
+ ${CC} -lstdc++ -o bin/pnd_run pnd_run.o ${SOLIB1}
+
# deployment and assembly components
#
# copy in goodies
cp libpnd* deployment/usr/lib
cp bin/pndnotifyd deployment/usr/bin
+ cp bin/pnd_run deployment/usr/bin
cp testdata/scripts/* deployment/usr/pandora/scripts
# copy in freebee .pnd apps to /usr/pandora/apps
# add pndnotify to etc/rc/startup-whatever
rawpxmltest: rawpxmltest.o ${LIB}
${CC} -lstdc++ -o bin/rawpxmltest rawpxmltest.o ${LIB}
+
+loggertest: loggertest.o ${LIB}
+ ${CC} -lstdc++ -o bin/loggertest loggertest.o libpnd.a
--- /dev/null
+
+#include <stdio.h> /* for printf, NULL */
+#include <stdlib.h> /* for free */
+#include <string.h> /* for strdup */
+
+#include "pnd_conf.h"
+#include "pnd_container.h"
+#include "pnd_apps.h"
+#include "pnd_discovery.h"
+#include "pnd_locate.h"
+#include "pnd_pndfiles.h"
+#include "pnd_pxml.h"
+
+static void usage ( char *argv[] ) {
+ printf ( "%s [-r runscript] [-n] path-to-pndfile\n", argv [ 0 ] );
+ printf ( "-r\tOptional. If not specified, will attempt to suss from configs.\n" );
+ printf ( "-n\tOptional. If present, instruct runscript to kill/restart X11 around app.\n" );
+ printf ( "pndfile\tRequired. Full path to the pnd-file to execute.\n" );
+ return;
+}
+
+int main ( int argc, char *argv[] ) {
+ char *pnd_run = NULL;
+ char *pndfile = NULL;
+ unsigned char no_x11 = 0;
+ unsigned char i;
+
+ for ( i = 1; i < argc; i++ ) {
+
+ if ( argv [ i ][ 0 ] == '-' && argv [ i ][ 1 ] == 'r' ) {
+ pnd_run = argv [ i + 1 ];
+ i++;
+ if ( ! pnd_run ) {
+ printf ( "-r specified, but no argument provided.\n" );
+ exit ( 0 );
+ }
+ } else if ( argv [ i ][ 0 ] == '-' && argv [ i ][ 1 ] == 'n' ) {
+ no_x11 = 1;
+ } else {
+
+ if ( argv [ i ][ 0 ] == '-' ) {
+ usage ( argv );
+ exit ( 0 );
+ } else if ( pndfile ) {
+ printf ( "Only one pndfile may be specified.\n" );
+ } else {
+ pndfile = argv [ i ];
+ }
+
+ }
+
+ } // for args
+
+ // if runscript was not specified on cmdline, attempt to pick it up from config
+ // ---> cribbed right out of discotest :/ copypaste ftw!
+ if ( ! pnd_run ) {
+ char *configpath;
+ char *appspath;
+ char *overridespath;
+
+ // attempt to fetch a sensible default searchpath for configs
+ configpath = pnd_conf_query_searchpath();
+
+ // attempt to fetch the apps config. since it finds us the runscript
+ pnd_conf_handle apph;
+
+ apph = pnd_conf_fetch_by_id ( pnd_conf_apps, configpath );
+
+ if ( apph ) {
+ appspath = pnd_conf_get_as_char ( apph, PND_APPS_KEY );
+
+ if ( ! appspath ) {
+ 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;
+ }
+
+ // given app-config, try to locate a runscript
+ char *run_searchpath;
+ char *run_script;
+ char *pndrun;
+
+ 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 );
+ }
+
+ // hand back to main proggy
+ pnd_run = pndrun; // lame, fix this
+
+ } // try to locate runscript
+
+ if ( ! pnd_run ) {
+ printf ( "Runscript could not be determined. Fail.\n" );
+ exit ( 0 );
+ }
+
+ if ( ! pndfile ) {
+ usage ( argv );
+ exit ( 0 );
+ }
+
+ // summary
+ printf ( "Runscript\t%s\n", pnd_run );
+ printf ( "Pndfile\t%s\n", pndfile );
+ printf ( "Kill X11\t%s\n", no_x11 ? "true" : "false" );
+
+ // sadly, to launch a pnd-file we need to know what the executable is in there
+ unsigned int pxmlbuflen = 96 * 1024; // lame, need to calculate it
+ char *pxmlbuf = malloc ( pxmlbuflen );
+ if ( ! pxmlbuf ) {
+ printf ( "ERROR: RAM exhausted!\n" );
+ exit ( 0 );
+ }
+ memset ( pxmlbuf, '\0', pxmlbuflen );
+
+ FILE *f = fopen ( pndfile, "r" );
+ if ( ! f ) {
+ printf ( "ERROR: Couldn't open pndfile %s!\n", pndfile );
+ exit ( 0 );
+ }
+
+ pnd_pxml_handle h = NULL;
+ if ( pnd_pnd_seek_pxml ( f ) ) {
+ if ( pnd_pnd_accrue_pxml ( f, pxmlbuf, pxmlbuflen ) ) {
+ h = pnd_pxml_fetch_buffer ( "pnd_run", pxmlbuf );
+ }
+ }
+
+ fclose ( f );
+
+ if ( ! h ) {
+ printf ( "ERROR: Couldn't pull PXML.xml from the pndfile.\n" );
+ exit ( 0 );
+ }
+
+ // attempt to invoke
+ unsigned int options = 0;
+ if ( no_x11 ) {
+ options |= PND_EXEC_OPTION_NOX11;
+ }
+
+ unsigned int clock = 200;
+ if ( pnd_pxml_get_clockspeed ( h ) ) {
+ clock = atoi ( pnd_pxml_get_clockspeed ( h ) );
+ }
+
+ if ( ! pnd_apps_exec ( pnd_run, pndfile,
+ pnd_pxml_get_unique_id ( h ),
+ pnd_pxml_get_exec ( h ),
+ pnd_pxml_get_startdir ( h ),
+ clock,
+ options )
+ )
+ {
+ printf ( "ERROR: PXML.xml data is bad\n" );
+ }
+
+ return ( 0 );
+} // main
#include "pnd_pxml.h"
#include "pnd_utility.h"
#include "pnd_desktop.h"
+#include "pnd_logger.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..
// op mode; emitting stdout or no?
static unsigned char g_daemon_mode = 0;
+typedef enum {
+ pndn_debug = 0,
+ pndn_rem, // will set default log level to here, so 'debug' is omitted
+ pndn_warning,
+ pndn_error,
+ pndn_none
+} pndnotify_loglevels_e;
+
// like discotest
char *configpath;
char *appspath;
// notifier handle
pnd_notify_handle nh = 0;
+// constants
+#define PNDNOTIFYD_LOGLEVEL "pndnotifyd.loglevel"
+
// decl's
void consume_configuration ( void );
void setup_notifications ( void );
exit ( 0 );
}
- }
+ } // for
- if ( ! g_daemon_mode ) {
- printf ( "Interval between checks is %u seconds\n", interval_secs );
+ /* enable logging?
+ */
+ if ( g_daemon_mode ) {
+ // nada
+ } else {
+ pnd_log_set_filter ( pndn_rem );
+ pnd_log_set_pretext ( "pndnotifyd" );
+ pnd_log_to_stdout();
+ pnd_log ( pndn_rem, "log level starting as %u", pnd_log_get_filter() );
}
+ pnd_log ( pndn_rem, "Interval between checks is %u seconds\n", interval_secs );
+
// basic daemon set up
if ( g_daemon_mode ) {
// detach from terminal
if ( ( i = fork() ) < 0 ) {
- printf ( "ERROR: Couldn't fork()\n" );
+ pnd_log ( pndn_error, "ERROR: Couldn't fork()\n" );
exit ( i );
}
if ( i ) {
/* 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 );
- printf ( ".desktop icon files emit to '%s'\n", iconpath );
- printf ( "Notify searchpath is '%s'\n", notifypath );
- }
+ pnd_log ( pndn_rem, "Apps searchpath is '%s'\n", appspath );
+ pnd_log ( pndn_rem, "PXML overrides searchpath is '%s'\n", overridespath );
+ pnd_log ( pndn_rem, ".desktop files emit to '%s'\n", dotdesktoppath );
+ pnd_log ( pndn_rem, ".desktop icon files emit to '%s'\n", iconpath );
+ pnd_log ( pndn_rem, "Notify searchpath is '%s'\n", notifypath );
/* set up signal handler
*/
// 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 ( "------------------------------------------------------\n" );
- printf ( "Changes within watched paths .. performing re-discover!\n" );
- }
+ pnd_log ( pndn_rem, "------------------------------------------------------\n" );
+ pnd_log ( pndn_rem, "Changes within watched paths .. performing re-discover!\n" );
// run the discovery
applist = pnd_disco_search ( appspath, overridespath );
while ( d ) {
- if ( ! g_daemon_mode ) {
- printf ( "Found app: %s\n", pnd_box_get_key ( d ) );
- }
+ pnd_log ( pndn_rem, "Found app: %s\n", pnd_box_get_key ( d ) );
// check if icon already exists (from a previous extraction say); if so, we needn't
// do it again
if ( stat ( existingpath, &dirs ) == 0 ) {
// icon seems to exist, so just crib the location into the .desktop
- if ( ! g_daemon_mode ) {
- printf ( " Found icon already existed, so reusing it! %s\n", existingpath );
- }
+ pnd_log ( pndn_rem, " Found icon already existed, so reusing it! %s\n", existingpath );
if ( d -> icon ) {
free ( d -> icon );
} else {
// icon seems unreadable or does not exist; lets try to create it..
- if ( ! g_daemon_mode ) {
- printf ( " Icon not already present, so trying to write it! %s\n", existingpath );
- }
+ pnd_log ( pndn_rem, " Icon not already present, so trying to write it! %s\n", existingpath );
// 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..)
}
d -> icon = strdup ( existingpath );
} else {
- if ( ! g_daemon_mode ) {
- printf ( " WARN: Couldn't write out icon %s\n", existingpath );
- }
+ pnd_log ( pndn_rem, " WARN: Couldn't write out icon %s\n", existingpath );
}
} // icon already exists?
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 ) );
- }
+ pnd_log ( pndn_rem, "ERROR: Error creating .desktop file for app: %s\n", pnd_box_get_key ( d ) );
}
// next!
} else {
- if ( ! g_daemon_mode ) {
- printf ( "No applications found in search path\n" );
- }
+ pnd_log ( pndn_rem, "No applications found in search path\n" );
} // got apps?
}
if ( source_libpnd ) {
#if 0
- if ( ! g_daemon_mode ) {
- printf ( "File '%s' appears to have been created by libpnd so candidate for delete: %u\n", buffer, source_libpnd );
- }
+ pnd_log ( pndn_rem,
+ "File '%s' appears to have been created by libpnd so candidate for delete: %u\n", buffer, source_libpnd );
#endif
} else {
#if 0
- if ( ! g_daemon_mode ) {
- printf ( "File '%s' appears NOT to have been created by libpnd, so leave it alone\n", buffer );
- }
+ pnd_log ( pndn_rem, "File '%s' appears NOT to have been created by libpnd, so leave it alone\n", buffer );
#endif
continue; // skip deleting it
}
if ( stat ( buffer, &dirs ) == 0 ) {
if ( dirs.st_mtime >= createtime ) {
#if 0
- if ( ! g_daemon_mode ) {
- printf ( "File '%s' seems 'new', so leave it alone.\n", buffer );
- }
+ pnd_log ( pndn_rem, "File '%s' seems 'new', so leave it alone.\n", buffer );
#endif
continue; // skip deleting it
}
// 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 nolonger relevent; removing it.\n", dirent -> d_name );
- }
+ pnd_log ( pndn_rem, "File '%s' seems nolonger relevent; removing it.\n", dirent -> d_name );
unlink ( buffer );
} // while getting filenames from dir
// if we've got a hup script located, lets invoke it
if ( pndhup ) {
- if ( ! g_daemon_mode ) {
- printf ( "Invoking hup script '%s'.\n", pndhup );
- }
+ pnd_log ( pndn_rem, "Invoking hup script '%s'.\n", pndhup );
pnd_exec_no_wait_1 ( pndhup, NULL );
}
run_script = PND_PNDRUN_FILENAME;
}
+ if ( pnd_conf_get_as_int ( apph, PNDNOTIFYD_LOGLEVEL ) != PND_CONF_BADNUM ) {
+ pnd_log_set_filter ( pnd_conf_get_as_int ( apph, PNDNOTIFYD_LOGLEVEL ) );
+ pnd_log ( pndn_rem, "config file causes loglevel to change to %u", pnd_log_get_filter() );
+ }
+
} else {
run_searchpath = NULL;
run_script = NULL;
}
// debug
- 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 ( "pndrun is %s\n", pndrun );
- if ( pndhup ) {
- printf ( "pndhup is %s\n", pndhup );
- } else {
- printf ( "No pndhup found (which is fine.)\n" );
- }
+ if ( run_searchpath ) pnd_log ( pndn_rem, "Locating pnd run in %s\n", run_searchpath );
+ if ( run_script ) pnd_log ( pndn_rem, "Locating pnd runscript as %s\n", run_script );
+ if ( pndrun ) pnd_log ( pndn_rem, "pndrun is %s\n", pndrun );
+ if ( pndhup ) {
+ pnd_log ( pndn_rem, "pndhup is %s\n", pndhup );
+ } else {
+ pnd_log ( pndn_rem, "No pndhup found (which is fine.)\n" );
}
/* handle globbing or variable substitution
}
if ( ! nh ) {
- if ( ! g_daemon_mode ) {
- printf ( "INOTIFY failed to init.\n" );
- }
+ pnd_log ( pndn_rem, "INOTIFY failed to init.\n" );
exit ( -1 );
}
#if 0
- if ( ! g_daemon_mode ) {
- printf ( "INOTIFY is up.\n" );
- }
+ pnd_log ( pndn_rem, "INOTIFY is up.\n" );
#endif
SEARCHPATH_PRE
{
- if ( ! g_daemon_mode ) {
- printf ( "Watching path '%s' and its descendents.\n", buffer );
- }
-
+ pnd_log ( pndn_rem, "Watching path '%s' and its descendents.\n", buffer );
pnd_notify_watch_path ( nh, buffer, PND_NOTIFY_RECURSE );
}
void sighup_handler ( int n ) {
- if ( ! g_daemon_mode ) {
- printf ( "---[ SIGHUP received ]---\n" );
- }
+ pnd_log ( pndn_rem, "---[ SIGHUP received ]---\n" );
// reparse config files
consume_configuration();
[pnd]
searchpath /media/*/pandora/scripts:/usr/pandora/scripts
runscript pnd_run.sh
+
+# [pndnotifyd] sets some extra pndnotifyd specific items
+[pndnotifyd]
+# logging level 0 means to include debug; level 1 (regular), 2 (warnings), 3 (errors)
+#loglevel 0
* NOTE: if specified, clock speed will be set prior to invoking the script, and set back on exit
* NOTE: No values can be except clockspeed; a 0 clockspeed means 'leave alone'. Set startdoir to "." instead of NULL.
* fork() is implied; calling this function does not kill this process :)
- * NOTE: PAss in the full path to the awesomeapp.pnd or to the directory containing PXML.xml (not the PXML.xml itself.)
+ * NOTE: Pass in the full path to the awesomeapp.pnd or to the directory containing PXML.xml (not the PXML.xml itself.)
* Options is a set of boolean flags, derived from the #define's below; OR them together.
* option-block, when set, suggests the launch should wait until the invoked application exits (disregarding why app exits)
* example: options = PND_EXEC_OPTION_BLOCK | PND_EXEC_OPTION_2;
* Returns NULL on error, otherwise a READ ONLY char* reference to the value.
*/
char *pnd_conf_get_as_char ( pnd_conf_handle c, char *key );
+#define PND_CONF_BADNUM (-31337) /* really lame hack, I know */
+int pnd_conf_get_as_int ( pnd_conf_handle c, char *key );
#ifdef __cplusplus
} /* "C" */
#define PND_DEVICE_SYS_BACKLIGHT_BRIGHTNESS "/sys/class/backlight/gpio-backlight/brightness"
#define PND_DEVICE_FRAMEBUFFER "/dev/fb0"
#define PND_DEVICE_NUB1 "/dev/input/js1"
-#define PND_DEVICE_NUB1 "/dev/input/js2"
+#define PND_DEVICE_NUB2 "/dev/input/js2"
/* utility
*/
--- /dev/null
+
+#ifndef h_pnd_logger_h
+#define h_pnd_logger_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+
+/* rudimentary logger; note that this is meant to be a no-brainer to use, so no setup is needed.
+ */
+#define PND_LOG_FORCE (0xFF) /* use this log level to require it be emitted, no skipping */
+
+// defaults will have no filtering, so any message will be emitted to all targets
+// default target is nothing, however, so logger is silent unless activated
+unsigned char pnd_log ( unsigned char level, char *format, ... ); // returns true if emitted; \n is implied!
+unsigned char pnd_log_to_stdout ( void ); // same as pnd_log_to_stream ( stdout );
+unsigned char pnd_log_to_stderr ( void ); // same as pnd_log_to_stream ( stderr );
+
+/* the below is all optional, for when you need more control
+ */
+
+// logging is additive; you can log to multiple targets at once. Returns 'true' if accepted, false if could not set up.
+void pnd_log_to_nil ( void ); // stop logging to anywhere; does not close streams/etc
+unsigned char pnd_log_to_stream ( FILE * ); // 'stdout', 'stderr', or your own FILE* are good values
+unsigned char pnd_log_to_syslog ( char *facility ); // NYI
+typedef void (*pnd_log_callback_f)( char *text, void *userdata );
+unsigned char pnd_log_to_callback ( pnd_log_callback_f f, void *userdata ); // NYI
+
+// pass NULL to free any pre-text, otherwise it'll be kept. Passed in string is duplicated, so you may free yours if you like.
+void pnd_log_set_pretext ( char * ); // example: your app-name, or app+function-names, say.
+
+// set a 'filter level'; any log message of higher-or-equal level than current filter-level will be emitted. Thus, to remove filters
+// just set to level 0. Returns existing setting.
+unsigned char pnd_log_set_filter ( unsigned char newlevel ); // ex: app-specific enum/#defines for your levels
+unsigned char pnd_log_get_filter ( void );
+
+// how many targets can be opened, entirely? this is a compile time limit, for sanity.
+unsigned char pnd_log_max_targets ( void );
+
+#ifdef __cplusplus
+} /* "C" */
+#endif
+
+#endif
char *pnd_conf_get_as_char ( pnd_conf_handle c, char *key ) {
return ( pnd_box_find_by_key ( c, key ) );
}
+
+int pnd_conf_get_as_int ( pnd_conf_handle c, char *key ) {
+ char *t = pnd_box_find_by_key ( c, key );
+
+ if ( ! t ) {
+ return ( PND_CONF_BADNUM ); // non-existant
+ }
+
+ int i = atoi ( t );
+
+ return ( i );
+}
return ( pnd_device_open_write_close ( PND_DEVICE_SYS_BACKLIGHT_BRIGHTNESS, buffer ) );
}
-unsigned int pnd_device_get_clock ( void ) {
+unsigned int pnd_device_get_backlight ( void ) {
char buffer [ 100 ];
if ( pnd_device_open_read_close ( PND_DEVICE_SYS_BACKLIGHT_BRIGHTNESS, buffer, 100 ) ) {
--- /dev/null
+
+#include <stdarg.h> // va-args
+#include <stdlib.h> // malloc/free
+#include <string.h> // strdup
+#include "pnd_logger.h"
+
+char *log_pretext = NULL;
+unsigned char log_filterlevel = 0;
+
+typedef enum {
+ pndl_nil = 0,
+ pndl_stream,
+ pndl_syslog,
+ pndl_callback
+} pnd_logger_e;
+
+typedef struct {
+ pnd_logger_e type;
+ union {
+ FILE *stream;
+ pnd_log_callback_f callback;
+ };
+} pnd_log_target_t;
+
+#define PND_LOG_MAX 5
+static pnd_log_target_t log_targets [ PND_LOG_MAX ]; // implicitly nil
+
+static int pnd_log_empty_slot ( void ) {
+ unsigned char i;
+
+ for ( i = 0; i < PND_LOG_MAX; i++ ) {
+ if ( log_targets [ i ].type == pndl_nil ) {
+ return ( i );
+ }
+ }
+
+ return ( -1 );
+}
+
+unsigned char pnd_log_set_filter ( unsigned char newlevel ) {
+ unsigned char foo = log_filterlevel;
+ log_filterlevel = newlevel;
+ return ( foo );
+}
+
+unsigned char pnd_log_get_filter ( void ) {
+ return ( log_filterlevel );
+}
+
+void pnd_log_set_pretext ( char *pre ) {
+
+ if ( log_pretext ) {
+ free ( log_pretext );
+ log_pretext = NULL;
+ }
+
+ if ( pre ) {
+ log_pretext = strdup ( pre );
+ }
+
+ return;
+}
+
+void pnd_log_to_nil ( void ) {
+ memset ( log_targets, '\0', sizeof(pnd_log_target_t) * PND_LOG_MAX );
+ return;
+}
+
+unsigned char pnd_log_to_stream ( FILE *f ) {
+ int i = pnd_log_empty_slot();
+
+ if ( i < 0 ) {
+ return ( 0 ); // fail!
+ }
+
+ log_targets [ i ].type = pndl_stream;
+ log_targets [ i ].stream = f;
+
+ return ( 1 );
+}
+
+unsigned char pnd_log_to_syslog ( char *facility ) {
+ return ( 0 ); // NYI
+}
+
+unsigned char pnd_log_to_callback ( pnd_log_callback_f f, void *userdata ) {
+ return ( 0 ); // NYI
+}
+
+unsigned char pnd_log_to_stdout ( void ) {
+ return ( pnd_log_to_stream ( stdout ) );
+}
+
+unsigned char pnd_log_to_stderr ( void ) {
+ return ( pnd_log_to_stream ( stderr ) );
+}
+
+unsigned char pnd_log_max_targets ( void ) {
+ return ( PND_LOG_MAX );
+}
+
+void pnd_log_emit ( char *message ) {
+ unsigned char i;
+
+ // iterate across targets and attempt to emit
+ for ( i = 0; i < PND_LOG_MAX; i++ ) {
+
+ switch ( log_targets [ i ].type ) {
+
+ case pndl_nil:
+ // nop
+ break;
+
+ case pndl_stream:
+ if ( log_pretext ) {
+ fprintf ( log_targets [ i ].stream, "%s\t", log_pretext );
+ }
+ if ( message ) {
+ fprintf ( log_targets [ i ].stream, "%s", message );
+ if ( strchr ( message, '\n' ) == NULL ) {
+ fprintf ( log_targets [ i ].stream, "\n" );
+ }
+ }
+ break;
+
+ case pndl_syslog:
+ // NYI
+ break;
+
+ case pndl_callback:
+ // NYI
+ break;
+
+ } // switch
+
+ } // for
+
+ return;
+}
+
+unsigned char pnd_log ( unsigned char level, char *fmt, ... ) {
+
+ if ( level == PND_LOG_FORCE ) {
+ // always proceed
+ } else if ( level < log_filterlevel ) {
+ return ( 0 ); // too low level
+ }
+
+ // format the actual log string
+ int n, size = 100;
+ char *p, *np;
+ va_list ap;
+
+ if ( ( p = malloc ( size ) ) == NULL ) {
+ return ( 0 ); // fail!
+ }
+
+ while ( 1 ) {
+
+ /* Try to print in the allocated space. */
+ va_start ( ap, fmt );
+ n = vsnprintf ( p, size, fmt, ap );
+ va_end ( ap );
+
+ /* If that worked, return the string. */
+ if ( n > -1 && n < size ) {
+ pnd_log_emit ( p );
+ break;
+ }
+
+ /* Else try again with more space. */
+ if ( n > -1 ) /* glibc 2.1 */
+ size = n + 1; /* precisely what is needed */
+ else /* glibc 2.0 */
+ size *= 2; /* twice the old size */
+ if ( ( np = realloc ( p, size ) ) == NULL ) {
+ free(p);
+ return ( 0 ); // fail!
+ } else {
+ p = np;
+ }
+
+ } // while
+
+ if ( p ) {
+ free ( p );
+ }
+
+ return ( 1 );
+}
--- /dev/null
+
+#include "pnd_logger.h"
+
+// arbitrary warning level
+#define PLOG_LOW 0
+#define PLOG_MEDIUM 1
+#define PLOG_HIGH 2
+
+int main ( void ) {
+
+ pnd_log ( PLOG_LOW, "low message, should be ignored" );
+
+ /* normal operation -------------- */
+ pnd_log_to_stdout();
+ pnd_log ( PLOG_LOW, "low message, should go stdout once" );
+ /* ------------------------------- */
+
+ /* extra testing vvvvvvvvvvvvvvvvv
+ */
+
+ pnd_log_to_stdout();
+ pnd_log ( PLOG_LOW, "low message, should go stdout twice" );
+
+ pnd_log_to_nil();
+ pnd_log_to_stdout();
+
+ pnd_log_set_pretext ( "loggertest" );
+ pnd_log ( PLOG_LOW, "low message, emit once, with pretext" );
+
+ pnd_log_set_filter ( PLOG_MEDIUM );
+ pnd_log ( PLOG_LOW, "low message, emit once, filter is medium+" );
+ pnd_log ( PLOG_MEDIUM, "medium message, emit once, filter is medium+" );
+ pnd_log ( PLOG_HIGH, "high message, emit once, filter is medium+" );
+
+ pnd_log_set_filter ( PLOG_LOW );
+ pnd_log ( PLOG_LOW, "low message, emit once, filter is low+ again" );
+
+ return ( 0 );
+}
[pnd]
searchpath /mnt/sd?/pandora/scripts:./testdata/scripts
runscript pnd_run.sh
+
+# [pndnotifyd] sets some extra pndnotifyd specific items
+[pndnotifyd]
+# logging level 0 means to include debug; level 1 (regular), 2 (warnings), 3 (errors)
+loglevel 0