[PATCH] (quasi) non-persistent SMPP - Non solicited feature

"Josh McAllister" <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <[email protected]>
This patch adds the ability to set smpp connection idle limits, and max
submits per connection. This is a requirement for Sprint's SMSC. 

Adds 2 smsc group config directives:
sms-max-submits
and
sms-max-idle

The patch does not change existing behavior if these are not set, or set
to 0. If sms-max-submits is set, it will shutdown the connection (unbind
/ wait for unbind_resp / etc) once we've submitted sms-max-submits
messages, and immediately re-connect. Likewise, sms-max-idle will
shutdown and immediately reconnect if we have submitted no messages in >
sms-max-idle seconds.

Note: the test for max-idle will not be executed while we're sitting
around doing nothing, so make sure that enquire-link-interval is set to
some number < max seconds you can be late disconnecting. In other words,
if sms-max-idle is 30 seconds, and enquire-link-interval is 30 seconds,
it could be 1 minute before we disconnect.

This is my first patch submission so please be gentle... but of course
feedback is appreciated.

Thanks,

Josh McAllister
non-persistent.patch (application/octet-stream, 12.7 KB)
Index: gw/smsc/smsc_smpp.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_smpp.c,v
retrieving revision 1.94
diff -u -r1.94 smsc_smpp.c
--- gw/smsc/smsc_smpp.c	1 Jun 2006 21:39:19 -0000	1.94
+++ gw/smsc/smsc_smpp.c	6 Jun 2006 21:43:07 -0000
@@ -157,6 +157,8 @@
     int priority;       /* set default priority for messages */    
     int validityperiod;    
     time_t throttling_err_time;
+    time_t last_msg_time;  /*when the last message was sent for this thread*/
+    int    msg_count;      /*message count for this thread*/
     int smpp_msg_id_type;  /* msg id in C string, hex or decimal */
     int autodetect_addr;
     Octstr *alt_charset;
@@ -1014,6 +1016,34 @@
             smpp_pdu_destroy(pdu);
             octstr_destroy(os);
             ++(*pending_submits);
+	    				/*______________________________________
+					  This section manages the SMSC
+					  connection limits. Two limits are
+					  enforced here. The first is an idle
+					  time limit. This portion of code
+					  merely performs a timestamp. The
+					  check will be done by the code that
+					  calls this function. If no timeout
+					  was specified, no timestamp will be
+					  performed. A NULL value for the
+					  current time inicates that no timeout
+					  was specified.
+
+					  The second limitation is on the
+					  number of messages that can be sent
+					  during a connection session. For this
+					  limitation, the code below will
+					  perform a "count-down". The initial
+					  value to count down from is provided
+					  by the caller of this function. If
+					  that value is 0, no count down will
+					  be performed. The message count has
+					  been exceeded if the count gets to
+					  "1".
+	    				______________________________________*/
+			smpp->last_msg_time = (smpp->last_msg_time)?time(NULL):(time_t)0;
+			smpp->msg_count -= (smpp->msg_count)? 1:0;
+			if(smpp->msg_count == 1) break;
             /*
              * obey throughput speed limit, if any.
              */
@@ -1265,9 +1295,9 @@
             tmp = octstr_duplicate(msgid);
         } else {
             if (smpp->smpp_msg_id_type & 0x02) {
-                tmp = octstr_format("%lu", strtoll(octstr_get_cstr(msgid), NULL, 16));
+                tmp = octstr_format("%ld", strtol(octstr_get_cstr(msgid), NULL, 16));
             } else {
-                tmp = octstr_format("%lu", strtoll(octstr_get_cstr(msgid), NULL, 10));
+                tmp = octstr_format("%ld", strtol(octstr_get_cstr(msgid), NULL, 10));
             }
         }
 
