1. Pedantic change for const-correctness: replaced all calls to
[OpenSRF.git] / src / router / osrf_router_main.c
1 /**
2         @file osrf_router_main.c
3         @brief top level of OSRF Router
4
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.
8
9         The first command-line parameter is the name of the configuration file.
10
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).
14
15         Any subsequent command-line parameters are silently ignored.
16 */
17
18 #include <signal.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include <errno.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"
28
29 static osrfRouter* router = NULL;
30
31 static volatile sig_atomic_t stop_signal = 0;
32
33 static void setupRouter(jsonObject* configChunk);
34
35 /**
36         @brief Respond to signal by setting a switch that will interrupt the main loop.
37         @param signo The signal number.
38
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.
41 */
42 void routerSignalHandler( int signo ) {
43
44         signal( signo, routerSignalHandler );
45         router_stop( router );
46         stop_signal = signo;
47 }
48
49 /**
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.
54
55         Load a configuration file, spawn zero or more child processes, and exit.
56 */
57 int main( int argc, char* argv[] ) {
58
59         if( argc < 3 ) {
60                 osrfLogError( OSRF_LOG_MARK,
61                         "Usage: %s <path_to_config_file> <config_context>", argv[0] );
62                 exit( EXIT_FAILURE );
63         }
64
65         const char* config_file = argv[1];
66         const char* context = argv[2];
67
68         /* Get a set of router definitions from a config file */
69
70         osrfConfig* cfg = osrfConfigInit(config_file, context);
71         if( NULL == cfg ) {
72                 osrfLogError( OSRF_LOG_MARK, "Router can't load config file %s", config_file );
73                 exit( EXIT_FAILURE );
74         }
75
76         osrfConfigSetDefaultConfig(cfg);
77         jsonObject* configInfo = osrfConfigGetValueObject(NULL, "/router");
78
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 );
82                 exit( EXIT_FAILURE );
83         }
84
85         /* We're done with the command line now, so we can safely overlay it */
86
87         init_proc_title( argc, argv );
88         set_proc_title( "OpenSRF Router" );
89
90         /* Spawn child process(es) */
91
92         int rc = EXIT_SUCCESS;
93         int parent = 1;    // boolean
94         int i;
95         for(i = 0; i < configInfo->size; i++) {
96                 jsonObject* configChunk = jsonObjectGetIndex(configInfo, i);
97                 if( ! jsonObjectGetKeyConst( configChunk, "transport" ) )
98                 {
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.
102
103                         // If we don't see a member for "transport" then this is presumably such a spurious
104                         // hit, so we silently ignore it.
105
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.
108                         continue;
109                 }
110                 if(fork() == 0) { /* create a new child to run this router instance */
111                         setupRouter(configChunk);
112                         parent = 0;
113                         break;  /* We're a child; don't spawn any more children here */
114                 }
115         }
116
117         if( parent ) {
118                 // Wait for all child processes to terminate; report their fates
119                 while( 1 ) {  // Loop until all children terminate
120                         int status;
121                         errno = 0;
122                         pid_t child_pid = wait( &status );
123                         if( -1 == child_pid ) {
124                                 // ECHILD means no children are left.  Anything else we ignore.
125                                 if( ECHILD == errno )
126                                         break;
127                         } else if( WIFEXITED( status ) ) {
128                                 // Relatively normal exit, i.e. via calling exit()
129                                 // or _exit(), or by returning from main()
130                                 int child_rc = WEXITSTATUS( status );
131                                 if( child_rc ) {
132                                         osrfLogWarning( OSRF_LOG_MARK, 
133                                                 "Child router process %ld exited with return status %d",
134                                                 (long) child_pid, child_rc );
135                                         rc = EXIT_FAILURE;
136                                 } else {
137                                         ;    // Terminated successfully; silently ignore
138                                 }
139                         } else if( WIFSIGNALED( status ) ) {
140                                 // Killed by a signal
141                                 int signo = WTERMSIG( status );
142                                 const char* extra = "";
143 #ifdef WCOREDUMP
144                                 if( WCOREDUMP( status ) )
145                                         extra = "with core dump ";
146 #endif
147                                 osrfLogWarning( OSRF_LOG_MARK, "Child router process %ld killed %sby signal %d",
148                                         (long) child_pid, extra, signo );
149
150                                 rc = EXIT_FAILURE;
151                         }
152                 }
153         }
154
155         if( stop_signal ) {
156                 // Interrupted by a signal?  Re-raise so the parent can see it.
157                 osrfLogWarning( OSRF_LOG_MARK, "Interrupted by signal %d; re-raising",
158                                 (int) stop_signal );
159                 signal( stop_signal, SIG_DFL );
160                 raise( stop_signal );
161         }
162
163         return rc;
164 }
165
166 /**
167         @brief Configure and run a child process.
168         @param configChunk Pointer to a subset of the loaded configuration.
169
170         Configure oneself, daemonize, and then call osrfRouterRun() to go into a
171         near-endless loop.  Return when interrupted by a signal, or when something goes wrong.
172 */
173 static void setupRouter(jsonObject* configChunk) {
174
175         const jsonObject* transport_cfg = jsonObjectGetKeyConst( configChunk, "transport" );
176
177         const char* server   = jsonObjectGetString( jsonObjectGetKeyConst( transport_cfg, "server" ));
178         const char* port     = jsonObjectGetString( jsonObjectGetKeyConst( transport_cfg, "port" ));
179         const char* username = jsonObjectGetString( jsonObjectGetKeyConst( transport_cfg, "username" ));
180         const char* password = jsonObjectGetString( jsonObjectGetKeyConst( transport_cfg, "password" ));
181         const char* resource = jsonObjectGetString( jsonObjectGetKeyConst( transport_cfg, "resource" ));
182
183         const char* level    = jsonObjectGetString( jsonObjectGetKeyConst( configChunk, "loglevel" ));
184         const char* log_file = jsonObjectGetString( jsonObjectGetKeyConst( configChunk, "logfile" ));
185         const char* facility = jsonObjectGetString( jsonObjectGetKeyConst( configChunk, "syslog" ));
186
187         int llevel = 1;
188         if(level) llevel = atoi(level);
189
190         if(!log_file)
191         {
192                 osrfLogError( OSRF_LOG_MARK, "Log file name not specified for router" );
193                 return;
194         }
195
196         if(!strcmp(log_file, "syslog")) {
197                 osrfLogInit( OSRF_LOG_TYPE_SYSLOG, "router", llevel );
198                 osrfLogSetSyslogFacility(osrfLogFacilityToInt(facility));
199
200         } else {
201                 osrfLogInit( OSRF_LOG_TYPE_FILE, "router", llevel );
202                 osrfLogSetFile( log_file );
203         }
204
205         osrfLogInfo( OSRF_LOG_MARK, "Router connecting as: server: %s port: %s "
206                 "user: %s resource: %s", server, port, username, resource );
207
208         int iport = 0;
209         if(port)
210                 iport = atoi( port );
211
212         osrfStringArray* tclients = osrfNewStringArray(4);
213         osrfStringArray* tservers = osrfNewStringArray(4);
214
215         jsonObject* tclientsList = jsonObjectFindPath(configChunk, "/trusted_domains/client");
216         jsonObject* tserversList = jsonObjectFindPath(configChunk, "/trusted_domains/server");
217
218         int i;
219
220         if(tserversList->type == JSON_ARRAY) {
221                 for( i = 0; i != tserversList->size; i++ ) {
222                         const char* serverDomain = jsonObjectGetString(jsonObjectGetIndex(tserversList, i));
223                         osrfLogInfo( OSRF_LOG_MARK,  "Router adding trusted server: %s", serverDomain);
224                         osrfStringArrayAdd(tservers, serverDomain);
225                 }
226         } else {
227                 const char* serverDomain = jsonObjectGetString(tserversList);
228                 osrfLogInfo( OSRF_LOG_MARK,  "Router adding trusted server: %s", serverDomain);
229                 osrfStringArrayAdd(tservers, serverDomain);
230         }
231
232         if(tclientsList->type == JSON_ARRAY) {
233                 for( i = 0; i != tclientsList->size; i++ ) {
234                         const char* clientDomain = jsonObjectGetString(jsonObjectGetIndex(tclientsList, i));
235                         osrfLogInfo( OSRF_LOG_MARK,  "Router adding trusted client: %s", clientDomain);
236                         osrfStringArrayAdd(tclients, clientDomain);
237                 }
238         } else {
239                 const char* clientDomain = jsonObjectGetString(tclientsList);
240                 osrfLogInfo( OSRF_LOG_MARK,  "Router adding trusted client: %s", clientDomain);
241                 osrfStringArrayAdd(tclients, clientDomain);
242         }
243
244
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 );
250                 return;
251         }
252
253         router = osrfNewRouter( server,
254                         username, resource, password, iport, tclients, tservers );
255
256         signal(SIGHUP,routerSignalHandler);
257         signal(SIGINT,routerSignalHandler);
258         signal(SIGTERM,routerSignalHandler);
259
260         if( (osrfRouterConnect(router)) != 0 ) {
261                 osrfLogError( OSRF_LOG_MARK, "Unable to connect router to jabber server %s... exiting",
262                         server );
263                 osrfRouterFree(router);
264                 return;
265         }
266
267         // Done configuring?  Let's get to work.
268
269         daemonize();
270         osrfRouterRun( router );
271
272         osrfRouterFree(router);
273         router = NULL;
274         osrfLogInfo( OSRF_LOG_MARK, "Router freed" );
275
276         return;
277 }