Kannel: Open Source WAP and SMS gateway  $Revision: 5037 $
sqlbox.c
Go to the documentation of this file.
1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2004 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Kannel Group (http://www.kannel.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  * endorse or promote products derived from this software without
29  * prior written permission. For written permission, please
30  * contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  * nor may "Kannel" appear in their name, without prior written
34  * permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group. For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 /*
58  * sqlbox.c - main program of the sqlbox
59  */
60 
61 #include <errno.h>
62 #include <unistd.h>
63 #include <signal.h>
64 #include <string.h>
65 
66 #include "gwlib/gwlib.h"
67 #include "gwlib/dbpool.h"
68 #include "gw/msg.h"
69 #include "gw/sms.h"
70 #include "gw/shared.h"
71 #include "gw/bb.h"
72 #include "sqlbox_sql.h"
73 
74 /* our config */
75 static Cfg *cfg;
76 /* have we received restart cmd from bearerbox? */
77 static volatile sig_atomic_t restart_sqlbox = 0;
78 static volatile sig_atomic_t sqlbox_status;
79 #define SQL_DEAD 0
80 #define SQL_SHUTDOWN 1
81 #define SQL_RUNNING 2
82 static long sqlbox_port;
83 static int sqlbox_port_ssl = 0;
84 static long bearerbox_port;
86 static int bearerbox_port_ssl = 0;
88 static long limit_per_cycle;
89 static int save_mo, save_mt, save_dlr;
90 
91 #ifndef HAVE_MSSQL
92 #ifndef HAVE_MYSQL
93 #ifndef HAVE_PGSQL
94 #ifndef HAVE_SDB
95 #ifndef HAVE_SQLITE
96 #ifndef HAVE_SQLITE3
97 #ifndef HAVE_ORACLE
98 #error You need support for at least one DB engine. Please recompile Kannel.
99 #endif
100 #endif
101 #endif
102 #endif
103 #endif
104 #endif
105 #endif
107 
108 #define SLEEP_BETWEEN_EMPTY_SELECTS 1.0
109 #define DEFAULT_LIMIT_PER_CYCLE 10
110 
111 typedef struct _boxc {
114  time_t connect_time;
115  Octstr *client_ip;
116  volatile sig_atomic_t alive;
117  Octstr *boxc_id; /* identifies the connected smsbox instance */
118 } Boxc;
119 
120 /*
121  * Adding hooks to kannel check config
122  *
123  * Martin Conte.
124  */
125 
126 static int sqlbox_is_allowed_in_group(Octstr *group, Octstr *variable)
127 {
128  Octstr *groupstr;
129 
130  groupstr = octstr_imm("group");
131 
132  #define OCTSTR(name) \
133  if (octstr_compare(octstr_imm(#name), variable) == 0) \
134  return 1;
135  #define SINGLE_GROUP(name, fields) \
136  if (octstr_compare(octstr_imm(#name), group) == 0) { \
137  if (octstr_compare(groupstr, variable) == 0) \
138  return 1; \
139  fields \
140  return 0; \
141  }
142  #define MULTI_GROUP(name, fields) \
143  if (octstr_compare(octstr_imm(#name), group) == 0) { \
144  if (octstr_compare(groupstr, variable) == 0) \
145  return 1; \
146  fields \
147  return 0; \
148  }
149  #include "sqlbox-cfg.def"
150 
151  return 0;
152 }
153 
154 #undef OCTSTR
155 #undef SINGLE_GROUP
156 #undef MULTI_GROUP
157 
158 static int sqlbox_is_single_group(Octstr *query)
159 {
160  #define OCTSTR(name)
161  #define SINGLE_GROUP(name, fields) \
162  if (octstr_compare(octstr_imm(#name), query) == 0) \
163  return 1;
164  #define MULTI_GROUP(name, fields) \
165  if (octstr_compare(octstr_imm(#name), query) == 0) \
166  return 0;
167  #include "sqlbox-cfg.def"
168  return 0;
169 }
170 
171 
172 /****************************************************************************
173  * Character convertion.
174  *
175  * The 'msgdata' is read from the DB table as URL-encoded byte stream,
176  * which we need to URL-decode to get the orginal message. We use this
177  * approach to get rid of the table character dependancy of the DB systems.
178  * The URL-encoded chars as a subset of ASCII which is typicall no problem
179  * for any of the supported DB systems.
180  */
181 
183 {
184  gw_assert(msg->type == sms);
185 
186  /* URL-decode first */
187  if (octstr_url_decode(msg->sms.msgdata) == -1)
188  return -1;
189  if (octstr_url_decode(msg->sms.udhdata) == -1)
190  return -1;
191 
192  /* If a specific character encoding has been indicated by the
193  * user, then make sure we convert to our internal representations. */
194  if (octstr_len(msg->sms.charset)) {
195 
196  if (msg->sms.coding == DC_7BIT) {
197  /* For 7 bit, convert to UTF-8 */
198  if (charset_convert(msg->sms.msgdata, octstr_get_cstr(msg->sms.charset), "UTF-8") < 0)
199  return -1;
200  }
201  else if (msg->sms.coding == DC_UCS2) {
202  /* For UCS-2, convert to UTF-16BE */
203  if (charset_convert(msg->sms.msgdata, octstr_get_cstr(msg->sms.charset), "UTF-16BE") < 0)
204  return -1;
205  }
206  }
207 
208  return 0;
209 }
210 
211 
212 /*
213  *-------------------------------------------------
214  * receiver thingies
215  *-------------------------------------------------
216  *
217 */
218 
219 /* read from either smsbox or bearerbox */
220 
221 static Msg *read_from_box(Connection *conn, Boxc *boxconn)
222 {
223  Msg *msg;
224 
225  while (boxconn->alive) {
226  switch (read_from_bearerbox_real(conn, &msg, 1.0)) {
227  case -1:
228  /* connection to bearerbox lost */
229  return NULL;
230  break;
231  case 0:
232  /* all is well */
233  return msg;
234  break;
235  case 1:
236  /* timeout */
237  break;
238  }
239  }
240 
241  return NULL;
242 }
243 
244 /*
245  *-------------------------------------------------
246  * sender thingies
247  *-------------------------------------------------
248  *
249 */
250 
251 /* send to either smsbox or bearerbox */
252 
253 static int send_msg(Connection *conn, Boxc *boxconn, Msg *pmsg)
254 {
255  Octstr *pack;
256 
257  pack = msg_pack(pmsg);
258 
259  if (pack == NULL)
260  return -1;
261 
262  if (conn_write_withlen(conn, pack) == -1) {
263  error(0, "Couldn't write Msg to box <%s>, disconnecting",
264  octstr_get_cstr(boxconn->client_ip));
265  octstr_destroy(pack);
266  return -1;
267  }
268  octstr_destroy(pack);
269  return 0;
270 }
271 
272 static void smsbox_to_bearerbox(void *arg)
273 {
274  Boxc *conn = arg;
275  Msg *msg, *msg_escaped;
276 
277  /* remove messages from socket until it is closed */
278  while (sqlbox_status == SQL_RUNNING && conn->alive) {
279 
280  //list_consume(suspended); /* block here if suspended */
281 
282  msg = read_from_box(conn->smsbox_connection, conn);
283 
284  if (msg == NULL) { /* garbage/connection lost */
285  conn->alive = 0;
286  break;
287  }
288 
289  if (msg_type(msg) == sms) {
290  debug("sqlbox", 0, "smsbox_to_bearerbox: sms received");
291  if (save_mt) {
292  msg_escaped = msg_duplicate(msg);
293  /* convert validity & deferred to minutes */
294  if (msg_escaped->sms.validity != SMS_PARAM_UNDEFINED)
295  msg_escaped->sms.validity = (msg_escaped->sms.validity - time(NULL))/60;
296  if (msg_escaped->sms.deferred != SMS_PARAM_UNDEFINED)
297  msg_escaped->sms.deferred = (msg_escaped->sms.deferred - time(NULL))/60;
298  gw_sql_save_msg(msg_escaped, octstr_imm("MT"));
299  msg_destroy(msg_escaped);
300  }
301  }
302 
303  send_msg(conn->bearerbox_connection, conn, msg);
304 
305  /* if this is an identification message from an smsbox instance */
306  if (msg_type(msg) == admin && msg->admin.command == cmd_identify) {
307  /*
308  * any smsbox sends this command even if boxc_id is NULL,
309  * but we will only consider real identified boxes
310  */
311  if (msg->admin.boxc_id != NULL) {
312 
313  /* and add the boxc_id into conn for boxc_status() output */
314  conn->boxc_id = msg->admin.boxc_id;
315  msg->admin.boxc_id = NULL;
316 
317  debug("sqlbox", 0, "smsbox_to_bearerbox: got boxc_id <%s> from <%s>",
318  octstr_get_cstr(conn->boxc_id),
319  octstr_get_cstr(conn->client_ip));
320  }
321  }
322  msg_destroy(msg);
323  }
324  conn->alive = 0;
325 }
326 
327 static Boxc *boxc_create(int fd, Octstr *ip, int ssl)
328 {
329  Boxc *boxc;
330 
331  boxc = gw_malloc(sizeof(Boxc));
332  boxc->smsbox_connection = conn_wrap_fd(fd, ssl);
333  boxc->bearerbox_connection = NULL;
334  boxc->client_ip = ip;
335  boxc->alive = 1;
336  boxc->connect_time = time(NULL);
337  boxc->boxc_id = NULL;
338  return boxc;
339 }
340 
341 static void boxc_destroy(Boxc *boxc)
342 {
343  if (boxc == NULL)
344  return;
345 
346  /* do nothing to the lists, as they are only references */
347 
348  if (boxc->smsbox_connection)
350  if (boxc->bearerbox_connection)
352  octstr_destroy(boxc->client_ip);
353  octstr_destroy(boxc->boxc_id);
354  gw_free(boxc);
355 }
356 
357 
358 static Boxc *accept_boxc(int fd, int ssl)
359 {
360  Boxc *newconn;
361  Octstr *ip;
362 
363  int newfd;
364  struct sockaddr_in client_addr;
365  socklen_t client_addr_len;
366 
367  client_addr_len = sizeof(client_addr);
368 
369  newfd = accept(fd, (struct sockaddr *)&client_addr, &client_addr_len);
370  if (newfd < 0)
371  return NULL;
372 
373  ip = host_ip(client_addr);
374 
375  // if (is_allowed_ip(box_allow_ip, box_deny_ip, ip) == 0) {
376  // info(0, "Box connection tried from denied host <%s>, disconnected",
377  // octstr_get_cstr(ip));
378  // octstr_destroy(ip);
379  // close(newfd);
380  // return NULL;
381  // }
382  newconn = boxc_create(newfd, ip, ssl);
383 
384  /*
385  * check if the SSL handshake was successfull, otherwise
386  * this is no valid box connection any more
387  */
388 #ifdef HAVE_LIBSSL
389  if (ssl && !conn_get_ssl(newconn->smsbox_connection))
390  return NULL;
391 #endif
392 
393  if (ssl)
394  info(0, "Client connected from <%s> using SSL", octstr_get_cstr(ip));
395  else
396  info(0, "Client connected from <%s>", octstr_get_cstr(ip));
397 
398 
399  /* XXX TODO: do the hand-shake, baby, yeah-yeah! */
400 
401  return newconn;
402 }
403 
404 
405 static void bearerbox_to_smsbox(void *arg)
406 {
407  Msg *msg, *msg_escaped;
408  Boxc *conn = arg;
409 
410  while (sqlbox_status == SQL_RUNNING && conn->alive) {
411 
412  msg = read_from_box(conn->bearerbox_connection, conn);
413 
414  if (msg == NULL) {
415  /* tell sqlbox to die */
416  conn->alive = 0;
417  debug("sqlbox", 0, "bearerbox_to_smsbox: connection to bearerbox died.");
418  break;
419  }
420  if (msg_type(msg) == admin) {
421  if (msg->admin.command == cmd_shutdown || msg->admin.command == cmd_restart) {
422  /* tell sqlbox to die */
423  conn->alive = 0;
424  debug("sqlbox", 0, "bearerbox_to_smsbox: Bearerbox told us to shutdown.");
425  break;
426  }
427  }
428 
429  if (msg_type(msg) == heartbeat) {
430  // todo
431  debug("sqlbox", 0, "bearerbox_to_smsbox: catch an heartbeat - we are alive");
432  msg_destroy(msg);
433  continue;
434  }
435  if (!conn->alive) {
436  msg_destroy(msg);
437  break;
438  }
439  if (msg_type(msg) == sms) {
440  msg_escaped = msg_duplicate(msg);
441  if (msg->sms.sms_type != report_mo) {
442  if (save_mo) {
443  gw_sql_save_msg(msg_escaped, octstr_imm("MO"));
444  }
445  }
446  else {
447  if (save_dlr) {
448  gw_sql_save_msg(msg_escaped, octstr_imm("DLR"));
449  }
450  }
451  msg_destroy(msg_escaped);
452  }
453  send_msg(conn->smsbox_connection, conn, msg);
454  msg_destroy(msg);
455  }
456  /* the client closes the connection, after that die in receiver */
457  conn->alive = 0;
458 }
459 
460 static void run_sqlbox(void *arg)
461 {
462  int fd;
463  Boxc *newconn;
464  long sender;
465 
466  fd = (int)arg;
467  newconn = accept_boxc(fd, sqlbox_port_ssl);
468  if (newconn == NULL) {
469  panic(0, "Socket accept failed");
470  return;
471  }
472  newconn->bearerbox_connection = connect_to_bearerbox_real(bearerbox_host, bearerbox_port, bearerbox_port_ssl, NULL /* bb_our_host */);
473  /* XXX add our_host if required */
474 
475 
476  sender = gwthread_create(bearerbox_to_smsbox, newconn);
477  if (sender == -1) {
478  error(0, "Failed to start a new thread, disconnecting client <%s>",
479  octstr_get_cstr(newconn->client_ip));
480  //goto cleanup;
481  }
482  smsbox_to_bearerbox(newconn);
483  gwthread_join(sender);
484  boxc_destroy(newconn);
485 }
486 
487 static void wait_for_connections(int fd, void (*function) (void *arg),
488  List *waited)
489 {
490  int ret;
491  int timeout = 10; /* 10 sec. */
492 
493  gw_assert(function != NULL);
494 
495  while(sqlbox_status == SQL_RUNNING) {
496 
497  ret = gwthread_pollfd(fd, POLLIN, 1.0);
498  if (sqlbox_status == SQL_SHUTDOWN) {
499  if (ret == -1 || !timeout)
500  break;
501  else
502  timeout--;
503  }
504 
505  if (ret > 0) {
506  gwthread_create(function, (void *)fd);
507  gwthread_sleep(1.0);
508  } else if (ret < 0) {
509  if(errno==EINTR) continue;
510  if(errno==EAGAIN) continue;
511  error(errno, "wait_for_connections failed");
512  }
513  }
514 }
515 
516 /*
517  * Identify ourself to bearerbox for smsbox-specific routing inside bearerbox.
518  * Do this even while no smsbox-id is given to unlock the sender thread in
519  * bearerbox.
520  */
522 {
523  Msg *msg;
524 
525  msg = msg_create(admin);
526  msg->admin.command = cmd_identify;
527  msg->admin.boxc_id = octstr_duplicate(conn->boxc_id);
528  send_msg(conn->bearerbox_connection, conn, msg);
529  msg_destroy(msg);
530 }
531 
532 static void bearerbox_to_sql(void *arg)
533 {
534  Boxc *conn = (Boxc *)arg;
535  Msg *msg, *mack;
536 
537  while (sqlbox_status == SQL_RUNNING && conn->alive) {
538  msg = read_from_box(conn->bearerbox_connection, conn);
539 
540  if (msg == NULL) { /* garbage/connection lost */
541  /* tell sqlbox to die */
542  conn->alive = 0;
544  debug("sqlbox", 0, "bearerbox_to_sql: connection to bearerbox died.");
545  break;
546  }
547  if (msg_type(msg) == heartbeat) {
548  // todo
549  debug("sqlbox", 0, "bearerbox_to_sql: catch an heartbeat - we are alive");
550  msg_destroy(msg);
551  continue;
552  }
553  /* if this is an identification message from an smsbox instance */
554  if (msg_type(msg) == admin && msg->admin.command == cmd_shutdown) {
555  /* tell sqlbox to die */
556  conn->alive = 0;
558  debug("sqlbox", 0, "bearerbox_to_sql: Bearerbox told us to shutdown.");
559  break;
560  }
561  if (msg_type(msg) == sms) {
562  if (msg->sms.sms_type != report_mo) {
563  if (save_mo) {
564  gw_sql_save_msg(msg, octstr_imm("MO"));
565  }
566  }
567  else {
568  if (save_dlr) {
569  gw_sql_save_msg(msg, octstr_imm("DLR"));
570  }
571  }
572 
573  /* create ack message */
574  mack = msg_create(ack);
575  mack->ack.nack = ack_success;
576  mack->ack.time = msg->sms.time;
577  uuid_copy(mack->ack.id, msg->sms.id);
578  send_msg(conn->bearerbox_connection, conn, mack);
579  msg_destroy(mack);
580 
581  }
582 
583  msg_destroy(msg);
584  }
585 }
586 
587 static void sql_single(Boxc *boxc)
588 {
589  Msg *msg;
590 
591  while (sqlbox_status == SQL_RUNNING && boxc->alive) {
592  if ((msg = gw_sql_fetch_msg()) != NULL) {
593  if (charset_processing(msg) == -1) {
594  error(0, "Could not charset process message, dropping it!");
595  msg_destroy(msg);
596  continue;
597  }
598  if (global_sender != NULL && (msg->sms.sender == NULL || octstr_len(msg->sms.sender) == 0)) {
599  msg->sms.sender = octstr_duplicate(global_sender);
600  }
601  /* convert validity and deferred to unix timestamp */
602  if (msg->sms.validity != SMS_PARAM_UNDEFINED)
603  msg->sms.validity = time(NULL) + msg->sms.validity * 60;
604  if (msg->sms.deferred != SMS_PARAM_UNDEFINED)
605  msg->sms.deferred = time(NULL) + msg->sms.deferred * 60;
606  send_msg(boxc->bearerbox_connection, boxc, msg);
607 
608  if (save_mt) {
609  /* convert validity & deferred back to minutes
610  * TODO clarify why we fetched message from DB and then insert it back here???
611  */
612  if (msg->sms.validity != SMS_PARAM_UNDEFINED)
613  msg->sms.validity = (msg->sms.validity - time(NULL))/60;
614  if (msg->sms.deferred != SMS_PARAM_UNDEFINED)
615  msg->sms.deferred = (msg->sms.deferred - time(NULL))/60;
616  gw_sql_save_msg(msg, octstr_imm("MT"));
617  }
618  }
619  else {
621  }
622  msg_destroy(msg);
623  }
624 }
625 
626 static void sql_list(Boxc *boxc)
627 {
628  Msg *msg;
629  List *qlist, *save_list;
630 
631  qlist = gwlist_create();
632  gwlist_add_producer(qlist);
633  save_list = gwlist_create();
634  gwlist_add_producer(save_list);
635 
636  while (sqlbox_status == SQL_RUNNING && boxc->alive) {
637  if ( gw_sql_fetch_msg_list(qlist, limit_per_cycle) > 0 ) {
638  while((gwlist_len(qlist)>0) && ((msg = gwlist_consume(qlist)) != NULL )) {
639  if (charset_processing(msg) == -1) {
640  error(0, "Could not charset process message, dropping it!");
641  msg_destroy(msg);
642  continue;
643  }
644  if (global_sender != NULL && (msg->sms.sender == NULL || octstr_len(msg->sms.sender) == 0)) {
645  msg->sms.sender = octstr_duplicate(global_sender);
646  }
647  /* convert validity and deferred to unix timestamp */
648  if (msg->sms.validity != SMS_PARAM_UNDEFINED)
649  msg->sms.validity = time(NULL) + msg->sms.validity * 60;
650  if (msg->sms.deferred != SMS_PARAM_UNDEFINED)
651  msg->sms.deferred = time(NULL) + msg->sms.deferred * 60;
652  send_msg(boxc->bearerbox_connection, boxc, msg);
653 
654  /* convert validity & deferred back to minutes */
655  if (save_mt && msg->sms.validity != SMS_PARAM_UNDEFINED)
656  msg->sms.validity = (msg->sms.validity - time(NULL))/60;
657  if (save_mt && msg->sms.deferred != SMS_PARAM_UNDEFINED)
658  msg->sms.deferred = (msg->sms.deferred - time(NULL))/60;
659  gwlist_produce(save_list, msg);
660  }
661  /* save_list also deletes and destroys messages */
662  gw_sql_save_list(save_list, octstr_imm("MT"), save_mt);
663  }
664  else {
666  }
667  }
668 
669  gwlist_remove_producer(qlist);
670  gwlist_remove_producer(save_list);
672  gwlist_destroy(save_list, msg_destroy_item);
673 }
674 
675 static void sql_to_bearerbox(void *arg)
676 {
677  Boxc *boxc;
678 
679  boxc = gw_malloc(sizeof(Boxc));
680  boxc->bearerbox_connection = connect_to_bearerbox_real(bearerbox_host, bearerbox_port, bearerbox_port_ssl, NULL /* bb_our_host */);
681  boxc->smsbox_connection = NULL;
682  boxc->client_ip = NULL;
683  boxc->alive = 1;
684  boxc->connect_time = time(NULL);
685  boxc->boxc_id = octstr_duplicate(sqlbox_id);
686  if (boxc->bearerbox_connection == NULL) {
687  boxc_destroy(boxc);
688  return;
689  }
690 
692  identify_to_bearerbox(boxc);
693 
694  if (gw_sql_fetch_msg_list == NULL || gw_sql_save_list == NULL || limit_per_cycle <= 1) {
695  sql_single(boxc);
696  }
697  else {
698  sql_list(boxc);
699  }
700 
701  boxc_destroy(boxc);
702 }
703 
704 static void sqlboxc_run(void *arg)
705 {
706  int fd;
707  int port;
708 
709  /* we will use one thread for SQL sms injections */
711 
712  port = (int)arg;
713 
714  fd = make_server_socket(port, NULL);
715  /* XXX add interface_name if required */
716 
717  if (fd < 0) {
718  panic(0, "Could not open sqlbox port %d", port);
719  }
720 
721  /*
722  * infinitely wait for new connections;
723  * to shut down the system, SIGTERM is send and then
724  * select drops with error, so we can check the status
725  */
726 
727  wait_for_connections(fd, run_sqlbox, NULL);
728 
729  /* close listen socket */
730  close(fd);
731 }
732 
733 
734 
735 /***********************************************************************
736  * Main program. Configuration, signal handling, etc.
737  */
738 
739 static void signal_handler(int signum) {
740  /* On some implementations (i.e. linuxthreads), signals are delivered
741  * to all threads. We only want to handle each signal once for the
742  * entire box, and we let the gwthread wrapper take care of choosing
743  * one.
744  */
745  if (!gwthread_shouldhandlesignal(signum))
746  return;
747 
748  switch (signum) {
749  case SIGINT:
750  if (sqlbox_status == SQL_RUNNING) {
751  error(0, "SIGINT received, aborting program...");
753  }
754  break;
755 
756  case SIGHUP:
757  warning(0, "SIGHUP received, catching and re-opening logs");
758  log_reopen();
759  alog_reopen();
760  break;
761  /*
762  * It would be more proper to use SIGUSR1 for this, but on some
763  * platforms that's reserved by the pthread support.
764  */
765  case SIGQUIT:
766  warning(0, "SIGQUIT received, reporting memory usage.");
767  gw_check_leaks();
768  break;
769  }
770 }
771 
772 
773 static void setup_signal_handlers(void) {
774  struct sigaction act;
775 
776  act.sa_handler = signal_handler;
777  sigemptyset(&act.sa_mask);
778  act.sa_flags = 0;
779  sigaction(SIGINT, &act, NULL);
780  sigaction(SIGQUIT, &act, NULL);
781  sigaction(SIGHUP, &act, NULL);
782  sigaction(SIGPIPE, &act, NULL);
783 }
784 
785 
786 
787 static void init_sqlbox(Cfg *cfg)
788 {
789  CfgGroup *grp;
790  Octstr *logfile;
791  long lvl;
792 
793  /* some default values */
794  sqlbox_port_ssl = 0;
796  bearerbox_port_ssl = 0;
797  logfile = NULL;
798  lvl = 0;
799 
800  /*
801  * first we take the port number in bearerbox and other values from the
802  * core group in configuration file
803  */
804 
805  grp = cfg_get_single_group(cfg, octstr_imm("sqlbox"));
806  if (cfg_get_integer(&bearerbox_port, grp, octstr_imm("bearerbox-port")) == -1)
807  panic(0, "Missing or bad 'bearerbox-port' in sqlbox group");
808 #ifdef HAVE_LIBSSL
809  cfg_get_bool(&bearerbox_port_ssl, grp, octstr_imm("smsbox-port-ssl"));
810  conn_config_ssl(grp);
811 #endif
812 
813  grp = cfg_get_single_group(cfg, octstr_imm("sqlbox"));
814  if (grp == NULL)
815  panic(0, "No 'sqlbox' group in configuration");
816 
817  bearerbox_host = cfg_get( grp, octstr_imm("bearerbox-host"));
818  if (bearerbox_host == NULL)
819  bearerbox_host = octstr_create(BB_DEFAULT_HOST);
820 
821  sqlbox_id = cfg_get(grp, octstr_imm("smsbox-id"));
822  global_sender = cfg_get(grp, octstr_imm("global-sender"));
823 
824  if (cfg_get_integer(&sqlbox_port, grp, octstr_imm("smsbox-port")) == -1)
825  sqlbox_port = 13005;
826 
827  /* setup limit per cycle */
828  if (cfg_get_integer(&limit_per_cycle, grp, octstr_imm("limit-per-cycle")) == -1)
830 
831  /* set up save parameters */
832  if (cfg_get_bool(&save_mo, grp, octstr_imm("save-mo")) == -1)
833  save_mo = 1;
834 
835  if (cfg_get_bool(&save_mt, grp, octstr_imm("save-mt")) == -1)
836  save_mt = 1;
837 
838  if (cfg_get_bool(&save_dlr, grp, octstr_imm("save-dlr")) == -1)
839  save_dlr = 1;
840 
841  /* setup logfile stuff */
842  logfile = cfg_get(grp, octstr_imm("log-file"));
843 
844  cfg_get_integer(&lvl, grp, octstr_imm("log-level"));
845 
846  if (logfile != NULL) {
847  info(0, "Starting to log to file %s level %ld",
848  octstr_get_cstr(logfile), lvl);
849  log_open(octstr_get_cstr(logfile), lvl, GW_NON_EXCL);
850  octstr_destroy(logfile);
851  }
852 
853  sql_type = sqlbox_init_sql(cfg);
854  if (sql_type == NULL) {
855  panic(0, "No proper SQL server defined.");
856  }
857 
858  gw_sql_enter(cfg);
859 
861 }
862 
863 static int check_args(int i, int argc, char **argv) {
864  if (strcmp(argv[i], "-H")==0 || strcmp(argv[i], "--tryhttp")==0) {
865  //only_try_http = 1;
866  } else {
867  return -1;
868  }
869 
870  return 0;
871 }
872 
873 int main(int argc, char **argv)
874 {
875  int cf_index;
876  Octstr *filename;
877 
878  gwlib_init();
879 
880  cf_index = get_and_set_debugs(argc, argv, check_args);
882 
883  if (argv[cf_index] == NULL) {
884  filename = octstr_create("sqlbox.conf");
885  } else {
886  filename = octstr_create(argv[cf_index]);
887  }
888 
889  cfg = cfg_create(filename);
890 
891  /* Adding cfg-checks to core */
892 
894 
895  if (cfg_read(cfg) == -1)
896  panic(0, "Couldn't read configuration from `%s'.", octstr_get_cstr(filename));
897 
898  octstr_destroy(filename);
899 
900  report_versions("sqlbox");
901 
902  init_sqlbox(cfg);
903 
904  sqlboxc_run((void *)sqlbox_port);
905 
906  cfg_destroy(cfg);
907  if (restart_sqlbox) {
908  gwthread_sleep(1.0);
909  }
910 
911  gw_sql_leave();
912  gwlib_shutdown();
913 
914  if (restart_sqlbox)
915  execvp(argv[0], argv);
916  return 0;
917 }
Connection * conn
Definition: bb_boxc.c:135
void error(int err, const char *fmt,...)
Definition: log.c:648
#define gw_sql_leave
Definition: sqlbox_sql.h:52
static void sqlboxc_run(void *arg)
Definition: sqlbox.c:704
void info(int err, const char *fmt,...)
Definition: log.c:672
static long limit_per_cycle
Definition: sqlbox.c:88
Msg * msg_duplicate(Msg *msg)
Definition: msg.c:111
static int bearerbox_port_ssl
Definition: sqlbox.c:86
#define gw_sql_fetch_msg
Definition: sqlbox_sql.h:42
#define gw_sql_save_list
Definition: sqlbox_sql.h:44
static Octstr * bearerbox_host
Definition: sqlbox.c:85
static void wait_for_connections(int fd, void(*function)(void *arg), List *waited)
Definition: sqlbox.c:487
Connection * connect_to_bearerbox_real(Octstr *host, int port, int ssl, Octstr *our_host)
Definition: shared.c:83
static void sql_to_bearerbox(void *arg)
Definition: sqlbox.c:675
gw_assert(wtls_machine->packet_to_send !=NULL)
int ssl
int read_from_bearerbox_real(Connection *conn, Msg **msg, double seconds)
Definition: shared.c:172
Octstr * client_ip
Definition: opensmppbox.c:152
static void setup_signal_handlers(void)
Definition: sqlbox.c:773
Definition: msg.h:109
void log_reopen(void)
Definition: log.c:297
static int send_msg(Connection *conn, Boxc *boxconn, Msg *pmsg)
Definition: sqlbox.c:253
void gwlist_produce(List *list, void *item)
Definition: list.c:411
#define SLEEP_BETWEEN_EMPTY_SELECTS
Definition: sqlbox.c:108
void gwthread_join(long thread)
long gwlist_len(List *list)
Definition: list.c:166
static Msg * read_from_box(Connection *conn, Boxc *boxconn)
Definition: sqlbox.c:221
static void smsbox_to_bearerbox(void *arg)
Definition: sqlbox.c:272
int octstr_url_decode(Octstr *ostr)
Definition: octstr.c:1744
#define BB_DEFAULT_HOST
Definition: bb.h:67
msg_type
Definition: msg.h:73
#define cfg_get(grp, varname)
Definition: cfg.h:86
static int sqlbox_is_allowed_in_group(Octstr *group, Octstr *variable)
Definition: sqlbox.c:126
static void signal_handler(int signum)
Definition: sqlbox.c:739
#define msg_create(type)
Definition: msg.h:136
int gwthread_shouldhandlesignal(int signal)
static int charset_processing(Msg *msg)
Definition: sqlbox.c:182
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
#define gw_sql_fetch_msg_list
Definition: sqlbox_sql.h:43
static volatile sig_atomic_t restart_sqlbox
Definition: sqlbox.c:77
Cfg * cfg_create(Octstr *filename)
Definition: cfg.c:318
static Boxc * boxc_create(int fd, Octstr *ip, int ssl)
Definition: sqlbox.c:327
Connection * bearerbox_connection
Definition: opensmppbox.c:142
volatile sig_atomic_t alive
Definition: opensmppbox.c:158
int cfg_read(Cfg *cfg)
Definition: cfg.c:452
static void sql_list(Boxc *boxc)
Definition: sqlbox.c:626
static int check_args(int i, int argc, char **argv)
Definition: sqlbox.c:863
static int port
Definition: fakesmsc.c:120
#define POLLIN
Definition: gwpoll.h:91
void msg_destroy_item(void *msg)
Definition: msg.c:147
Octstr * octstr_imm(const char *cstr)
Definition: octstr.c:281
void cfg_add_hooks(void *allowed, void *single)
Definition: cfg.c:253
Definition: msg.h:79
Definition: cfg.c:164
Octstr * boxc_id
Definition: opensmppbox.c:159
void cfg_destroy(Cfg *cfg)
Definition: cfg.c:331
void conn_config_ssl(CfgGroup *grp)
Definition: conn.c:1538
void gwlist_remove_producer(List *list)
Definition: list.c:401
void conn_destroy(Connection *conn)
Definition: conn.c:621
static long sqlbox_port
Definition: sqlbox.c:82
#define octstr_duplicate(ostr)
Definition: octstr.h:187
static Boxc * accept_boxc(int fd, int ssl)
Definition: sqlbox.c:358
struct _boxc Boxc
void uuid_copy(uuid_t dst, const uuid_t src)
Definition: gw_uuid.c:150
static void bearerbox_to_smsbox(void *arg)
Definition: sqlbox.c:405
void msg_destroy(Msg *msg)
Definition: msg.c:132
int make_server_socket(int port, const char *interface_name)
Definition: socket.c:93
time_t connect_time
Definition: opensmppbox.c:150
void warning(int err, const char *fmt,...)
Definition: log.c:660
#define DEFAULT_LIMIT_PER_CYCLE
Definition: sqlbox.c:109
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:322
#define SQL_RUNNING
Definition: sqlbox.c:81
char filename[FILENAME_MAX+1]
Definition: log.c:171
#define gwthread_create(func, arg)
Definition: gwthread.h:90
#define octstr_create(cstr)
Definition: octstr.h:125
#define gw_sql_save_msg(message, table)
Definition: sqlbox_sql.h:45
void gwthread_sleep(double seconds)
static volatile sig_atomic_t sqlbox_status
Definition: sqlbox.c:78
#define SMS_PARAM_UNDEFINED
Definition: sms.h:91
static int save_dlr
Definition: sqlbox.c:89
static void boxc_destroy(Boxc *boxc)
Definition: sqlbox.c:341
int main(int argc, char **argv)
Definition: sqlbox.c:873
int conn_write_withlen(Connection *conn, Octstr *data)
Definition: conn.c:1069
static void run_sqlbox(void *arg)
Definition: sqlbox.c:460
int gwthread_pollfd(int fd, int events, double timeout)
static void init_sqlbox(Cfg *cfg)
Definition: sqlbox.c:787
void alog_reopen(void)
Definition: accesslog.c:85
struct server_type * sqlbox_init_sql(Cfg *cfg)
Definition: sqlbox_sql.c:4
void report_versions(const char *boxname)
Definition: utils.c:539
int log_open(char *filename, int level, enum excl_state excl)
Definition: log.c:375
static int sqlbox_port_ssl
Definition: sqlbox.c:83
long octstr_len(const Octstr *ostr)
Definition: octstr.c:340
int cfg_get_bool(int *n, CfgGroup *grp, Octstr *varname)
Definition: cfg.c:756
Definition: octstr.c:118
void * gwlist_consume(List *list)
Definition: list.c:427
static void identify_to_bearerbox(Boxc *conn)
Definition: sqlbox.c:521
Octstr * host_ip(struct sockaddr_in addr)
Definition: socket.c:615
Connection * smsbox_connection
Definition: sqlbox.c:112
static void sql_single(Boxc *boxc)
Definition: sqlbox.c:587
static Cfg * cfg
Definition: sqlbox.c:75
void debug(const char *place, int err, const char *fmt,...)
Definition: log.c:726
static void bearerbox_to_sql(void *arg)
Definition: sqlbox.c:532
int cfg_get_integer(long *n, CfgGroup *grp, Octstr *varname)
Definition: cfg.c:739
#define panic
Definition: log.h:87
Definition: cfg.c:73
int socklen_t
Definition: socket.h:73
static int save_mo
Definition: sqlbox.c:89
void gwlib_shutdown(void)
Definition: gwlib.c:94
Octstr * msg_pack(Msg *msg)
Definition: msg.c:181
#define gwlist_create()
Definition: list.h:136
#define SQL_SHUTDOWN
Definition: sqlbox.c:80
enum msg_type type
Definition: msg.h:80
#define gw_sql_enter
Definition: sqlbox_sql.h:51
static long bearerbox_port
Definition: sqlbox.c:84
void gwlib_init(void)
Definition: gwlib.c:78
CfgGroup * cfg_get_single_group(Cfg *cfg, Octstr *name)
Definition: cfg.c:636
void gwlist_add_producer(List *list)
Definition: list.c:383
Octstr * sqlbox_id
Definition: sqlbox.c:106
#define BB_DEFAULT_SMSBOX_PORT
Definition: bb.h:68
int get_and_set_debugs(int argc, char **argv, int(*find_own)(int index, int argc, char **argv))
Definition: utils.c:626
static Octstr * global_sender
Definition: sqlbox.c:87
struct server_type * sql_type
Definition: list.c:102
static XMLRPCDocument * msg
Definition: test_xmlrpc.c:86
static int sqlbox_is_single_group(Octstr *query)
Definition: sqlbox.c:158
static int save_mt
Definition: sqlbox.c:89
#define DC_UCS2
Definition: sms.h:112
Connection * conn_wrap_fd(int fd, int ssl)
Definition: conn.c:560
int charset_convert(Octstr *string, char *charset_from, char *charset_to)
Definition: charset.c:589
#define DC_7BIT
Definition: sms.h:110
void gwlist_destroy(List *list, gwlist_item_destructor_t *destructor)
Definition: list.c:145
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.