jabberd2  2.2.17
main.c
Go to the documentation of this file.
1 /*
2  * jabberd - Jabber Open Source Server
3  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
4  * Ryan Eatmon, Robert Norris
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
19  */
20 
21 #include "sm.h"
22 #include <stringprep.h>
23 
31 static sig_atomic_t sm_shutdown = 0;
32 static sig_atomic_t sm_logrotate = 0;
33 static sm_t sm = NULL;
34 static char* config_file;
35 
36 static void _sm_signal(int signum)
37 {
38  sm_shutdown = 1;
39  sm_lost_router = 0;
40 }
41 
42 static void _sm_signal_hup(int signum)
43 {
44  config_t conf;
45 
46  log_write(sm->log, LOG_NOTICE, "HUP handled. reloading modules...");
47 
48  sm_logrotate = 1;
49 
50  /* reload dynamic modules */
51  conf = config_new();
52  if (conf && config_load(conf, config_file) == 0) {
53  config_free(sm->config);
54  sm->config = conf;
55  /*_sm_config_expand(sm);*/ /* we want to reload modules only */
56  } else {
57  log_write(sm->log, LOG_WARNING, "couldn't reload config (%s)", config_file);
58  if (conf) config_free(conf);
59  }
60  mm_free(sm->mm);
61  sm->mm = mm_new(sm);
62 }
63 
64 static void _sm_signal_usr1(int signum)
65 {
66  set_debug_flag(0);
67 }
68 
69 static void _sm_signal_usr2(int signum)
70 {
71  set_debug_flag(1);
72 }
73 
75 static void _sm_pidfile(sm_t sm) {
76  char *pidfile;
77  FILE *f;
78  pid_t pid;
79 
80  pidfile = config_get_one(sm->config, "pidfile", 0);
81  if(pidfile == NULL)
82  return;
83 
84  pid = getpid();
85 
86  if((f = fopen(pidfile, "w+")) == NULL) {
87  log_write(sm->log, LOG_ERR, "couldn't open %s for writing: %s", pidfile, strerror(errno));
88  return;
89  }
90 
91  if(fprintf(f, "%d", pid) < 0) {
92  log_write(sm->log, LOG_ERR, "couldn't write to %s: %s", pidfile, strerror(errno));
93  fclose(f);
94  return;
95  }
96 
97  fclose(f);
98 
99  log_write(sm->log, LOG_INFO, "process id is %d, written to %s", pid, pidfile);
100 }
101 
103 static void _sm_config_expand(sm_t sm)
104 {
105  char *str;
106  config_elem_t elem;
107 
109 
110  sm->id = config_get_one(sm->config, "id", 0);
111  if(sm->id == NULL)
112  sm->id = "sm";
113 
114  sm->router_ip = config_get_one(sm->config, "router.ip", 0);
115  if(sm->router_ip == NULL)
116  sm->router_ip = "127.0.0.1";
117 
118  sm->router_port = j_atoi(config_get_one(sm->config, "router.port", 0), 5347);
119 
120  sm->router_user = config_get_one(sm->config, "router.user", 0);
121  if(sm->router_user == NULL)
122  sm->router_user = "jabberd";
123  sm->router_pass = config_get_one(sm->config, "router.pass", 0);
124  if(sm->router_pass == NULL)
125  sm->router_pass = "secret";
126 
127  sm->router_pemfile = config_get_one(sm->config, "router.pemfile", 0);
128 
129  sm->retry_init = j_atoi(config_get_one(sm->config, "router.retry.init", 0), 3);
130  sm->retry_lost = j_atoi(config_get_one(sm->config, "router.retry.lost", 0), 3);
131  if((sm->retry_sleep = j_atoi(config_get_one(sm->config, "router.retry.sleep", 0), 2)) < 1)
132  sm->retry_sleep = 1;
133 
134  sm->log_type = log_STDOUT;
135  if(config_get(sm->config, "log") != NULL) {
136  if((str = config_get_attr(sm->config, "log", 0, "type")) != NULL) {
137  if(strcmp(str, "file") == 0)
138  sm->log_type = log_FILE;
139  else if(strcmp(str, "syslog") == 0)
140  sm->log_type = log_SYSLOG;
141  }
142  }
143 
144  if(sm->log_type == log_SYSLOG) {
145  sm->log_facility = config_get_one(sm->config, "log.facility", 0);
146  sm->log_ident = config_get_one(sm->config, "log.ident", 0);
147  if(sm->log_ident == NULL)
148  sm->log_ident = "jabberd/sm";
149  } else if(sm->log_type == log_FILE)
150  sm->log_ident = config_get_one(sm->config, "log.file", 0);
151 
152  elem = config_get(sm->config, "storage.limits.queries");
153  if(elem != NULL)
154  {
155  sm->query_rate_total = j_atoi(elem->values[0], 0);
156  if(sm->query_rate_total != 0)
157  {
158  sm->query_rate_seconds = j_atoi(j_attr((const char **) elem->attrs[0], "seconds"), 5);
159  sm->query_rate_wait = j_atoi(j_attr((const char **) elem->attrs[0], "throttle"), 60);
160  }
161  }
162 }
163 
164 static void _sm_hosts_expand(sm_t sm)
165 {
166  config_elem_t elem;
167  char id[1024];
168  int i;
169 
170  elem = config_get(sm->config, "local.id");
171  if(!elem) {
172  /* use SM id */
173  xhash_put(sm->hosts, pstrdup(xhash_pool(sm->hosts), sm->id), sm);
174  log_write(sm->log, LOG_NOTICE, "id: %s", sm->id);
175  return;
176  }
177 
178  for(i = 0; i < elem->nvalues; i++) {
179  /* stringprep ids (domain names) so that they are in canonical form */
180  strncpy(id, elem->values[i], 1024);
181  id[1023] = '\0';
182  if (stringprep_nameprep(id, 1024) != 0) {
183  log_write(sm->log, LOG_ERR, "cannot stringprep id %s, aborting", id);
184  exit(1);
185  }
186 
187  /* insert into vHosts xhash */
188  xhash_put(sm->hosts, pstrdup(xhash_pool(sm->hosts), id), sm);
189 
190  log_write(sm->log, LOG_NOTICE, "[%s] configured", id);
191  }
192 }
193 
194 static int _sm_router_connect(sm_t sm) {
195  log_write(sm->log, LOG_NOTICE, "attempting connection to router at %s, port=%d", sm->router_ip, sm->router_port);
196 
197  sm->fd = mio_connect(sm->mio, sm->router_port, sm->router_ip, NULL, sm_mio_callback, (void *) sm);
198  if(sm->fd == NULL) {
199  if(errno == ECONNREFUSED)
200  sm_lost_router = 1;
201  log_write(sm->log, LOG_NOTICE, "connection attempt to router failed: %s (%d)", MIO_STRERROR(MIO_ERROR), MIO_ERROR);
202  return 1;
203  }
204 
205  sm->router = sx_new(sm->sx_env, sm->fd->fd, sm_sx_callback, (void *) sm);
206  sx_client_init(sm->router, 0, NULL, NULL, NULL, "1.0");
207 
208  return 0;
209 }
210 
211 JABBER_MAIN("jabberd2sm", "Jabber 2 Session Manager", "Jabber Open Source Server: Session Manager", "jabberd2router\0")
212 {
213  int optchar;
214  sess_t sess;
215  char id[1024];
216 #ifdef POOL_DEBUG
217  time_t pool_time = 0;
218 #endif
219  const char *cli_id = 0;
220 
221 #ifdef HAVE_UMASK
222  umask((mode_t) 0027);
223 #endif
224 
225  srand(time(NULL));
226 
227 #ifdef HAVE_WINSOCK2_H
228 /* get winsock running */
229  {
230  WORD wVersionRequested;
231  WSADATA wsaData;
232  int err;
233 
234  wVersionRequested = MAKEWORD( 2, 2 );
235 
236  err = WSAStartup( wVersionRequested, &wsaData );
237  if ( err != 0 ) {
238  /* !!! tell user that we couldn't find a usable winsock dll */
239  return 0;
240  }
241  }
242 #endif
243 
244  jabber_signal(SIGINT, _sm_signal);
245  jabber_signal(SIGTERM, _sm_signal);
246 #ifdef SIGHUP
247  jabber_signal(SIGHUP, _sm_signal_hup);
248 #endif
249 #ifdef SIGPIPE
250  jabber_signal(SIGPIPE, SIG_IGN);
251 #endif
252  jabber_signal(SIGUSR1, _sm_signal_usr1);
253  jabber_signal(SIGUSR2, _sm_signal_usr2);
254 
255 
256  sm = (sm_t) calloc(1, sizeof(struct sm_st));
257 
258  /* load our config */
259  sm->config = config_new();
260 
261  config_file = CONFIG_DIR "/sm.xml";
262 
263  /* cmdline parsing */
264  while((optchar = getopt(argc, argv, "Dc:hi:?")) >= 0)
265  {
266  switch(optchar)
267  {
268  case 'c':
269  config_file = optarg;
270  break;
271  case 'D':
272 #ifdef DEBUG
273  set_debug_flag(1);
274 #else
275  printf("WARN: Debugging not enabled. Ignoring -D.\n");
276 #endif
277  break;
278  case 'i':
279  cli_id = optarg;
280  break;
281  case 'h': case '?': default:
282  fputs(
283  "sm - jabberd session manager (" VERSION ")\n"
284  "Usage: sm <options>\n"
285  "Options are:\n"
286  " -c <config> config file to use [default: " CONFIG_DIR "/sm.xml]\n"
287  " -i id Override <id> config element\n"
288 #ifdef DEBUG
289  " -D Show debug output\n"
290 #endif
291  ,
292  stdout);
293  config_free(sm->config);
294  free(sm);
295  return 1;
296  }
297  }
298 
299  if(config_load_with_id(sm->config, config_file, cli_id) != 0)
300  {
301  fputs("sm: couldn't load config, aborting\n", stderr);
302  config_free(sm->config);
303  free(sm);
304  return 2;
305  }
306 
307  _sm_config_expand(sm);
308 
309  sm->log = log_new(sm->log_type, sm->log_ident, sm->log_facility);
310  log_write(sm->log, LOG_NOTICE, "starting up");
311 
312  /* stringprep id (domain name) so that it's in canonical form */
313  strncpy(id, sm->id, 1024);
314  id[sizeof(id)-1] = '\0';
315  if (stringprep_nameprep(id, 1024) != 0) {
316  log_write(sm->log, LOG_ERR, "cannot stringprep id %s, aborting", sm->id);
317  exit(1);
318  }
319  sm->id = id;
320 
321  _sm_pidfile(sm);
322 
323  sm_signature(sm, PACKAGE " sm " VERSION);
324 
325  /* start storage */
326  sm->st = storage_new(sm->config, sm->log);
327  if (sm->st == NULL) {
328  log_write(sm->log, LOG_ERR, "failed to initialise one or more storage drivers, aborting");
329  exit(1);
330  }
331 
332  /* pre-index known namespaces */
333  sm->xmlns = xhash_new(101);
334  xhash_put(sm->xmlns, uri_AUTH, (void *) ns_AUTH);
335  xhash_put(sm->xmlns, uri_REGISTER, (void *) ns_REGISTER);
336  xhash_put(sm->xmlns, uri_ROSTER, (void *) ns_ROSTER);
337  xhash_put(sm->xmlns, uri_AGENTS, (void *) ns_AGENTS);
338  xhash_put(sm->xmlns, uri_DELAY, (void *) ns_DELAY);
339  xhash_put(sm->xmlns, uri_BROWSE, (void *) ns_BROWSE);
340  xhash_put(sm->xmlns, uri_EVENT, (void *) ns_EVENT);
341  xhash_put(sm->xmlns, uri_GATEWAY, (void *) ns_GATEWAY);
342  xhash_put(sm->xmlns, uri_EXPIRE, (void *) ns_EXPIRE);
343  xhash_put(sm->xmlns, uri_SEARCH, (void *) ns_SEARCH);
344  xhash_put(sm->xmlns, uri_DISCO, (void *) ns_DISCO);
347  sm->xmlns_refcount = xhash_new(101);
348 
349  /* supported features */
350  sm->features = xhash_new(101);
351 
352  /* load acls */
353  sm->acls = aci_load(sm);
354 
355  /* the core supports iq, everything else is handled by the modules */
356  feature_register(sm, "iq");
357 
358  /* startup the modules */
359  sm->mm = mm_new(sm);
360 
361  log_write(sm->log, LOG_NOTICE, "version: %s", sm->signature);
362 
363  sm->sessions = xhash_new(401);
364 
365  sm->users = xhash_new(401);
366 
367  sm->query_rates = xhash_new(101);
368 
369  sm->sx_env = sx_env_new();
370 
371 #ifdef HAVE_SSL
372  if(sm->router_pemfile != NULL) {
373  sm->sx_ssl = sx_env_plugin(sm->sx_env, sx_ssl_init, NULL, sm->router_pemfile, NULL, NULL);
374  if(sm->sx_ssl == NULL) {
375  log_write(sm->log, LOG_ERR, "failed to load SSL pemfile, SSL disabled");
376  sm->router_pemfile = NULL;
377  }
378  }
379 #endif
380 
381  /* get sasl online */
382  sm->sx_sasl = sx_env_plugin(sm->sx_env, sx_sasl_init, "xmpp", NULL, NULL);
383  if(sm->sx_sasl == NULL) {
384  log_write(sm->log, LOG_ERR, "failed to initialise SASL context, aborting");
385  exit(1);
386  }
387 
388  sm->mio = mio_new(MIO_MAXFD);
389 
390  /* vHosts map */
391  sm->hosts = xhash_new(1021);
392  _sm_hosts_expand(sm);
393 
394  sm->retry_left = sm->retry_init;
395  _sm_router_connect(sm);
396 
397  while(!sm_shutdown) {
398  mio_run(sm->mio, 5);
399 
400  if(sm_logrotate) {
402 
403  log_write(sm->log, LOG_NOTICE, "reopening log ...");
404  log_free(sm->log);
405  sm->log = log_new(sm->log_type, sm->log_ident, sm->log_facility);
406  log_write(sm->log, LOG_NOTICE, "log started");
407 
408  sm_logrotate = 0;
409  }
410 
411  if(sm_lost_router) {
412  if(sm->retry_left < 0) {
413  log_write(sm->log, LOG_NOTICE, "attempting reconnect");
414  sleep(sm->retry_sleep);
415  sm_lost_router = 0;
416  if (sm->router) sx_free(sm->router);
417  _sm_router_connect(sm);
418  }
419 
420  else if(sm->retry_left == 0) {
421  sm_shutdown = 1;
422  }
423 
424  else {
425  log_write(sm->log, LOG_NOTICE, "attempting reconnect (%d left)", sm->retry_left);
426  sm->retry_left--;
427  sleep(sm->retry_sleep);
428  sm_lost_router = 0;
429  if (sm->router) sx_free(sm->router);
430  _sm_router_connect(sm);
431  }
432  }
433 
434 #ifdef POOL_DEBUG
435  if(time(NULL) > pool_time + 60) {
436  pool_stat(1);
437  pool_time = time(NULL);
438  }
439 #endif
440  }
441 
442  log_write(sm->log, LOG_NOTICE, "shutting down");
443 
444  /* shut down sessions */
445  if(xhash_iter_first(sm->sessions))
446  do {
447  xhash_iter_get(sm->sessions, NULL, NULL, (void *) &sess);
448  sm_c2s_action(sess, "ended", NULL);
449  sess_end(sess);
450  } while (xhash_iter_next(sm->sessions));
451 
452  xhash_free(sm->sessions);
453 
454  if (sm->fd) mio_close(sm->mio, sm->fd);
455  mio_free(sm->mio);
456 
457  mm_free(sm->mm);
458  storage_free(sm->st);
459 
460  aci_unload(sm->acls);
461  xhash_free(sm->acls);
462  xhash_free(sm->features);
463  xhash_free(sm->xmlns);
465  xhash_free(sm->users);
466  xhash_free(sm->hosts);
467  xhash_free(sm->query_rates);
468 
469  sx_free(sm->router);
470 
471  sx_env_free(sm->sx_env);
472 
473  log_free(sm->log);
474 
475  config_free(sm->config);
476 
477  free(sm);
478 
479 #ifdef POOL_DEBUG
480  pool_stat(1);
481 #endif
482 
483 #ifdef HAVE_WINSOCK2_H
484  WSACleanup();
485 #endif
486 
487  return 0;
488 }