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>
22 #include "opensrf/utils.h"
23 #include "opensrf/log.h"
24 #include "opensrf/osrf_list.h"
25 #include "opensrf/string_array.h"
26 #include "opensrf/osrfConfig.h"
27 #include "osrf_router.h"
29 static osrfRouter* router = NULL;
31 static volatile sig_atomic_t stop_signal = 0;
33 static void setupRouter(jsonObject* configChunk);
36 @brief Respond to signal by setting a switch that will interrupt the main loop.
37 @param signo The signal number.
39 Signal handler. We not only interrupt the main loop but also remember the signal
40 number so that we can report it later and re-raise it.
42 void routerSignalHandler( int signo ) {
44 signal( signo, routerSignalHandler );
45 router_stop( router );
50 @brief The top-level function of the router program.
51 @param argc Number of items in command line.
52 @param argv Pointer to array of items on command line.
53 @return System return code.
55 Load a configuration file, spawn zero or more child processes, and exit.
57 int main( int argc, char* argv[] ) {
60 osrfLogError( OSRF_LOG_MARK,
61 "Usage: %s <path_to_config_file> <config_context>", argv[0] );
65 const char* config_file = argv[1];
66 const char* context = argv[2];
68 /* Get a set of router definitions from a config file */
70 osrfConfig* cfg = osrfConfigInit(config_file, context);
72 osrfLogError( OSRF_LOG_MARK, "Router can't load config file %s", config_file );
76 osrfConfigSetDefaultConfig(cfg);
77 jsonObject* configInfo = osrfConfigGetValueObject(NULL, "/router");
79 if( configInfo->size < 1 || NULL == jsonObjectGetIndex( configInfo, 1 ) ) {
80 osrfLogError( OSRF_LOG_MARK, "No routers defined in config file %s, context \"%s\"",
81 config_file, context );
85 /* We're done with the command line now, so we can safely overlay it */
87 init_proc_title( argc, argv );
88 set_proc_title( "OpenSRF Router" );
90 /* Spawn child process(es) */
92 int rc = EXIT_SUCCESS;
93 int parent = 1; // boolean
95 for(i = 0; i < configInfo->size; i++) {
96 jsonObject* configChunk = jsonObjectGetIndex(configInfo, i);
97 if( ! jsonObjectGetKey( configChunk, "transport" ) )
99 // In searching the configuration file for a given context, we may have found a
100 // spurious hit on an unrelated part of the configuration file that happened to use
101 // the same XML tag. In fact this happens routinely in practice.
103 // If we don't see a member for "transport" then this is presumably such a spurious
104 // hit, so we silently ignore it.
106 // It is also possible that it's the right part of the configuration file but it has a
107 // typo or other such error, making it look spurious. In that case, well, too bad.
110 if(fork() == 0) { /* create a new child to run this router instance */
111 setupRouter(configChunk);
113 break; /* We're a child; don't spawn any more children here */
118 // Wait for all child processes to terminate.
119 // If any ended abnormally, report it.
120 while( 1 ) { // Loop until all children terminate
123 pid_t child_pid = wait( &status );
124 if( -1 == child_pid ) {
125 // ECHILD means no children are left. Anything else we ignore.
126 if( ECHILD == errno )
128 } else if( WIFEXITED( status ) ) {
129 // Relatively normal exit, i.e. via calling exit()
130 // or _exit(), or by returning from main()
131 int child_rc = WEXITSTATUS( status );
133 osrfLogWarning( OSRF_LOG_MARK,
134 "Child router process %ld exited with return status %d",
135 (long) child_pid, child_rc );
138 ; // Terminated successfully; silently ignore
140 } else if( WIFSIGNALED( status ) ) {
141 // Killed by a signal
142 int signo = WTERMSIG( status );
143 const char* extra = "";
145 if( WCOREDUMP( status ) )
146 extra = "with core dump ";
148 osrfLogWarning( OSRF_LOG_MARK, "Child router process %ld killed %sby signal %d",
149 (long) child_pid, extra, signo );
157 // Interrupted by a signal? Re-raise so the parent can see it.
158 osrfLogWarning( OSRF_LOG_MARK, "Interrupted by signal %d; re-raising",
160 signal( stop_signal, SIG_DFL );
161 raise( stop_signal );
168 @brief Configure and run a child process.
169 @param configChunk Pointer to a subset of the loaded configuration.
171 Configure oneself, daemonize, and then call osrfRouterRun() to go into a
172 near-endless loop. Return when interrupted by a signal, or when something goes wrong.
174 static void setupRouter(jsonObject* configChunk) {
176 jsonObject* transport_cfg = jsonObjectGetKey( configChunk, "transport" );
178 const char* server = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "server" ) );
179 const char* port = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "port" ) );
180 const char* username = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "username" ) );
181 const char* password = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "password" ) );
182 const char* resource = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "resource" ) );
184 const char* level = jsonObjectGetString( jsonObjectGetKey( configChunk, "loglevel" ) );
185 const char* log_file = jsonObjectGetString( jsonObjectGetKey( configChunk, "logfile" ) );
186 const char* facility = jsonObjectGetString( jsonObjectGetKey( configChunk, "syslog" ) );
189 if(level) llevel = atoi(level);
193 osrfLogError( OSRF_LOG_MARK, "Log file name not specified for router" );
197 if(!strcmp(log_file, "syslog")) {
198 osrfLogInit( OSRF_LOG_TYPE_SYSLOG, "router", llevel );
199 osrfLogSetSyslogFacility(osrfLogFacilityToInt(facility));
202 osrfLogInit( OSRF_LOG_TYPE_FILE, "router", llevel );
203 osrfLogSetFile( log_file );
206 osrfLogInfo( OSRF_LOG_MARK, "Router connecting as: server: %s port: %s "
207 "user: %s resource: %s", server, port, username, resource );
211 iport = atoi( port );
213 osrfStringArray* tclients = osrfNewStringArray(4);
214 osrfStringArray* tservers = osrfNewStringArray(4);
216 jsonObject* tclientsList = jsonObjectFindPath(configChunk, "/trusted_domains/client");
217 jsonObject* tserversList = jsonObjectFindPath(configChunk, "/trusted_domains/server");
221 if(tserversList->type == JSON_ARRAY) {
222 for( i = 0; i != tserversList->size; i++ ) {
223 const char* serverDomain = jsonObjectGetString(jsonObjectGetIndex(tserversList, i));
224 osrfLogInfo( OSRF_LOG_MARK, "Router adding trusted server: %s", serverDomain);
225 osrfStringArrayAdd(tservers, serverDomain);
228 const char* serverDomain = jsonObjectGetString(tserversList);
229 osrfLogInfo( OSRF_LOG_MARK, "Router adding trusted server: %s", serverDomain);
230 osrfStringArrayAdd(tservers, serverDomain);
233 if(tclientsList->type == JSON_ARRAY) {
234 for( i = 0; i != tclientsList->size; i++ ) {
235 const char* clientDomain = jsonObjectGetString(jsonObjectGetIndex(tclientsList, i));
236 osrfLogInfo( OSRF_LOG_MARK, "Router adding trusted client: %s", clientDomain);
237 osrfStringArrayAdd(tclients, clientDomain);
240 const char* clientDomain = jsonObjectGetString(tclientsList);
241 osrfLogInfo( OSRF_LOG_MARK, "Router adding trusted client: %s", clientDomain);
242 osrfStringArrayAdd(tclients, clientDomain);
245 if( tclients->size == 0 || tservers->size == 0 ) {
246 osrfLogError( OSRF_LOG_MARK,
247 "We need trusted servers and trusted client to run the router...");
248 osrfStringArrayFree( tservers );
249 osrfStringArrayFree( tclients );
253 router = osrfNewRouter( server,
254 username, resource, password, iport, tclients, tservers );
256 signal(SIGHUP,routerSignalHandler);
257 signal(SIGINT,routerSignalHandler);
258 signal(SIGTERM,routerSignalHandler);
260 if( (osrfRouterConnect(router)) != 0 ) {
261 osrfLogError( OSRF_LOG_MARK, "Unable to connect router to jabber server %s... exiting",
263 osrfRouterFree(router);
267 // Done configuring? Let's get to work.
270 osrfRouterRun( router );
272 osrfRouterFree(router);
274 osrfLogInfo( OSRF_LOG_MARK, "Router freed" );