0f262eb050ff44f9681432ef361ef1c1837dc125
[OpenSRF.git] / src / libopensrf / osrf_system.c
1 #include <sys/types.h>
2 #include <sys/time.h>
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <sys/select.h>
8 #include <sys/wait.h>
9 #include <signal.h>
10
11 #include "opensrf/utils.h"
12 #include "opensrf/log.h"
13 #include "opensrf/osrf_system.h"
14 #include "opensrf/osrf_application.h"
15 #include "opensrf/osrf_prefork.h"
16
17 #ifndef HOST_NAME_MAX
18 #define HOST_NAME_MAX 256
19 #endif
20
21 static void report_child_status( pid_t pid, int status );
22 struct child_node;
23 typedef struct child_node ChildNode;
24
25 static void handleKillSignal(int signo) {
26     /* we are the top-level process and we've been 
27      * killed. Kill all of our children */
28     kill(0, SIGTERM);
29     sleep(1); /* give the children a chance to die before we reap them */
30     pid_t child_pid;
31     int status;
32     while( (child_pid=waitpid(-1,&status,WNOHANG)) > 0) 
33         osrfLogInfo(OSRF_LOG_MARK, "Killed child %d", child_pid);
34     _exit(0);
35 }
36
37
38 struct child_node
39 {
40         ChildNode* pNext;
41         ChildNode* pPrev;
42         pid_t pid;
43         char* app;
44         char* libfile;
45 };
46
47 static ChildNode* child_list;
48
49 static transport_client* osrfGlobalTransportClient = NULL;
50
51 static void add_child( pid_t pid, const char* app, const char* libfile );
52 static void delete_child( ChildNode* node );
53 static void delete_all_children( void );
54 static ChildNode* seek_child( pid_t pid );
55
56 transport_client* osrfSystemGetTransportClient( void ) {
57         return osrfGlobalTransportClient;
58 }
59
60 void osrfSystemIgnoreTransportClient() {
61         osrfGlobalTransportClient = NULL;
62 }
63
64 int osrf_system_bootstrap_client( char* config_file, char* contextnode ) {
65         return osrfSystemBootstrapClientResc(config_file, contextnode, NULL);
66 }
67
68 int osrfSystemInitCache( void ) {
69
70         jsonObject* cacheServers = osrf_settings_host_value_object("/cache/global/servers/server");
71         char* maxCache = osrf_settings_host_value("/cache/global/max_cache_time");
72
73         if( cacheServers && maxCache) {
74
75                 if( cacheServers->type == JSON_ARRAY ) {
76                         int i;
77                         const char* servers[cacheServers->size];
78                         for( i = 0; i != cacheServers->size; i++ ) {
79                                 servers[i] = jsonObjectGetString( jsonObjectGetIndex(cacheServers, i) );
80                                 osrfLogInfo( OSRF_LOG_MARK, "Adding cache server %s", servers[i]);
81                         }
82                         osrfCacheInit( servers, cacheServers->size, atoi(maxCache) );
83
84                 } else {
85                         const char* servers[] = { jsonObjectGetString(cacheServers) };          
86                         osrfLogInfo( OSRF_LOG_MARK, "Adding cache server %s", servers[0]);
87                         osrfCacheInit( servers, 1, atoi(maxCache) );
88                 }
89
90         } else {
91                 osrfLogError( OSRF_LOG_MARK,  "Missing config value for /cache/global/servers/server _or_ "
92                         "/cache/global/max_cache_time");
93         }
94
95         jsonObjectFree( cacheServers );
96         return 0;
97 }
98
99
100 /**
101         @brief Set yourself up for business.
102         @param hostname Full network name of the host where the process is running; or 
103         'localhost' will do.
104         @param configfile Name of the configuration file; normally '/openils/conf/opensrf_core.xml'.
105         @param contextNode 
106         @return - Name of an aggregate within the configuration file, containing the relevant
107         subset of configuration stuff.
108
109         
110 */
111 int osrfSystemBootstrap( const char* hostname, const char* configfile,
112                 const char* contextNode ) {
113         if( !(hostname && configfile && contextNode) ) return -1;
114
115         /* first we grab the settings */
116         if(!osrfSystemBootstrapClientResc(configfile, contextNode, "settings_grabber" )) {
117                 osrfLogError( OSRF_LOG_MARK,
118                         "Unable to bootstrap for host %s from configuration file %s",
119                         hostname, configfile );
120                 return -1;
121         }
122
123         int retcode = osrf_settings_retrieve(hostname);
124         osrf_system_disconnect_client();
125
126         if( retcode ) {
127                 osrfLogError( OSRF_LOG_MARK,
128                         "Unable to retrieve settings for host %s from configuration file %s",
129                         hostname, configfile );
130                 return -1;
131         }
132
133         // Turn into a daemon.  The parent forks and exits.  Only the
134         // child returns, with the standard streams (stdin, stdout, and
135         // stderr) redirected to /dev/null.
136         /* NOTE: This has been moved from below the 'if (apps)' block below ... move it back if things go crazy */
137         daemonize();
138
139         jsonObject* apps = osrf_settings_host_value_object("/activeapps/appname");
140         osrfStringArray* arr = osrfNewStringArray(8);
141         
142         if(apps) {
143                 int i = 0;
144
145                 if(apps->type == JSON_STRING) {
146                         osrfStringArrayAdd(arr, jsonObjectGetString(apps));
147
148                 } else {
149                         const jsonObject* app;
150                         while( (app = jsonObjectGetIndex(apps, i++)) ) 
151                                 osrfStringArrayAdd(arr, jsonObjectGetString(app));
152                 }
153                 jsonObjectFree(apps);
154
155                 const char* appname = NULL;
156                 i = 0;
157                 while( (appname = osrfStringArrayGetString(arr, i++)) ) {
158
159                         char* lang = osrf_settings_host_value("/apps/%s/language", appname);
160
161                         if(lang && !strcasecmp(lang,"c"))  {
162
163                                 char* libfile = osrf_settings_host_value("/apps/%s/implementation", appname);
164                 
165                                 if(! (appname && libfile) ) {
166                                         osrfLogWarning( OSRF_LOG_MARK, "Missing appname / libfile in settings config");
167                                         continue;
168                                 }
169
170                                 osrfLogInfo( OSRF_LOG_MARK, "Launching application %s with implementation %s", appname, libfile);
171                 
172                                 pid_t pid;
173                 
174                                 if( (pid = fork()) ) { 
175                                         // store pid in local list for re-launching dead children...
176                                         add_child( pid, appname, libfile );
177                                         osrfLogInfo( OSRF_LOG_MARK, "Running application child %s: process id %ld",
178                                                                  appname, (long) pid );
179         
180                                 } else {
181                 
182                                         osrfLogInfo( OSRF_LOG_MARK, " * Running application %s\n", appname);
183                                         if( osrfAppRegisterApplication( appname, libfile ) == 0 ) 
184                                                 osrf_prefork_run(appname);
185         
186                                         osrfLogDebug( OSRF_LOG_MARK, "Server exiting for app %s and library %s\n", appname, libfile );
187                                         exit(0);
188                                 }
189                         } // language == c
190                 } 
191         } // should we do something if there are no apps? does the wait(NULL) below do that for us?
192
193         osrfStringArrayFree(arr);
194
195     signal(SIGTERM, handleKillSignal);
196     signal(SIGINT, handleKillSignal);
197         
198         while(1) {
199                 errno = 0;
200                 int status;
201                 pid_t pid = wait( &status );
202                 if(-1 == pid) {
203                         if(errno == ECHILD)
204                                 osrfLogError(OSRF_LOG_MARK, "We have no more live services... exiting");
205                         else
206                                 osrfLogError(OSRF_LOG_MARK, "Exiting top-level system loop with error: %s", strerror(errno));
207                         break;
208                 } else {
209                         report_child_status( pid, status );
210                 }
211         }
212
213         delete_all_children();
214         return 0;
215 }
216
217
218 static void report_child_status( pid_t pid, int status )
219 {
220         const char* app;
221         const char* libfile;
222         ChildNode* node = seek_child( pid );
223
224         if( node ) {
225                 app     = node->app     ? node->app     : "[unknown]";
226                 libfile = node->libfile ? node->libfile : "[none]";
227         } else
228                 app = libfile = NULL;
229         
230         if( WIFEXITED( status ) )
231         {
232                 int rc = WEXITSTATUS( status );  // return code of child process
233                 if( rc )
234                         osrfLogError( OSRF_LOG_MARK, "Child process %ld (app %s) exited with return code %d",
235                                                   (long) pid, app, rc );
236                 else
237                         osrfLogInfo( OSRF_LOG_MARK, "Child process %ld (app %s) exited normally",
238                                                   (long) pid, app );
239         }
240         else if( WIFSIGNALED( status ) )
241         {
242                 osrfLogError( OSRF_LOG_MARK, "Child process %ld (app %s) killed by signal %d",
243                                           (long) pid, app, WTERMSIG( status) );
244         }
245         else if( WIFSTOPPED( status ) )
246         {
247                 osrfLogError( OSRF_LOG_MARK, "Child process %ld (app %s) stopped by signal %d",
248                                           (long) pid, app, (int) WSTOPSIG( status ) );
249         }
250
251         delete_child( node );
252 }
253
254 /*----------- Routines to manage list of children --*/
255
256 static void add_child( pid_t pid, const char* app, const char* libfile )
257 {
258         /* Construct new child node */
259         
260         ChildNode* node = safe_malloc( sizeof( ChildNode ) );
261
262         node->pid = pid;
263
264         if( app )
265                 node->app = strdup( app );
266         else
267                 node->app = NULL;
268
269         if( libfile )
270                 node->libfile = strdup( libfile );
271         else
272                 node->libfile = NULL;
273         
274         /* Add new child node to the head of the list */
275
276         node->pNext = child_list;
277         node->pPrev = NULL;
278
279         if( child_list )
280                 child_list->pPrev = node;
281
282         child_list = node;
283 }
284
285 static void delete_child( ChildNode* node ) {
286
287         /* Sanity check */
288
289         if( ! node )
290                 return;
291         
292         /* Detach the node from the list */
293
294         if( node->pPrev )
295                 node->pPrev->pNext = node->pNext;
296         else
297                 child_list = node->pNext;
298
299         if( node->pNext )
300                 node->pNext->pPrev = node->pPrev;
301
302         /* Deallocate the node and its payload */
303
304         free( node->app );
305         free( node->libfile );
306         free( node );
307 }
308
309 static void delete_all_children( void ) {
310
311         while( child_list )
312                 delete_child( child_list );
313 }
314
315 static ChildNode* seek_child( pid_t pid ) {
316
317         /* Return a pointer to the child node for the */
318         /* specified process ID, or NULL if not found */
319         
320         ChildNode* node = child_list;
321         while( node ) {
322                 if( node->pid == pid )
323                         break;
324                 else
325                         node = node->pNext;
326         }
327
328         return node;
329 }
330
331 /*----------- End of routines to manage list of children --*/
332
333
334 int osrfSystemBootstrapClientResc( const char* config_file,
335                 const char* contextnode, const char* resource ) {
336
337         int failure = 0;
338
339         if(osrfSystemGetTransportClient()) {
340                 osrfLogInfo(OSRF_LOG_MARK, "Client is already bootstrapped");
341                 return 1; /* we already have a client connection */
342         }
343
344         if( !( config_file && contextnode ) && ! osrfConfigHasDefaultConfig() ) {
345                 osrfLogError( OSRF_LOG_MARK, "No Config File Specified\n" );
346                 return -1;
347         }
348
349         if( config_file ) {
350                 osrfConfig* cfg = osrfConfigInit( config_file, contextnode );
351                 if(cfg)
352                         osrfConfigSetDefaultConfig(cfg);
353                 else
354                         return 0;   /* Can't load configuration?  Bail out */
355         }
356
357
358         char* log_file          = osrfConfigGetValue( NULL, "/logfile");
359         if(!log_file) {
360                 fprintf(stderr, "No log file specified in configuration file %s\n",
361                                 config_file);
362                 return -1;
363         }
364
365         char* log_level         = osrfConfigGetValue( NULL, "/loglevel" );
366         osrfStringArray* arr    = osrfNewStringArray(8);
367         osrfConfigGetValueList(NULL, arr, "/domain");
368
369         char* username          = osrfConfigGetValue( NULL, "/username" );
370         char* password          = osrfConfigGetValue( NULL, "/passwd" );
371         char* port              = osrfConfigGetValue( NULL, "/port" );
372         char* unixpath          = osrfConfigGetValue( NULL, "/unixpath" );
373         char* facility          = osrfConfigGetValue( NULL, "/syslog" );
374         char* actlog            = osrfConfigGetValue( NULL, "/actlog" );
375
376         /* if we're a source-client, tell the logger */
377         char* isclient = osrfConfigGetValue(NULL, "/client");
378         if( isclient && !strcasecmp(isclient,"true") )
379                 osrfLogSetIsClient(1);
380         free(isclient);
381
382         int llevel = 0;
383         int iport = 0;
384         if(port) iport = atoi(port);
385         if(log_level) llevel = atoi(log_level);
386
387         if(!strcmp(log_file, "syslog")) {
388                 osrfLogInit( OSRF_LOG_TYPE_SYSLOG, contextnode, llevel );
389                 osrfLogSetSyslogFacility(osrfLogFacilityToInt(facility));
390                 if(actlog) osrfLogSetSyslogActFacility(osrfLogFacilityToInt(actlog));
391
392         } else {
393                 osrfLogInit( OSRF_LOG_TYPE_FILE, contextnode, llevel );
394                 osrfLogSetFile( log_file );
395         }
396
397
398         /* Get a domain, if one is specified */
399         const char* domain = osrfStringArrayGetString( arr, 0 ); /* just the first for now */
400         if(!domain) {
401                 fprintf(stderr, "No domain specified in configuration file %s\n", config_file);
402                 osrfLogError( OSRF_LOG_MARK, "No domain specified in configuration file %s\n", config_file);
403                 failure = 1;
404         }
405
406         if(!username) {
407                 fprintf(stderr, "No username specified in configuration file %s\n", config_file);
408                 osrfLogError( OSRF_LOG_MARK, "No username specified in configuration file %s\n", config_file);
409                 failure = 1;
410         }
411
412         if(!password) {
413                 fprintf(stderr, "No password specified in configuration file %s\n", config_file);
414                 osrfLogError( OSRF_LOG_MARK, "No password specified in configuration file %s\n", config_file);
415                 failure = 1;
416         }
417
418         if((iport <= 0) && !unixpath) {
419                 fprintf(stderr, "No unixpath or valid port in configuration file %s\n", config_file);
420                 osrfLogError( OSRF_LOG_MARK, "No unixpath or valid port in configuration file %s\n",
421                         config_file);
422                 failure = 1;
423         }
424
425         if (failure) {
426                 osrfStringArrayFree(arr);
427                 free(log_file);
428                 free(log_level);
429                 free(username);
430                 free(password);
431                 free(port);
432                 free(unixpath);
433                 free(facility);
434                 free(actlog);
435                 return 0;
436         }
437
438         osrfLogInfo( OSRF_LOG_MARK, "Bootstrapping system with domain %s, port %d, and unixpath %s",
439                 domain, iport, unixpath ? unixpath : "(none)" );
440         transport_client* client = client_init( domain, iport, unixpath, 0 );
441
442         char host[HOST_NAME_MAX + 1] = "";
443         gethostname(host, sizeof(host) );
444         host[HOST_NAME_MAX] = '\0';
445
446         char tbuf[32];
447         tbuf[0] = '\0';
448         snprintf(tbuf, 32, "%f", get_timestamp_millis());
449
450         if(!resource) resource = "";
451
452         int len = strlen(resource) + 256;
453         char buf[len];
454         buf[0] = '\0';
455         snprintf(buf, len - 1, "%s_%s_%s_%ld", resource, host, tbuf, (long) getpid() );
456
457         if(client_connect( client, username, password, buf, 10, AUTH_DIGEST )) {
458                 /* child nodes will leak the parents client... but we can't free
459                         it without disconnecting the parents client :( */
460                 osrfGlobalTransportClient = client;
461         }
462
463         osrfStringArrayFree(arr);
464         free(actlog);
465         free(facility);
466         free(log_level);
467         free(log_file);
468         free(username);
469         free(password);
470         free(port);     
471         free(unixpath);
472
473         if(osrfGlobalTransportClient)
474                 return 1;
475
476         return 0;
477 }
478
479 int osrf_system_disconnect_client( void ) {
480         client_disconnect( osrfGlobalTransportClient );
481         client_free( osrfGlobalTransportClient );
482         osrfGlobalTransportClient = NULL;
483         return 0;
484 }
485
486 static int shutdownComplete = 0;
487 int osrf_system_shutdown( void ) {
488     if(shutdownComplete) return 0;
489         osrfConfigCleanup();
490     osrfCacheCleanup();
491         osrf_system_disconnect_client();
492         osrf_settings_free_host_config(NULL);
493         osrfAppSessionCleanup();
494         osrfLogCleanup();
495     shutdownComplete = 1;
496         return 1;
497 }
498
499
500
501