Changed the signal handling.
[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 
6         processes.  Each child process configures itself, daemonizes itself, and then (via a
7         call to osrfRouterRun()) goes into an infinite loop to route messages among clients
8         and servers.
9
10         The first command-line parameter is the name of the configuration file.
11         
12         The second command-line parameter is the context -- an XML tag identifying the subset
13         of the configuration file that is relevant to this application (since a configuration
14         file may include information for multiple applications).
15
16         Any subsequent command-line parameters are silently ignored.
17 */
18
19 #include "osrf_router.h"
20 #include <opensrf/osrfConfig.h>
21 #include <opensrf/utils.h>
22 #include <opensrf/log.h>
23 #include <opensrf/osrf_json.h>
24 #include <signal.h>
25
26 /**
27         An osrfRouter contains various bits and scraps that the router uses for networking.
28 */
29 static osrfRouter* router = NULL;
30
31 static 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 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, */
86         /* so we can safely overlay it */
87         
88         init_proc_title( argc, argv );
89         set_proc_title( "OpenSRF Router" );
90
91         /* Spawn child process(es) */
92         
93     int i;
94     for(i = 0; i < configInfo->size; i++) {
95         jsonObject* configChunk = jsonObjectGetIndex(configInfo, i);
96                 if( ! jsonObjectGetKey( configChunk, "transport" ) )
97                 {
98                         // In searching the configuration file for a given context, we may have found a
99                         // spurious hit on an unrelated part of the configuration file that happened to use
100                         // the same XML tag.  In fact this happens routinely in practice.
101                         
102                         // If we don't see a member for "transport" then this is presumably such a spurious
103                         // hit, so we silently ignore it.
104                         
105                         // It is also possible that it's the right part of the configuration file but it has a
106                         // typo or other such error, making it look spurious.  In that case, well, too bad.
107                         continue;
108                 }
109         if(fork() == 0) { /* create a new child to run this router instance */
110             setupRouter(configChunk);
111                         break;  /* We're a child; don't spawn any more children here */
112                 }
113     }
114
115         if( stop_signal ) {
116                 // Interrupted by a signal?  Re raise so the parent can see it.
117                 osrfLogWarning( OSRF_LOG_MARK, "Interrupted by signal %d; re-raising",
118                                 (int) stop_signal );
119                 raise( stop_signal );
120         }
121
122         return EXIT_SUCCESS;
123 }
124
125 /**
126         @brief Configure and run a child process.
127         @param configChunk Pointer to a subset of the loaded configuration.
128
129         Configure oneself, daemonize, and then call osrfRouterRun() to go into an infinite
130         loop.  Do not return unless something goes wrong.
131 */
132 static void setupRouter(jsonObject* configChunk) {
133
134         jsonObject* transport_cfg = jsonObjectGetKey( configChunk, "transport" );
135
136         const char* server   = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "server" ) );
137         const char* port     = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "port" ) );
138         const char* username = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "username" ) );
139         const char* password = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "password" ) );
140         const char* resource = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "resource" ) );
141
142         const char* level    = jsonObjectGetString( jsonObjectGetKey( configChunk, "loglevel" ) );
143         const char* log_file = jsonObjectGetString( jsonObjectGetKey( configChunk, "logfile" ) );
144         const char* facility = jsonObjectGetString( jsonObjectGetKey( configChunk, "syslog" ) );
145
146         int llevel = 1;
147         if(level) llevel = atoi(level);
148
149         if(!log_file)
150         { 
151                 fprintf(stderr, "Log file name not specified for router\n");
152                 return;
153         }
154
155         if(!strcmp(log_file, "syslog")) {
156                 osrfLogInit( OSRF_LOG_TYPE_SYSLOG, "router", llevel );
157                 osrfLogSetSyslogFacility(osrfLogFacilityToInt(facility));
158
159         } else {
160                 osrfLogInit( OSRF_LOG_TYPE_FILE, "router", llevel );
161                 osrfLogSetFile( log_file );
162         }
163
164         osrfLogInfo( OSRF_LOG_MARK, "Router connecting as: server: %s port: %s "
165                 "user: %s resource: %s", server, port, username, resource );
166
167         int iport = 0;
168         if(port)        iport = atoi( port );
169
170         osrfStringArray* tclients = osrfNewStringArray(4);
171         osrfStringArray* tservers = osrfNewStringArray(4);
172
173     jsonObject* tclientsList = jsonObjectFindPath(configChunk, "/trusted_domains/client");
174     jsonObject* tserversList = jsonObjectFindPath(configChunk, "/trusted_domains/server");
175
176         int i;
177
178     if(tserversList->type == JSON_ARRAY) {
179             for( i = 0; i != tserversList->size; i++ ) {
180             const char* serverDomain = jsonObjectGetString(jsonObjectGetIndex(tserversList, i));
181                     osrfLogInfo( OSRF_LOG_MARK,  "Router adding trusted server: %s", serverDomain);
182             osrfStringArrayAdd(tservers, serverDomain);
183         }
184     } else {
185         const char* serverDomain = jsonObjectGetString(tserversList);
186         osrfLogInfo( OSRF_LOG_MARK,  "Router adding trusted server: %s", serverDomain);
187         osrfStringArrayAdd(tservers, serverDomain);
188     }
189
190     if(tclientsList->type == JSON_ARRAY) {
191             for( i = 0; i != tclientsList->size; i++ ) {
192             const char* clientDomain = jsonObjectGetString(jsonObjectGetIndex(tclientsList, i));
193                     osrfLogInfo( OSRF_LOG_MARK,  "Router adding trusted client: %s", clientDomain);
194             osrfStringArrayAdd(tclients, clientDomain);
195         }
196     } else {
197         const char* clientDomain = jsonObjectGetString(tclientsList);
198         osrfLogInfo( OSRF_LOG_MARK,  "Router adding trusted client: %s", clientDomain);
199         osrfStringArrayAdd(tclients, clientDomain);
200     }
201
202
203         if( tclients->size == 0 || tservers->size == 0 ) {
204                 osrfLogError( OSRF_LOG_MARK, "We need trusted servers and trusted client to run the router...");
205                 osrfStringArrayFree( tservers );
206                 osrfStringArrayFree( tclients );
207                 return;
208         }
209
210         router = osrfNewRouter( server,
211                         username, resource, password, iport, tclients, tservers );
212         
213         signal(SIGHUP,routerSignalHandler);
214         signal(SIGINT,routerSignalHandler);
215         signal(SIGTERM,routerSignalHandler);
216
217         if( (osrfRouterConnect(router)) != 0 ) {
218                 fprintf(stderr, "Unable to connect router to jabber server %s... exiting\n", server );
219                 osrfRouterFree(router);
220                 return;
221         }
222
223         daemonize();
224         osrfRouterRun( router );
225
226         // Shouldn't get here, since osrfRouterRun()
227         // should go into an infinite loop
228
229         osrfRouterFree(router);
230         router = NULL;
231         
232         osrfLogInfo( OSRF_LOG_MARK, "Router freed" );
233
234         return;
235 }
236
237