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* log_tag = jsonObjectGetString( jsonObjectGetKeyConst( configChunk, "logtag" ));
260 const char* facility = jsonObjectGetString( jsonObjectGetKeyConst( configChunk, "syslog" ));
263 if(level) llevel = atoi(level);
267 osrfLogError( OSRF_LOG_MARK, "Log file name not specified for router" );
271 if(!strcmp(log_file, "syslog")) {
272 if(log_tag) osrfLogSetLogTag(log_tag);
273 osrfLogInit( OSRF_LOG_TYPE_SYSLOG, "router", llevel );
274 osrfLogSetSyslogFacility(osrfLogFacilityToInt(facility));
277 osrfLogInit( OSRF_LOG_TYPE_FILE, "router", llevel );
278 osrfLogSetFile( log_file );
281 osrfLogInfo( OSRF_LOG_MARK, "Router connecting as: server: %s port: %s "
282 "user: %s resource: %s", server, port, username, resource );
286 iport = atoi( port );
288 osrfStringArray* tclients = osrfNewStringArray(4);
289 osrfStringArray* tservers = osrfNewStringArray(4);
291 jsonObject* tclientsList = jsonObjectFindPath(configChunk, "/trusted_domains/client");
292 jsonObject* tserversList = jsonObjectFindPath(configChunk, "/trusted_domains/server");
296 if(tserversList->type == JSON_ARRAY) {
297 for( i = 0; i != tserversList->size; i++ ) {
298 const char* serverDomain = jsonObjectGetString(jsonObjectGetIndex(tserversList, i));
299 osrfLogInfo( OSRF_LOG_MARK, "Router adding trusted server: %s", serverDomain);
300 osrfStringArrayAdd(tservers, serverDomain);
303 const char* serverDomain = jsonObjectGetString(tserversList);
304 osrfLogInfo( OSRF_LOG_MARK, "Router adding trusted server: %s", serverDomain);
305 osrfStringArrayAdd(tservers, serverDomain);
308 if(tclientsList->type == JSON_ARRAY) {
309 for( i = 0; i != tclientsList->size; i++ ) {
310 const char* clientDomain = jsonObjectGetString(jsonObjectGetIndex(tclientsList, i));
311 osrfLogInfo( OSRF_LOG_MARK, "Router adding trusted client: %s", clientDomain);
312 osrfStringArrayAdd(tclients, clientDomain);
315 const char* clientDomain = jsonObjectGetString(tclientsList);
316 osrfLogInfo( OSRF_LOG_MARK, "Router adding trusted client: %s", clientDomain);
317 osrfStringArrayAdd(tclients, clientDomain);
321 if( tclients->size == 0 || tservers->size == 0 ) {
322 osrfLogError( OSRF_LOG_MARK,
323 "We need trusted servers and trusted client to run the router...");
324 osrfStringArrayFree( tservers );
325 osrfStringArrayFree( tclients );
329 router = osrfNewRouter( server,
330 username, resource, password, iport, tclients, tservers );
332 signal(SIGHUP,routerSignalHandler);
333 signal(SIGINT,routerSignalHandler);
334 signal(SIGTERM,routerSignalHandler);
336 if( (osrfRouterConnect(router)) != 0 ) {
337 osrfLogError( OSRF_LOG_MARK, "Unable to connect router to jabber server %s... exiting",
339 osrfRouterFree(router);
343 // Done configuring? Let's get to work.
345 daemonizeWithCallback( storeRouterDaemonPid, configPos );
346 osrfRouterRun( router );
348 osrfRouterFree(router);
350 osrfLogInfo( OSRF_LOG_MARK, "Router freed" );
355 void storeRouterDaemonPid( pid_t p, int i ) {
357 daemon_pid_list[i] = p;