Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

mtbatch.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 /*
00058  * mtbatch.c - an MT batch run utility for bearerbox
00059  *
00060  * This utility reads in a content file which has the SMS text message and
00061  * a receivers file, which has receiver numbers in each line. It connects
00062  * to bearerbox as if it would be a smsbox and issues the SMS sequentially
00063  * to bearerbox.
00064  *
00065  * Stipe Tolj <stolj@wapme.de>
00066  *
00067  * XXX add udh (etc.) capabilities. Currently we handle only 7-bit.
00068  * XXX add ACK handling by providing a queue and retrying if failed.
00069  */
00070 
00071 #include <string.h>
00072 #include <unistd.h>
00073 #include <signal.h>
00074 
00075 #include "gwlib/gwlib.h"
00076 
00077 #include "msg.h"
00078 #include "sms.h"
00079 #include "bb.h"
00080 #include "shared.h"
00081 #include "heartbeat.h"
00082 
00083 static char *pid_file;
00084 static Octstr *smsbox_id = NULL;
00085 static Octstr *content = NULL;
00086 static List *lines = NULL;
00087 static Octstr *bb_host;
00088 static long bb_port;
00089 static int bb_ssl;
00090 static Octstr *service = NULL;
00091 static Octstr *account = NULL;
00092 static Octstr *from = NULL;
00093 static Octstr *smsc_id = NULL;
00094 static long sms_max_length = MAX_SMS_OCTETS;
00095 static double delay = 0;
00096 
00097 
00098 static void write_pid_file(void) {
00099     FILE *f;
00100         
00101     if (pid_file != NULL) {
00102     f = fopen(pid_file, "w");
00103     fprintf(f, "%d\n", (int)getpid());
00104     fclose(f);
00105     }
00106 }
00107 
00108 /***********************************************************************
00109  * Communication with the bearerbox.
00110  */
00111 
00112 
00113 /* 
00114  * Identify ourself to bearerbox for smsbox-specific routing inside bearerbox.
00115  * Do this even while no smsbox-id is given to unlock the sender thread in
00116  * bearerbox.
00117  */
00118 static void identify_to_bearerbox(void)
00119 {
00120     Msg *msg;
00121     
00122     msg = msg_create(admin);
00123     msg->admin.command = cmd_identify;
00124     msg->admin.boxc_id = octstr_duplicate(smsbox_id);
00125     write_to_bearerbox(msg);
00126 }
00127 
00128 
00129 /*
00130  * Read an Msg from the bearerbox and send it to the proper receiver
00131  * via a List. At the moment all messages are sent to the smsbox_requests
00132  * List.
00133  */
00134 static void read_messages_from_bearerbox(void *arg)
00135 {
00136     time_t start, t;
00137     unsigned long secs;
00138     unsigned long total_s, total_f, total_ft, total_b;
00139     Msg *msg;
00140 
00141     total_s = total_f = total_ft = total_b = 0;
00142     start = t = time(NULL);
00143     while (program_status != shutting_down) {
00144         int ret;
00145         /* block infinite for reading messages */
00146         ret = read_from_bearerbox(&msg, INFINITE_TIME);
00147         if (ret == -1)
00148             break;
00149         else if (ret == 1) /* timeout */
00150             continue;
00151         else if (msg == NULL) /* just to be sure, may not happens */
00152             break;
00153 
00154         if (msg_type(msg) == admin) {
00155             if (msg->admin.command == cmd_shutdown ||
00156                 msg->admin.command == cmd_restart) {
00157                 info(0, "Bearerbox told us to die");
00158                 program_status = shutting_down;
00159             }
00160             /*
00161              * XXXX here should be suspend/resume, add RSN
00162              */
00163             msg_destroy(msg);
00164         } else if (msg_type(msg) == ack) {
00165             switch (msg->ack.nack) {
00166                 case ack_success:
00167                     total_s++;
00168                     break;
00169                 case ack_failed:
00170                     total_f++;
00171                     break;
00172                 case ack_failed_tmp:
00173                     total_ft++;
00174                     break;
00175                 case ack_buffered:
00176                     total_b++;
00177                     break;
00178             }
00179             msg_destroy(msg);
00180         } else {
00181             warning(0, "Received other message than sms/admin, ignoring!");
00182             msg_destroy(msg);
00183         }
00184     }
00185     secs = difftime(time(NULL), start);
00186     info(0, "Received acks: %ld success, %ld failed, %ld failed temporarly, %ld queued in %ld seconds "
00187          "(%.2f per second)", total_s, total_f, total_ft, total_b, secs, 
00188          (float)(total_s+total_f+total_ft+total_b) / secs);
00189 }
00190 
00191 /*
00192  * Send a message to the bearerbox for delivery to a phone.
00193  * Return >= 0 for success & count of splitted sms messages, 
00194  * -1 for failure.  Does not destroy the msg.
00195  */
00196 static int send_message(Msg *msg)
00197 {
00198     unsigned long msg_count;
00199     List *list;
00200     Msg *part;
00201     
00202     gw_assert(msg != NULL);
00203     gw_assert(msg_type(msg) == sms);
00204     
00205     /* 
00206      * Encode our smsbox-id to the msg structure.
00207      * This will allow bearerbox to return specific answers to the
00208      * same smsbox, mainly for DLRs and SMS proxy modes.
00209      */
00210     if (smsbox_id != NULL) {
00211         msg->sms.boxc_id = octstr_duplicate(smsbox_id);
00212     }
00213     
00214     list = sms_split(msg, NULL, NULL, NULL, NULL, 1, 0, 100, sms_max_length);
00215     msg_count = gwlist_len(list);
00216 
00217     debug("sms", 0, "message length %ld, sending %ld messages", 
00218           octstr_len(msg->sms.msgdata), msg_count);
00219 
00220     while ((part = gwlist_extract_first(list)) != NULL) {
00221 
00222         if (delay > 0)
00223             gwthread_sleep(delay);
00224 
00225         /* pass message to bearerbox */
00226         if (deliver_to_bearerbox(part) != 0)
00227             return -1;
00228         
00229     }    
00230     gwlist_destroy(list, NULL);
00231     
00232     return msg_count;
00233 }
00234 
00235 
00236 static void help(void) 
00237 {
00238     info(0, "Usage: mtbatch [options] content-file receivers-file ...");
00239     info(0, "where options are:");
00240     info(0, "-v number");
00241     info(0, "    set log level for stderr logging");
00242     info(0, "-b host");
00243     info(0, "    defines the host of bearerbox (default: localhost)");
00244     info(0, "-p port");
00245     info(0, "    the smsbox port to connect to (default: 13002)");
00246     info(0, "-s");
00247     info(0, "    inidicatr to use SSL for bearerbox connection (default: no)");
00248     info(0, "-i smsbox-id");
00249     info(0, "    defines the smsbox-id to be used for bearerbox connection (default: none)");
00250     info(0, "-f sender");
00251     info(0, "    which sender address should be used");
00252     info(0, "-n service");
00253     info(0, "    defines which service name should be logged (default: none)");
00254     info(0, "-a account");
00255     info(0, "    defines which account name should be logged (default: none)");
00256     info(0, "-d seconds");
00257     info(0, "    delay between message sending to bearerbox (default: 0)");
00258     info(0, "-r smsc-id");
00259     info(0, "    use a specific route for the MT traffic");
00260 }
00261 
00262 static void init_batch(Octstr *cfilename, Octstr *rfilename)
00263 {
00264     Octstr *receivers;
00265     long lineno = 0; 
00266 
00267     content = octstr_read_file(octstr_get_cstr(cfilename)); 
00268     octstr_strip_crlfs(content);
00269     if (content == NULL) 
00270         panic(0,"Can not read content file `%s'.", 
00271               octstr_get_cstr(cfilename));
00272     info(0,"SMS-Text: <%s>", octstr_get_cstr(content));
00273 
00274     info(0,"Loading receiver list. This may take a while...");
00275     receivers = octstr_read_file(octstr_get_cstr(rfilename)); 
00276     if (receivers == NULL) 
00277         panic(0,"Can not read receivers file `%s'.", 
00278               octstr_get_cstr(rfilename)); 
00279 
00280     lines = octstr_split(receivers, octstr_imm("\n")); 
00281     lineno = gwlist_len(lines);
00282     if (lineno <= 0) 
00283         panic(0,"Receiver file seems empty!");
00284 
00285     info(0,"Receivers file `%s' contains %ld destination numbers.",
00286          octstr_get_cstr(rfilename), lineno);
00287 }
00288 
00289 static void run_batch(void)
00290 {
00291     Octstr *no;
00292     unsigned long lineno = 0;
00293 
00294     while ((no = gwlist_consume(lines)) != NULL) {
00295         if (octstr_check_range(no, 0, 256, gw_isdigit)) {
00296         Msg *msg;
00297         
00298         lineno++;
00299 
00300         msg = msg_create(sms);
00301 
00302         msg->sms.smsc_id = smsc_id ? octstr_duplicate(smsc_id) : NULL;
00303         msg->sms.service = service ? octstr_duplicate(service) : NULL;
00304         msg->sms.sms_type = mt_push;
00305         msg->sms.sender = octstr_duplicate(from);
00306         msg->sms.receiver = octstr_duplicate(no);
00307         msg->sms.account = account ? octstr_duplicate(account) : NULL;
00308         msg->sms.msgdata = content ? octstr_duplicate(content) : octstr_create("");
00309         msg->sms.udhdata = octstr_create("");
00310         msg->sms.coding = DC_7BIT;
00311 
00312         if (send_message(msg) < 0) {
00313             panic(0,"Failed to send message at line <%ld> for receiver `%s' to bearerbox.",
00314                   lineno, octstr_get_cstr(no));
00315         }   
00316         msg_destroy(msg);
00317         octstr_destroy(no);
00318         }
00319     }
00320 } 
00321 
00322 int main(int argc, char **argv)
00323 {
00324     int opt;
00325     Octstr *cf, *rf;
00326 
00327     gwlib_init();
00328 
00329     bb_host = octstr_create("localhost");
00330     bb_port = 13001;
00331     bb_ssl = 0;
00332         
00333     while ((opt = getopt(argc, argv, "hv:b:p:si:n:a:f:d:r:")) != EOF) {
00334         switch (opt) {
00335             case 'v':
00336                 log_set_output_level(atoi(optarg));
00337                 break;
00338             case 'b':
00339                 octstr_destroy(bb_host);
00340                 bb_host = octstr_create(optarg);
00341                 break;
00342             case 'p':
00343                 bb_port = atoi(optarg);
00344                 break;
00345             case 's':
00346                 bb_ssl = 1;
00347                 break;
00348             case 'i':
00349                 smsbox_id = octstr_create(optarg);
00350                 break;
00351             case 'n':
00352                 service = octstr_create(optarg);
00353                 break;
00354             case 'a':
00355                 account = octstr_create(optarg);
00356                 break;
00357             case 'f':
00358                 from = octstr_create(optarg);
00359                 break;
00360             case 'd':
00361                 delay = atof(optarg);
00362                 break;
00363             case 'r':
00364                 smsc_id = octstr_create(optarg);
00365                 break;
00366             case '?':
00367             default:
00368                 error(0, "Invalid option %c", opt);
00369                 help();
00370                 panic(0, "Stopping.");
00371         }
00372     }
00373     
00374     if (optind == argc || argc-optind < 2) {
00375         help();
00376         exit(0);
00377     }
00378 
00379     /* check some mandatory elements */
00380     if (from == NULL)
00381         panic(0,"Sender address not specified. Use option -f to specify sender address.");
00382 
00383     rf = octstr_create(argv[argc-1]);
00384     cf = octstr_create(argv[argc-2]);
00385 
00386     report_versions("mtbatch");
00387     write_pid_file();
00388  
00389     init_batch(cf, rf);
00390 
00391     connect_to_bearerbox(bb_host, bb_port, bb_ssl, NULL /* bb_our_host */);
00392     identify_to_bearerbox();
00393     gwthread_create(read_messages_from_bearerbox, NULL);
00394 
00395     run_batch();
00396 
00397     program_status = shutting_down;
00398     gwthread_join_all();
00399 
00400     octstr_destroy(bb_host);
00401     octstr_destroy(smsbox_id);
00402     octstr_destroy(content);
00403     octstr_destroy(service);
00404     octstr_destroy(account);
00405     octstr_destroy(smsc_id);
00406     gwlist_destroy(lines, octstr_destroy_item); 
00407    
00408     gwlib_shutdown();
00409 
00410     return 0;
00411 }
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.