Miscellaneous minor tweaks:
[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 "opensrf/utils.h"
20 #include "opensrf/log.h"
21 #include "opensrf/osrf_list.h"
22 #include "opensrf/string_array.h"
23 #include "opensrf/osrfConfig.h"
24 #include "osrf_router.h"
25
26 static osrfRouter* router = NULL;
27
28 static sig_atomic_t stop_signal = 0;
29
30 static void setupRouter(jsonObject* configChunk);
31
32 /**
33         @brief Respond to signal by setting a switch that will interrupt the main loop.
34         @param signo The signal number.
35
36         Signal handler.  We not only interrupt the main loop but also remember the signal
37         number so that we can report it later and re-raise it.
38 */
39 void routerSignalHandler( int signo ) {
40
41         signal( signo, routerSignalHandler );
42         router_stop( router );
43         stop_signal = signo;
44 }
45
46 /**
47         @brief The top-level function of the router program.
48         @param argc Number of items in command line.
49         @param argv Pointer to array of items on command line.
50         @return System return code.
51
52         Load a configuration file, spawn zero or more child processes, and exit.
53 */
54 int main( int argc, char* argv[] ) {
55
56         if( argc < 3 ) {
57                 osrfLogError( OSRF_LOG_MARK,
58                         "Usage: %s <path_to_config_file> <config_context>", argv[0] );
59                 exit( EXIT_FAILURE );
60         }
61
62         const char* config_file = argv[1];
63         const char* context = argv[2];
64
65         /* Get a set of router definitions from a config file */
66
67         osrfConfig* cfg = osrfConfigInit(config_file, context);
68         if( NULL == cfg ) {
69                 osrfLogError( OSRF_LOG_MARK, "Router can't load config file %s", config_file );
70                 exit( EXIT_FAILURE );
71         }
72
73         osrfConfigSetDefaultConfig(cfg);
74         jsonObject* configInfo = osrfConfigGetValueObject(NULL, "/router");
75
76         if( configInfo->size < 1 || NULL == jsonObjectGetIndex( configInfo, 1 ) ) {
77                 osrfLogError( OSRF_LOG_MARK, "No routers defined in config file %s, context \"%s\"",
78                         config_file, context );
79                 exit( EXIT_FAILURE );
80         }
81
82         /* We're done with the command line now, so we can safely overlay it */
83
84         init_proc_title( argc, argv );
85         set_proc_title( "OpenSRF Router" );
86
87         /* Spawn child process(es) */
88
89         int i;
90         for(i = 0; i < configInfo->size; i++) {
91                 jsonObject* configChunk = jsonObjectGetIndex(configInfo, i);
92                 if( ! jsonObjectGetKey( configChunk, "transport" ) )
93                 {
94                         // In searching the configuration file for a given context, we may have found a
95                         // spurious hit on an unrelated part of the configuration file that happened to use
96                         // the same XML tag.  In fact this happens routinely in practice.
97
98                         // If we don't see a member for "transport" then this is presumably such a spurious
99                         // hit, so we silently ignore it.
100
101                         // It is also possible that it's the right part of the configuration file but it has a
102                         // typo or other such error, making it look spurious.  In that case, well, too bad.
103                         continue;
104                 }
105                 if(fork() == 0) { /* create a new child to run this router instance */
106                         setupRouter(configChunk);
107                         break;  /* We're a child; don't spawn any more children here */
108                 }
109         }
110
111         if( stop_signal ) {
112                 // Interrupted by a signal?  Re raise so the parent can see it.
113                 osrfLogWarning( OSRF_LOG_MARK, "Interrupted by signal %d; re-raising",
114                                 (int) stop_signal );
115                 raise( stop_signal );
116         }
117
118         return EXIT_SUCCESS;
119 }
120
121 /**
122         @brief Configure and run a child process.
123         @param configChunk Pointer to a subset of the loaded configuration.
124
125         Configure oneself, daemonize, and then call osrfRouterRun() to go into a
126         near-endless loop.  Return when interrupted by a signal, or when something goes wrong.
127 */
128 static void setupRouter(jsonObject* configChunk) {
129
130         jsonObject* transport_cfg = jsonObjectGetKey( configChunk, "transport" );
131
132         const char* server   = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "server" ) );
133         const char* port     = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "port" ) );
134         const char* username = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "username" ) );
135         const char* password = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "password" ) );
136         const char* resource = jsonObjectGetString( jsonObjectGetKey( transport_cfg, "resource" ) );
137
138         const char* level    = jsonObjectGetString( jsonObjectGetKey( configChunk, "loglevel" ) );
139         const char* log_file = jsonObjectGetString( jsonObjectGetKey( configChunk, "logfile" ) );
140         const char* facility = jsonObjectGetString( jsonObjectGetKey( configChunk, "syslog" ) );
141
142         int llevel = 1;
143         if(level) llevel = atoi(level);
144
145         if(!log_file)
146         {
147                 fprintf(stderr, "Log file name not specified for router\n");
148                 return;
149         }
150
151         if(!strcmp(log_file, "syslog")) {
152                 osrfLogInit( OSRF_LOG_TYPE_SYSLOG, "router", llevel );
153                 osrfLogSetSyslogFacility(osrfLogFacilityToInt(facility));
154
155         } else {
156                 osrfLogInit( OSRF_LOG_TYPE_FILE, "router", llevel );
157                 osrfLogSetFile( log_file );
158         }
159
160         osrfLogInfo( OSRF_LOG_MARK, "Router connecting as: server: %s port: %s "
161                 "user: %s resource: %s", server, port, username, resource );
162
163         int iport = 0;
164         if(port)
165                 iport = atoi( port );
166
167         osrfStringArray* tclients = osrfNewStringArray(4);
168         osrfStringArray* tservers = osrfNewStringArray(4);
169
170         jsonObject* tclientsList = jsonObjectFindPath(configChunk, "/trusted_domains/client");
171         jsonObject* tserversList = jsonObjectFindPath(configChunk, "/trusted_domains/server");
172
173         int i;
174
175         if(tserversList->type == JSON_ARRAY) {
176                 for( i = 0; i != tserversList->size; i++ ) {
177                         const char* serverDomain = jsonObjectGetString(jsonObjectGetIndex(tserversList, i));
178                         osrfLogInfo( OSRF_LOG_MARK,  "Router adding trusted server: %s", serverDomain);
179                         osrfStringArrayAdd(tservers, serverDomain);
180                 }
181         } else {
182                 const char* serverDomain = jsonObjectGetString(tserversList);
183                 osrfLogInfo( OSRF_LOG_MARK,  "Router adding trusted server: %s", serverDomain);
184                 osrfStringArrayAdd(tservers, serverDomain);
185         }
186
187         if(tclientsList->type == JSON_ARRAY) {
188                 for( i = 0; i != tclientsList->size; i++ ) {
189                         const char* clientDomain = jsonObjectGetString(jsonObjectGetIndex(tclientsList, i));
190                         osrfLogInfo( OSRF_LOG_MARK,  "Router adding trusted client: %s", clientDomain);
191                         osrfStringArrayAdd(tclients, clientDomain);
192                 }
193         } else {
194                 const char* clientDomain = jsonObjectGetString(tclientsList);
195                 osrfLogInfo( OSRF_LOG_MARK,  "Router adding trusted client: %s", clientDomain);
196                 osrfStringArrayAdd(tclients, clientDomain);
197         }
198
199
200         if( tclients->size == 0 || tservers->size == 0 ) {
201                 osrfLogError( OSRF_LOG_MARK,
202                                 "We need trusted servers and trusted client to run the router...");
203                 osrfStringArrayFree( tservers );
204                 osrfStringArrayFree( tclients );
205                 return;
206         }
207
208         router = osrfNewRouter( server,
209                         username, resource, password, iport, tclients, tservers );
210
211         signal(SIGHUP,routerSignalHandler);
212         signal(SIGINT,routerSignalHandler);
213         signal(SIGTERM,routerSignalHandler);
214
215         if( (osrfRouterConnect(router)) != 0 ) {
216                 fprintf(stderr, "Unable to connect router to jabber server %s... exiting\n", server );
217                 osrfRouterFree(router);
218                 return;
219         }
220
221         // Done configuring?  Let's get to work.
222
223         daemonize();
224         osrfRouterRun( router );
225
226         osrfRouterFree(router);
227         router = NULL;
228         osrfLogInfo( OSRF_LOG_MARK, "Router freed" );
229
230         return;
231 }