2 @file osrf_router_main.c
3 @brief top level of OSRF Router
5 This top level loads a configuration file and forks into one or more child processes.
6 Each child process configures itself, daemonizes itself, and then (via a call to
7 osrfRouterRun()) goes into an infinite loop to route messages among clients and servers.
9 The first command-line parameter is the name of the configuration file.
11 The second command-line parameter is the context -- an XML tag identifying the subset
12 of the configuration file that is relevant to this application (since a configuration
13 file may include information for multiple applications).
15 Any subsequent command-line parameters are silently ignored.
19 #include <sys/types.h>
23 #include "opensrf/utils.h"
24 #include "opensrf/log.h"
25 #include "opensrf/osrf_list.h"
26 #include "opensrf/string_array.h"
27 #include "opensrf/osrfConfig.h"
28 #include "osrf_router.h"
30 static osrfRouter* router = NULL;
32 static volatile sig_atomic_t stop_signal = 0;
34 static void setupRouter( const jsonObject* configChunk, int configPos );
36 /* I think it's important these following things not be static */
37 pid_t* daemon_pid_list;
38 size_t daemon_pid_list_size;
41 void storeRouterDaemonPid( pid_t, int );
44 @brief Respond to signal by setting a switch that will interrupt the main loop.
45 @param signo The signal number.
47 Signal handler. We not only interrupt the main loop but also remember the signal
48 number so that we can report it later and re-raise it.
50 void routerSignalHandler( int signo ) {
52 signal( signo, routerSignalHandler );
53 router_stop( router );
58 @brief The top-level function of the router program.
59 @param argc Number of items in command line.
60 @param argv Pointer to array of items on command line.
61 @return System return code.
63 Load a configuration file, spawn zero or more child processes, and exit.
65 int main( int argc, char* argv[] ) {
68 osrfLogError( OSRF_LOG_MARK,
69 "Usage: %s <path_to_config_file> <config_context> [pid_file]",
74 const char* config_file = argv[1];
75 const char* context = argv[2];
76 char* pid_file = argc >= 4 ? strdup(argv[3]) : NULL;
78 /* Get a set of router definitions from a config file */
80 osrfConfig* cfg = osrfConfigInit(config_file, context);
82 osrfLogError( OSRF_LOG_MARK, "Router can't load config file %s", config_file );
86 osrfConfigSetDefaultConfig(cfg);
87 jsonObject* configInfo = osrfConfigGetValueObject(NULL, "/router");
89 if( configInfo->size < 1 || NULL == jsonObjectGetIndex( configInfo, 1 ) ) {
90 osrfLogError( OSRF_LOG_MARK, "No routers defined in config file %s, context \"%s\"",
91 config_file, context );
95 /* We're done with the command line now, so we can safely overlay it */
97 init_proc_title( argc, argv );
98 set_proc_title( "OpenSRF Router" );
100 /* Set up some shared memory so after some forking(), our children
101 * can tell us about all our grandchildren's PIDs, and we can write
104 daemon_pid_list_size = configInfo->size * sizeof(pid_t);
105 daemon_pid_list = mmap(
106 NULL, daemon_pid_list_size, PROT_READ | PROT_WRITE,
107 MAP_ANONYMOUS | MAP_SHARED, -1 , 0
110 if ( daemon_pid_list == MAP_FAILED ) {
113 "mmap() for router daemon PID list failed: %s",
116 exit( EXIT_FAILURE );
119 memset( daemon_pid_list, 0, daemon_pid_list_size );
121 /* Spawn child process(es) */
123 int rc = EXIT_SUCCESS;
124 int parent = 1; // boolean
129 for(i = 0; i < configInfo->size; i++) {
130 const jsonObject* configChunk = jsonObjectGetIndex( configInfo, i );
131 if( ! jsonObjectGetKeyConst( configChunk, "transport" ) )
133 // In searching the configuration file for a given context, we may have found a
134 // spurious hit on an unrelated part of the configuration file that happened to use
135 // the same XML tag. In fact this happens routinely in practice.
137 // If we don't see a member for "transport" then this is presumably such a spurious
138 // hit, so we silently ignore it.
140 // It is also possible that it's the right part of the configuration file but it has a
141 // typo or other such error, making it look spurious. In that case, well, too bad.
145 if(fork() == 0) { /* create a new child to run this router instance */
146 free( pid_file ); /* we don't need this as a child */
147 setupRouter(configChunk, i);
149 break; /* We're a child; don't spawn any more children here */
154 // Wait for all child processes to terminate; report their fates
155 while( 1 ) { // Loop until all children terminate
158 pid_t child_pid = wait( &status );
159 if( -1 == child_pid ) {
160 // ECHILD means no children are left. Anything else we ignore.
161 if( ECHILD == errno )
163 } else if( WIFEXITED( status ) ) {
164 // Relatively normal exit, i.e. via calling exit()
165 // or _exit(), or by returning from main()
166 int child_rc = WEXITSTATUS( status );
168 osrfLogWarning( OSRF_LOG_MARK,
169 "Child router process %ld exited with return status %d",
170 (long) child_pid, child_rc );
173 ; // Terminated successfully; silently ignore
175 } else if( WIFSIGNALED( status ) ) {
176 // Killed by a signal
177 int signo = WTERMSIG( status );
178 const char* extra = "";
180 if( WCOREDUMP( status ) )
181 extra = "with core dump ";
183 osrfLogWarning( OSRF_LOG_MARK, "Child router process %ld killed %sby signal %d",
184 (long) child_pid, extra, signo );
190 /* If rc is still EXIT_SUCCESS after the preceding loop,
191 * all our children have spawned grandchildren and will have
192 * reported their IDs via our shared memory daemon_pid_list
195 * A note about that list: it's going to have one pid_t-sized
196 * slot for every configInfo chunk in the config. Commonly,
197 * this code sees empty chunks or chunks that don't correspond
198 * to full router configs, so the slots for these unused chunks
199 * get left at zero. We skip those zeros both when writing the
200 * PID file and when reporting to the log.
202 if ( rc == EXIT_SUCCESS ) {
204 if ( (pid_fp = fopen(pid_file, "w")) ) {
205 for (i = 0; i < configInfo->size; i++) {
206 if ( daemon_pid_list[i] > 0 )
207 fprintf(pid_fp, "%d\n", daemon_pid_list[i]);
211 osrfLogWarning(OSRF_LOG_MARK,
212 "Tried to write PID file at %s but couldn't: %s",
213 pid_file, strerror(errno));
218 // for (i = 0; i < configInfo->size; i++) {
219 // if ( daemon_pid_list[i] > 0 ) {
220 // osrfLogInfo(OSRF_LOG_MARK,
221 // "Reporting a grandchild with PID %d", daemon_pid_list[i]);
225 munmap( daemon_pid_list, daemon_pid_list_size );
229 // Interrupted by a signal? Re-raise so the parent can see it.
230 osrfLogDebug(OSRF_LOG_MARK,
231 "Router received signal %d; re-raising", (int) stop_signal);
232 signal( stop_signal, SIG_DFL );
233 raise( stop_signal );
240 @brief Configure and run a child process.
241 @param configChunk Pointer to a subset of the loaded configuration.
242 @param configPos Index of configChunk in its original containing array, doubling as index for storing PID of child from daemonization (the original process' grandchild)
244 Configure oneself, daemonize, and then call osrfRouterRun() to go into a
245 near-endless loop. Return when interrupted by a signal, or when something goes wrong.
247 static void setupRouter( const jsonObject* configChunk, int configPos ) {
249 const jsonObject* transport_cfg = jsonObjectGetKeyConst( configChunk, "transport" );
251 const char* server = jsonObjectGetString( jsonObjectGetKeyConst( transport_cfg, "server" ));
252 const char* port = jsonObjectGetString( jsonObjectGetKeyConst( transport_cfg, "port" ));
253 const char* username = jsonObjectGetString( jsonObjectGetKeyConst( transport_cfg, "username" ));
254 const char* password = jsonObjectGetString( jsonObjectGetKeyConst( transport_cfg, "password" ));
255 const char* resource = jsonObjectGetString( jsonObjectGetKeyConst( transport_cfg, "resource" ));
257 const char* level = jsonObjectGetString( jsonObjectGetKeyConst( configChunk, "loglevel" ));
258 const char* log_file = jsonObjectGetString( jsonObjectGetKeyConst( configChunk, "logfile" ));
259 const char* facility = jsonObjectGetString( jsonObjectGetKeyConst( configChunk, "syslog" ));
262 if(level) llevel = atoi(level);
266 osrfLogError( OSRF_LOG_MARK, "Log file name not specified for router" );
270 if(!strcmp(log_file, "syslog")) {
271 osrfLogInit( OSRF_LOG_TYPE_SYSLOG, "router", llevel );
272 osrfLogSetSyslogFacility(osrfLogFacilityToInt(facility));
275 osrfLogInit( OSRF_LOG_TYPE_FILE, "router", llevel );
276 osrfLogSetFile( log_file );
279 osrfLogInfo( OSRF_LOG_MARK, "Router connecting as: server: %s port: %s "
280 "user: %s resource: %s", server, port, username, resource );
284 iport = atoi( port );
286 osrfStringArray* tclients = osrfNewStringArray(4);
287 osrfStringArray* tservers = osrfNewStringArray(4);
289 jsonObject* tclientsList = jsonObjectFindPath(configChunk, "/trusted_domains/client");
290 jsonObject* tserversList = jsonObjectFindPath(configChunk, "/trusted_domains/server");
294 if(tserversList->type == JSON_ARRAY) {
295 for( i = 0; i != tserversList->size; i++ ) {
296 const char* serverDomain = jsonObjectGetString(jsonObjectGetIndex(tserversList, i));
297 osrfLogInfo( OSRF_LOG_MARK, "Router adding trusted server: %s", serverDomain);
298 osrfStringArrayAdd(tservers, serverDomain);
301 const char* serverDomain = jsonObjectGetString(tserversList);
302 osrfLogInfo( OSRF_LOG_MARK, "Router adding trusted server: %s", serverDomain);
303 osrfStringArrayAdd(tservers, serverDomain);
306 if(tclientsList->type == JSON_ARRAY) {
307 for( i = 0; i != tclientsList->size; i++ ) {
308 const char* clientDomain = jsonObjectGetString(jsonObjectGetIndex(tclientsList, i));
309 osrfLogInfo( OSRF_LOG_MARK, "Router adding trusted client: %s", clientDomain);
310 osrfStringArrayAdd(tclients, clientDomain);
313 const char* clientDomain = jsonObjectGetString(tclientsList);
314 osrfLogInfo( OSRF_LOG_MARK, "Router adding trusted client: %s", clientDomain);
315 osrfStringArrayAdd(tclients, clientDomain);
319 if( tclients->size == 0 || tservers->size == 0 ) {
320 osrfLogError( OSRF_LOG_MARK,
321 "We need trusted servers and trusted client to run the router...");
322 osrfStringArrayFree( tservers );
323 osrfStringArrayFree( tclients );
327 router = osrfNewRouter( server,
328 username, resource, password, iport, tclients, tservers );
330 signal(SIGHUP,routerSignalHandler);
331 signal(SIGINT,routerSignalHandler);
332 signal(SIGTERM,routerSignalHandler);
334 if( (osrfRouterConnect(router)) != 0 ) {
335 osrfLogError( OSRF_LOG_MARK, "Unable to connect router to jabber server %s... exiting",
337 osrfRouterFree(router);
341 // Done configuring? Let's get to work.
343 daemonizeWithCallback( storeRouterDaemonPid, configPos );
344 osrfRouterRun( router );
346 osrfRouterFree(router);
348 osrfLogInfo( OSRF_LOG_MARK, "Router freed" );
353 void storeRouterDaemonPid( pid_t p, int i ) {
355 daemon_pid_list[i] = p;