Kannel: Open Source WAP and SMS gateway  $Revision: 5037 $
smsc_wrapper.c
Go to the documentation of this file.
1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2016 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  * SMSC Connection wrapper
59  *
60  * Interface to old SMS center implementations
61  *
62  * Kalle Marjola 2000
63  */
64 
65 #include "gwlib/gwlib.h"
66 #include "smscconn.h"
67 #include "smscconn_p.h"
68 #include "bb_smscconn_cb.h"
69 
70 #include "smsc.h"
71 #include "smsc_p.h"
72 
73 
74 typedef struct smsc_wrapper {
77  List *stopped; /* list-trick for suspend/isolate */
81 } SmscWrapper;
82 
83 
84 static void smscwrapper_destroy(SmscWrapper *wrap)
85 {
86  if (wrap == NULL)
87  return;
88  gwlist_destroy(wrap->outgoing_queue, NULL);
89  gwlist_destroy(wrap->stopped, NULL);
91  if (wrap->smsc != NULL)
92  smsc_close(wrap->smsc);
93  gw_free(wrap);
94 }
95 
96 
97 static int reconnect(SMSCConn *conn)
98 {
99  SmscWrapper *wrap = conn->data;
100  Msg *msg;
101  int ret;
102  int wait = 1;
103 
104  /* disable double-reconnect
105  * NOTE: it is still possible that we do double-connect if
106  * first thread gets through this if-statement and then
107  * execution switches to another thread.. this can be avoided
108  * via double-mutex system, but I do not feel it is worth it,
109  * maybe later --rpr
110  */
111  if (conn->status == SMSCCONN_RECONNECTING) {
112  mutex_lock(wrap->reconnect_mutex); /* wait here */
114  return 0;
115  }
117 
118  debug("bb.sms", 0, "smsc_wrapper <%s>: reconnect started",
119  octstr_get_cstr(conn->name));
120 
121  while((msg = gwlist_extract_first(wrap->outgoing_queue))!=NULL) {
123  }
125 
126 
127  while(conn->why_killed == SMSCCONN_ALIVE) {
128  ret = smsc_reopen(wrap->smsc);
129  if (ret == 0) {
130  info(0, "Re-open of %s succeeded.", octstr_get_cstr(conn->name));
131  mutex_lock(conn->flow_mutex);
132  conn->status = SMSCCONN_ACTIVE;
133  conn->connect_time = time(NULL);
134  mutex_unlock(conn->flow_mutex);
135  bb_smscconn_connected(conn);
136  break;
137  }
138  else if (ret == -2) {
139  error(0, "Re-open of %s failed permanently",
140  octstr_get_cstr(conn->name));
141  mutex_lock(conn->flow_mutex);
144  mutex_unlock(conn->flow_mutex);
145  return -1; /* permanent failure */
146  }
147  else {
148  error(0, "Re-open to <%s> failed, retrying after %d minutes...",
149  octstr_get_cstr(conn->name), wait);
150  gwthread_sleep(wait*60.0);
151 
152  wait = wait > 10 ? 10 : wait * 2 + 1;
153  }
154  }
156  return 0;
157 }
158 
159 
160 static Msg *sms_receive(SMSCConn *conn)
161 {
162  SmscWrapper *wrap = conn->data;
163  int ret;
164  Msg *newmsg = NULL;
165 
166  if (smscenter_pending_smsmessage(wrap->smsc) == 1) {
167 
168  ret = smscenter_receive_msg(wrap->smsc, &newmsg);
169  if (ret == 1) {
170 
171  /* if any smsc_id available, use it */
172  newmsg->sms.smsc_id = octstr_duplicate(conn->id);
173 
174  return newmsg;
175  } else if (ret == 0) { /* "NEVER" happens */
176  warning(0, "SMSC %s: Pending message returned '1', "
177  "but nothing to receive!", octstr_get_cstr(conn->name));
178  msg_destroy(newmsg);
179  return NULL;
180  } else {
181  msg_destroy(newmsg);
182  if (reconnect(conn) == -1)
183  smscconn_shutdown(conn, 0);
184  return NULL;
185  }
186  }
187  return NULL;
188 }
189 
190 
191 static void wrapper_receiver(void *arg)
192 {
193  Msg *msg;
194  SMSCConn *conn = arg;
195  SmscWrapper *wrap = conn->data;
196  /* SmscWrapper *wrap = conn->data; ** non-used */
197  double sleep = 0.0001;
198 
199  /* Make sure we log into our own log-file if defined */
200  log_thread_to(conn->log_idx);
201 
202  /* remove messages from SMSC until we are killed */
203  while(conn->why_killed == SMSCCONN_ALIVE) {
204 
205  gwlist_consume(wrap->stopped); /* block here if suspended/isolated */
206 
207  msg = sms_receive(conn);
208  if (msg) {
209  debug("bb.sms", 0, "smscconn (%s): new message received",
210  octstr_get_cstr(conn->name));
211  sleep = 0.0001;
212  bb_smscconn_receive(conn, msg);
213  }
214  else {
215  /* note that this implementations means that we sleep even
216  * when we fail connection.. but time is very short, anyway
217  */
218  gwthread_sleep(sleep);
219  /* gradually sleep longer and longer times until something starts to
220  * happen - this of course reduces response time, but that's better than
221  * extensive CPU usage when it is not used
222  */
223  sleep *= 2;
224  if (sleep >= 2.0)
225  sleep = 1.999999;
226  }
227  }
229 
230  /* this thread is joined at sender */
231 }
232 
233 
234 
235 static int sms_send(SMSCConn *conn, Msg *msg)
236 {
237  SmscWrapper *wrap = conn->data;
238  int ret;
239 
240  debug("bb.sms", 0, "smscconn_sender (%s): sending message",
241  octstr_get_cstr(conn->name));
242 
243  ret = smscenter_submit_msg(wrap->smsc, msg);
244  if (ret == -1) {
245  bb_smscconn_send_failed(conn, msg,
247 
248  if (reconnect(conn) == -1)
249  smscconn_shutdown(conn, 0);
250  return -1;
251  } else {
252  bb_smscconn_sent(conn, msg, NULL);
253  return 0;
254  }
255 }
256 
257 
258 static void wrapper_sender(void *arg)
259 {
260  Msg *msg;
261  SMSCConn *conn = arg;
262  SmscWrapper *wrap = conn->data;
263 
264  /* Make sure we log into our own log-file if defined */
265  log_thread_to(conn->log_idx);
266 
267  /* send messages to SMSC until our outgoing_list is empty and
268  * no producer anymore (we are set to shutdown) */
269  while(conn->status != SMSCCONN_DEAD) {
270 
271  if ((msg = gwlist_consume(wrap->outgoing_queue)) == NULL)
272  break;
273 
274  if (octstr_search_char(msg->sms.receiver, ' ', 0) != -1) {
275  /*
276  * multi-send: this should be implemented in corresponding
277  * SMSC protocol, but while we are waiting for that...
278  */
279  int i;
280  Msg *newmsg;
281  /* split from spaces: in future, split with something more sensible,
282  * this is dangerous... (as space is url-encoded as '+')
283  */
284  List *nlist = octstr_split_words(msg->sms.receiver);
285 
286  debug("bb.sms", 0, "Handling multi-receiver message");
287 
288  for(i=0; i < gwlist_len(nlist); i++) {
289 
290  newmsg = msg_duplicate(msg);
291  octstr_destroy(newmsg->sms.receiver);
292 
293  newmsg->sms.receiver = gwlist_get(nlist, i);
294  sms_send(conn, newmsg);
295  }
296  gwlist_destroy(nlist, NULL);
297  msg_destroy(msg);
298  }
299  else
300  sms_send(conn,msg);
301 
302  }
303  /* cleanup, we are now dying */
304 
305  debug("bb.sms", 0, "SMSCConn %s sender died, waiting for receiver",
306  octstr_get_cstr(conn->name));
307 
309 
310  if (conn->is_stopped) {
312  conn->is_stopped = 0;
313  }
314 
317 
318  /* call 'failed' to all messages still in queue */
319 
320  mutex_lock(conn->flow_mutex);
321 
322  conn->status = SMSCCONN_DEAD;
323 
324  while((msg = gwlist_extract_first(wrap->outgoing_queue))!=NULL) {
326  }
327  smscwrapper_destroy(wrap);
328  conn->data = NULL;
329 
330  mutex_unlock(conn->flow_mutex);
331 
333 }
334 
335 
336 
337 static int wrapper_add_msg(SMSCConn *conn, Msg *sms)
338 {
339  SmscWrapper *wrap = conn->data;
340  Msg *copy;
341 
342  copy = msg_duplicate(sms);
343  gwlist_produce(wrap->outgoing_queue, copy);
344 
345  return 0;
346 }
347 
348 
349 static int wrapper_shutdown(SMSCConn *conn, int finish_sending)
350 {
351  SmscWrapper *wrap = conn->data;
352 
353  debug("bb.sms", 0, "Shutting down SMSCConn %s, %s",
354  octstr_get_cstr(conn->name), finish_sending ? "slow" : "instant");
355 
356  if (finish_sending == 0) {
357  Msg *msg;
358  while((msg = gwlist_extract_first(wrap->outgoing_queue))!=NULL) {
360  }
361  }
365  return 0;
366 }
367 
368 static void wrapper_stop(SMSCConn *conn)
369 {
370  SmscWrapper *wrap = conn->data;
371 
372  debug("smscconn", 0, "Stopping wrapper");
374 
375 }
376 
377 static void wrapper_start(SMSCConn *conn)
378 {
379  SmscWrapper *wrap = conn->data;
380 
381  debug("smscconn", 0, "Starting wrapper");
383 }
384 
385 
386 static long wrapper_queued(SMSCConn *conn)
387 {
388  SmscWrapper *wrap = conn->data;
389  long ret = gwlist_len(wrap->outgoing_queue);
390 
391  /* use internal queue as load, maybe something else later */
392 
393  conn->load = ret;
394  return ret;
395 }
396 
398 {
399  /* 1. Call smsc_open()
400  * 2. create sender/receiver threads
401  * 3. fill up the conn
402  *
403  * XXX open() SHOULD be done in distinct thread, not here!
404  */
405 
406  SmscWrapper *wrap;
407 
408  wrap = gw_malloc(sizeof(SmscWrapper));
409  wrap->smsc = NULL;
410  conn->data = wrap;
411  conn->send_msg = wrapper_add_msg;
412 
413 
414  wrap->outgoing_queue = gwlist_create();
415  wrap->stopped = gwlist_create();
416  wrap->reconnect_mutex = mutex_create();
418 
419  if ((wrap->smsc = smsc_open(cfg)) == NULL)
420  goto error;
421 
422  conn->name = octstr_create(smsc_name(wrap->smsc));
423  conn->status = SMSCCONN_ACTIVE;
424  conn->connect_time = time(NULL);
425 
426  if (conn->is_stopped)
428 
429 
430  /* XXX here we could fail things... specially if the second one
431  * fails.. so fix this ASAP
432  *
433  * moreover, open should be in sender/receiver, so that we can continue
434  * while trying to open... maybe move this, or just wait for new
435  * implementations of various SMSC protocols
436  */
437 
438  if ((wrap->receiver_thread = gwthread_create(wrapper_receiver, conn))==-1)
439  goto error;
440 
441  if ((wrap->sender_thread = gwthread_create(wrapper_sender, conn))==-1)
442  goto error;
443 
444  conn->shutdown = wrapper_shutdown;
445  conn->queued = wrapper_queued;
446  conn->stop_conn = wrapper_stop;
447  conn->start_conn = wrapper_start;
448 
449  return 0;
450 
451 error:
452  error(0, "Failed to create Smsc wrapper");
453  conn->data = NULL;
454  smscwrapper_destroy(wrap);
456  conn->status = SMSCCONN_DEAD;
457  return -1;
458 }
459 
460 
461 
Octstr * name
Definition: smscconn_p.h:173
void error(int err, const char *fmt,...)
Definition: log.c:612
void info(int err, const char *fmt,...)
Definition: log.c:636
Msg * msg_duplicate(Msg *msg)
Definition: msg.c:111
void bb_smscconn_connected(SMSCConn *conn)
Definition: bb_smscconn.c:192
static int sms_send(SMSCConn *conn, Msg *msg)
Definition: smsc_wrapper.c:235
List * outgoing_queue
Definition: smsc_wrapper.c:76
#define mutex_unlock(m)
Definition: thread.h:136
void smscconn_shutdown(SMSCConn *conn, int finish_sending)
Definition: smscconn.c:378
List * stopped
Definition: smsc_wrapper.c:77
void bb_smscconn_killed(void)
Definition: bb_smscconn.c:199
void gwlist_produce(List *list, void *item)
Definition: list.c:411
Octstr * id
Definition: smscconn_p.h:174
void gwthread_join(long thread)
long gwlist_len(List *list)
Definition: list.c:166
void * data
Definition: smscconn_p.h:249
static int reconnect(SMSCConn *conn)
Definition: smsc_wrapper.c:97
#define mutex_create()
Definition: thread.h:96
char * smsc_name(SMSCenter *smsc)
Definition: smsc.c:643
void * gwlist_get(List *list, long pos)
Definition: list.c:292
void(* stop_conn)(SMSCConn *conn)
Definition: smscconn_p.h:246
int log_idx
Definition: smscconn_p.h:197
static void wrapper_sender(void *arg)
Definition: smsc_wrapper.c:258
long receiver_thread
Definition: smsc_wrapper.c:78
int smsc_wrapper_create(SMSCConn *conn, CfgGroup *cfg)
Definition: smsc_wrapper.c:397
int smscenter_receive_msg(SMSCenter *smsc, Msg **msg)
Definition: smsc.c:251
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
void(* start_conn)(SMSCConn *conn)
Definition: smscconn_p.h:245
long octstr_search_char(const Octstr *ostr, int ch, long pos)
Definition: octstr.c:1010
void log_thread_to(int idx)
Definition: log.c:723
static void wrapper_stop(SMSCConn *conn)
Definition: smsc_wrapper.c:368
Mutex * reconnect_mutex
Definition: smsc_wrapper.c:80
static int wrapper_add_msg(SMSCConn *conn, Msg *sms)
Definition: smsc_wrapper.c:337
SMSCenter * smsc
Definition: smsc_wrapper.c:75
smscconn_killed_t why_killed
Definition: smscconn_p.h:153
SMSCenter * smsc_open(CfgGroup *grp)
Definition: smsc.c:444
static void smscwrapper_destroy(SmscWrapper *wrap)
Definition: smsc_wrapper.c:84
Definition: msg.h:79
void * gwlist_extract_first(List *list)
Definition: list.c:305
int smsc_reopen(SMSCenter *smsc)
Definition: smsc.c:610
void gwlist_remove_producer(List *list)
Definition: list.c:401
long bb_smscconn_receive(SMSCConn *conn, Msg *sms)
Definition: bb_smscconn.c:478
time_t connect_time
Definition: smscconn_p.h:155
int smscenter_pending_smsmessage(SMSCenter *smsc)
Definition: smsc.c:302
static void wrapper_start(SMSCConn *conn)
Definition: smsc_wrapper.c:377
#define octstr_duplicate(ostr)
Definition: octstr.h:187
Mutex * flow_mutex
Definition: smscconn_p.h:157
void msg_destroy(Msg *msg)
Definition: msg.c:132
void warning(int err, const char *fmt,...)
Definition: log.c:624
List * octstr_split_words(const Octstr *ostr)
Definition: octstr.c:1600
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:322
#define gwthread_create(func, arg)
Definition: gwthread.h:90
#define octstr_create(cstr)
Definition: octstr.h:125
static int wrapper_shutdown(SMSCConn *conn, int finish_sending)
Definition: smsc_wrapper.c:349
void gwthread_sleep(double seconds)
void mutex_destroy(Mutex *mutex)
Definition: thread.c:97
volatile sig_atomic_t is_stopped
Definition: smscconn_p.h:169
int smsc_close(SMSCenter *smsc)
Definition: smsc.c:648
static void wrapper_receiver(void *arg)
Definition: smsc_wrapper.c:191
static Cfg * cfg
Definition: smsbox.c:115
void bb_smscconn_sent(SMSCConn *conn, Msg *sms, Octstr *reply)
Definition: bb_smscconn.c:279
void * gwlist_consume(List *list)
Definition: list.c:427
int(* shutdown)(SMSCConn *conn, int finish_sending)
Definition: smscconn_p.h:229
static Msg * sms_receive(SMSCConn *conn)
Definition: smsc_wrapper.c:160
void debug(const char *place, int err, const char *fmt,...)
Definition: log.c:690
void gwthread_wakeup(long thread)
Definition: cfg.c:73
smscconn_status_t status
Definition: smscconn_p.h:151
#define gwlist_create()
Definition: list.h:136
long(* queued)(SMSCConn *conn)
Definition: smscconn_p.h:240
int(* send_msg)(SMSCConn *conn, Msg *msg)
Definition: smscconn_p.h:235
Definition: thread.h:76
long sender_thread
Definition: smsc_wrapper.c:79
void bb_smscconn_send_failed(SMSCConn *conn, Msg *sms, int reason, Octstr *reply)
Definition: bb_smscconn.c:328
void gwlist_add_producer(List *list)
Definition: list.c:383
#define mutex_lock(m)
Definition: thread.h:130
Definition: list.c:102
int smscenter_submit_msg(SMSCenter *smsc, Msg *msg)
Definition: smsc.c:209
static XMLRPCDocument * msg
Definition: test_xmlrpc.c:86
static long wrapper_queued(SMSCConn *conn)
Definition: smsc_wrapper.c:386
struct smsc_wrapper SmscWrapper
int load
Definition: smscconn_p.h:152
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.