@@ -1481,10 +1511,10 @@
                     tmp = octstr_duplicate(pdu->u.submit_sm_resp.message_id);
                 } else {
                     if (smpp->smpp_msg_id_type & 0x01) {
-                        tmp = octstr_format("%lu", strtoll(  /* hex */
+                        tmp = octstr_format("%ld", strtol(  /* hex */
                             octstr_get_cstr(pdu->u.submit_sm_resp.message_id), NULL, 16));
                     } else {
-                        tmp = octstr_format("%lu", strtoll(  /* decimal */
+                        tmp = octstr_format("%ld", strtol(  /* decimal */
                             octstr_get_cstr(pdu->u.submit_sm_resp.message_id), NULL, 10));
                     }
                 }
@@ -1723,6 +1753,26 @@
     struct io_arg *io_arg;
     int transmitter;
     Connection *conn;
+    					/*______________________________________
+					  This variable is used for 2 purposes.
+					  Initially, it is the return value
+					  obtained from the "read_pdu()" 
+					  function. A value of "1" indicates a
+					  successful read, and causes the read
+					  loop to continue. A value of "0" is
+					  the normal loop exit condition. A
+					  value of "-1" is an error loop exit
+					  condition.
+
+					  The second use for this variable is
+					  to specify that a message timeout or
+					  message count limit has been
+					  exceeded. This will be the case if
+					  (ret == 1).  This will be the 
+					  meaning of the variable as it is
+					  evaluated OUTSIDE of the 
+					  "read_pdu()" loop.
+    					______________________________________*/
     int ret;
     long last_enquire_sent;
     long pending_submits;
@@ -1730,9 +1780,31 @@
     SMPP_PDU *pdu;
     double timeout;
     time_t last_response, last_cleanup;
+    int max_msg_count;
+    int reconnect;
+    time_t max_msg_time;
 
     io_arg = arg;
     smpp = io_arg->smpp;
+    					/*______________________________________
+					  These lines retrive the SMSC
+					  connection limits that were packaged
+					  up by the caller. For a description
+					  of the use of these, see the
+					  send_messages() function.
+
+					  The values originate in a
+					  configuration file. The function that
+					  retrieves this information from the
+					  configuration file is not easily
+					  called from this context. Thus, the
+					  values come via the caller of this
+					  function and are carried in the smpp
+					  structure.
+    					______________________________________*/
+    max_msg_time = smpp->last_msg_time;
+    max_msg_count = smpp->msg_count;
+
     transmitter = io_arg->transmitter;
     gw_free(io_arg);
 
@@ -1752,7 +1824,20 @@
         pending_submits = -1;
         len = 0;
         smpp->throttling_err_time = 0;
+
+			/*______________________________________
+			These lines initialize the SMSC
+			limit management variables. The idea
+			is to reset these each time a
+			connection is made.
+			______________________________________*/
+		if(max_msg_count) smpp->msg_count = max_msg_count + 1;
+		smpp->last_msg_time = (max_msg_time)?time(NULL):(time_t)0;
+		reconnect = 0;
+
+
         for (;conn != NULL;) {
+
             timeout = last_enquire_sent + smpp->enquire_link_interval
                         - date_universal_now();
 
@@ -1763,7 +1848,7 @@
              * Read so long as unbind_resp received or timeout passed. Otherwise we have
              * double delivered messages.
              */
-            if (smpp->quitting) {
+            if (smpp->quitting || reconnect) {
                 send_unbind(smpp, conn);
                 last_response = time(NULL);
                 while(conn_wait(conn, 1.00) != -1 &&
@@ -1783,6 +1868,12 @@
 
             send_enquire_link(smpp, conn, &last_enquire_sent);
 
+		    			/*______________________________________
+					  To see a description of the use of
+					  the "ret" variable, look at the
+					  variable's definition at the start of
+					  this function.
+		    			______________________________________*/
             while ((ret = read_pdu(smpp, conn, &len, &pdu)) == 1) {
                 last_response = time(NULL);
                 /* Deal with the PDU we just got */
@@ -1807,8 +1898,30 @@
                 if (transmitter && difftime(time(NULL), smpp->throttling_err_time) > SMPP_THROTTLING_SLEEP_TIME) {
                     smpp->throttling_err_time = 0;
                     send_messages(smpp, conn, &pending_submits);
-                }
-            }
+				}
+		    			/*______________________________________
+					  This test breaks the connection with
+					  the server if the message count was
+					  exceeded.  
+		    			______________________________________*/
+				if(smpp->msg_count == 1) {
+				    debug("bb.smpp",0, "SMPP %s reached max submits during read_pdu. Reconnecting momentarily.", octstr_get_cstr(smpp->conn->id));
+				    ret = 1;
+				    reconnect = 1;
+				    break;
+				}
+		    			/*______________________________________
+					  This test breaks the connection with
+					  the server if the idle time was
+					  exceeded.  
+		    			______________________________________*/
+				if ( max_msg_time && (max_msg_time < (time(NULL) - smpp->last_msg_time)) ) {
+				    debug("bb.smpp",0, "SMPP %s reached max idle time during read_pdu. Reconnecting momentarily.", octstr_get_cstr(smpp->conn->id));
+				    ret = 1;
+				    reconnect = 1;
+				    break;
+				}
+            }				/*while(read_pdu)*/
 
             if (ret == -1) {
                 error(0, "SMPP[%s]: I/O error or other error. Re-connecting.",
@@ -1824,7 +1937,6 @@
                 break;
             }
 
-
             /* cleanup sent queue */
             if (transmitter && difftime(time(NULL), last_cleanup) > smpp->wait_ack) {
                 if (do_queue_cleanup(smpp, &pending_submits, smpp->wait_ack_action))
@@ -1835,8 +1947,29 @@
             if (transmitter && difftime(time(NULL), smpp->throttling_err_time) > SMPP_THROTTLING_SLEEP_TIME) {
                 smpp->throttling_err_time = 0;
                 send_messages(smpp, conn, &pending_submits);
-            }
-        }
+			}
+		    			/*______________________________________
+					  This test breaks the connection with
+					  the server if the message count was
+					  exceeded.  
+		    			______________________________________*/
+			if(smpp->msg_count == 1) {
+				debug("bb.smpp",0, "SMPP %s reached max submitts. Reconnecting momentarily.", octstr_get_cstr(smpp->conn->id));
+				ret = 1;
+		    	reconnect = 1;
+			}
+		    			/*______________________________________
+					  This test breaks the connection with
+					  the server if the idle time was
+					  exceeded.  
+		    			______________________________________*/
+			if ( max_msg_time && (max_msg_time < (time(NULL) - smpp->last_msg_time)) ) {
+				debug("bb.smpp",0, "SMPP %s reached max idle time. Reconnecting momentarily.", octstr_get_cstr(smpp->conn->id));
+				ret = 1;
+		    	reconnect = 1;
+			}
+		
+        }				/*for ... SMS I/O */
 
         if (conn != NULL) {
             conn_destroy(conn);
@@ -1877,9 +2010,21 @@
          * Reconnect if that was a connection problem.
          */
         if (!smpp->quitting) {
-            error(0, "SMPP[%s]: Couldn't connect to SMS center (retrying in %ld seconds).",
-                  octstr_get_cstr(smpp->conn->id), smpp->conn->reconnect_delay);
-            gwthread_sleep(smpp->conn->reconnect_delay);
+	    					
+		    			/*______________________________________
+					  This section inserts a delay in the 
+					  event that connection to the SMS
+					  server was a problem. Otherwise, no
+					  delay is imposed on the reconnect.
+
+					  The default for the delay imposed
+					  here is 10.0 seconds.
+		    			______________________________________*/
+	    	if(ret != 1) {
+				error(0, "SMPP[%s]: Couldn't connect to SMS center (retrying in %ld seconds).",
+				    octstr_get_cstr(smpp->conn->id), smpp->conn->reconnect_delay);
+				gwthread_sleep(smpp->conn->reconnect_delay);
+			}
         }
     }
     mutex_lock(smpp->conn->flow_mutex);
@@ -1983,6 +2128,28 @@
     Octstr *alt_charset;
     Octstr *alt_addr_charset;
     long connection_timeout, wait_ack, wait_ack_action;
+    long max_msg_count;
+    long max_msg_time;
+
+    					/*______________________________________
+					  These lines retrieve the SMSC
+					  connection limits from the config
+					  file. These are limitations for max
+					  idle time and max message count per
+					  connection.
+
+					  These values are used in the
+					  'io_thread' function. They are
+					  extracted here because the
+					  cfg_get_integer() function requires
+					  the 'grp' resource, which is not
+					  available from within the io_thread
+					  function.
+    					______________________________________*/
+    if (cfg_get_integer(&max_msg_count, grp, octstr_imm("sms-max-submits")) == -1) 
+        max_msg_count = 0; 
+    if (cfg_get_integer(&max_msg_time, grp, octstr_imm("sms-max-idle")) == -1) 
+        max_msg_time = 0; 
 
     my_number = alt_addr_charset = alt_charset = NULL;
     transceiver_mode = 0;
@@ -2150,11 +2317,19 @@
 
     conn->status = SMSCCONN_CONNECTING; 
        
+    					/*______________________________________
+					  These lines package up the SMSC
+					  connection limits so that the
+					  io_thread will be aware of them.
+    					______________________________________*/
+    smpp->last_msg_time =  (time_t)max_msg_time;
+    smpp->msg_count = max_msg_count;
     /* 
      * I/O threads are only started if the corresponding ports 
      * have been configured with positive numbers. Use 0 to  
      * disable the creation of the corresponding thread. 
      */ 
+
     if (port != 0) 
         smpp->transmitter = gwthread_create(io_thread, io_arg_create(smpp,  
                                            (transceiver_mode ? 2 : 1))); 
Index: gwlib/cfg.def
===================================================================
RCS file: /home/cvs/gateway/gwlib/cfg.def,v
retrieving revision 1.118
diff -u -r1.118 cfg.def
--- gwlib/cfg.def	9 May 2006 14:32:31 -0000	1.118
+++ gwlib/cfg.def	6 Jun 2006 21:43:08 -0000
@@ -368,6 +368,8 @@
     OCTSTR(preferred-prefix-regex)
     OCTSTR(max-error-count)
     OCTSTR(alt-addr-charset)
+    OCTSTR(sms-max-idle)
+    OCTSTR(sms-max-submits)
 )
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.