acc_cdr.c

Go to the documentation of this file.
00001 /*
00002  * Accounting module
00003  *
00004  * Copyright (C) 2011 - Sven Knoblich 1&1 Internet AG
00005  *
00006  * This file is part of Kamailio, a free SIP server.
00007  *
00008  * Kamailio is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version
00012  *
00013  * Kamailio is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  */
00023 
00038 #include "../../modules/tm/tm_load.h"
00039 #include "../../str.h"
00040 #include "../dialog/dlg_load.h"
00041 
00042 #include "acc_api.h"
00043 #include "acc_cdr.h"
00044 #include "acc_mod.h"
00045 #include "acc_extra.h"
00046 #include "acc.h"
00047 
00048 #include <sys/time.h>
00049 
00050 /* Solaris does not provide timersub macro in <sys/time.h> */
00051 #ifdef __OS_solaris
00052 #define timersub(tvp, uvp, vvp)                     \
00053     do {                                \
00054         (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;      \
00055         (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;   \
00056         if ((vvp)->tv_usec < 0) {               \
00057             (vvp)->tv_sec--;                \
00058             (vvp)->tv_usec += 1000000;          \
00059         }                           \
00060     } while (0)
00061 #endif // __OS_solaris
00062 
00063 #define TIME_STR_BUFFER_SIZE 20
00064 #define TIME_BUFFER_LENGTH 256
00065 
00066 struct dlg_binds dlgb;
00067 struct acc_extra* cdr_extra = NULL;
00068 int cdr_facility = LOG_DAEMON;
00069 
00070 static const str zero_duration = { "0", 1};
00071 static const char time_separator = {'.'};
00072 static char time_buffer[ TIME_BUFFER_LENGTH];
00073 static const str empty_string = { "", 0};
00074 
00075 // buffers which are used to collect the crd data for writing
00076 static str cdr_attrs[ MAX_CDR_CORE + MAX_CDR_EXTRA];
00077 static str cdr_value_array[ MAX_CDR_CORE + MAX_CDR_EXTRA];
00078 static int cdr_int_arr[ MAX_CDR_CORE + MAX_CDR_EXTRA];
00079 static char cdr_type_array[ MAX_CDR_CORE + MAX_CDR_EXTRA];
00080 
00081 extern struct tm_binds tmb;
00082 extern str cdr_start_str;
00083 extern str cdr_end_str;
00084 extern str cdr_duration_str;
00085 
00086 /* write all basic information to buffers(e.g. start-time ...) */
00087 static int cdr_core2strar( struct dlg_cell* dlg,
00088                            str* values,
00089                            int* unused,
00090                            char* types)
00091 {
00092     str* start = NULL;
00093     str* end = NULL;
00094     str* duration = NULL;
00095 
00096     if( !dlg || !values || !types)
00097     {
00098         LM_ERR( "invalid input parameter!\n");
00099         return 0;
00100     }
00101 
00102     start = dlgb.get_dlg_var( dlg, (str*)&cdr_start_str);
00103     end = dlgb.get_dlg_var( dlg, (str*)&cdr_end_str);
00104     duration = dlgb.get_dlg_var( dlg, (str*)&cdr_duration_str);
00105 
00106     values[0] = ( start != NULL ? *start : empty_string);
00107     types[0] = ( start != NULL ? TYPE_STR : TYPE_NULL);
00108 
00109     values[1] = ( end != NULL ? *end : empty_string);
00110     types[1] = ( end != NULL ? TYPE_STR : TYPE_NULL);
00111 
00112     values[2] = ( duration != NULL ? *duration : empty_string);
00113     types[2] = ( duration != NULL ? TYPE_STR : TYPE_NULL);
00114 
00115     return MAX_CDR_CORE;
00116 }
00117 
00118 /* collect all crd data and write it to a syslog */
00119 static int write_cdr( struct dlg_cell* dialog,
00120                       struct sip_msg* message)
00121 {
00122     static char cdr_message[ MAX_SYSLOG_SIZE];
00123     static char* const cdr_message_end = cdr_message +
00124                                          MAX_SYSLOG_SIZE -
00125                                          2;// -2 because of the string ending '\n\0'
00126     char* message_position = NULL;
00127     int message_index = 0;
00128     int counter = 0;
00129 
00130     if( !dialog || !message)
00131     {
00132         LM_ERR( "dialog and/or message is/are empty!");
00133         return -1;
00134     }
00135 
00136     /* get default values */
00137     message_index = cdr_core2strar( dialog,
00138                                     cdr_value_array,
00139                                     cdr_int_arr,
00140                                     cdr_type_array);
00141 
00142     /* get extra values */
00143     message_index += extra2strar( cdr_extra,
00144                                   message,
00145                                   cdr_value_array + message_index,
00146                                   cdr_int_arr + message_index,
00147                                   cdr_type_array + message_index);
00148 
00149     for( counter = 0, message_position = cdr_message;
00150          counter < message_index ;
00151          counter++ )
00152     {
00153         const char* const next_message_end = message_position +
00154                                              2 + // ', ' -> two letters
00155                                              cdr_attrs[ counter].len +
00156                                              1 + // '=' -> one letter
00157                                              cdr_value_array[ counter].len;
00158 
00159         if( next_message_end >= cdr_message_end ||
00160             next_message_end < message_position)
00161         {
00162             LM_WARN("cdr message too long, truncating..\n");
00163             message_position = cdr_message_end;
00164             break;
00165         }
00166 
00167         if( counter > 0)
00168         {
00169             *(message_position++) = A_SEPARATOR_CHR;
00170             *(message_position++) = A_SEPARATOR_CHR_2;
00171         }
00172 
00173         memcpy( message_position,
00174                 cdr_attrs[ counter].s,
00175                 cdr_attrs[ counter].len);
00176 
00177         message_position += cdr_attrs[ counter].len;
00178 
00179         *( message_position++) = A_EQ_CHR;
00180 
00181         memcpy( message_position,
00182                 cdr_value_array[ counter].s,
00183                 cdr_value_array[ counter].len);
00184 
00185         message_position += cdr_value_array[ counter].len;
00186     }
00187 
00188     /* terminating line */
00189     *(message_position++) = '\n';
00190     *(message_position++) = '\0';
00191 
00192     LM_GEN2( cdr_facility, log_level, "%s", cdr_message);
00193 
00194     return 0;
00195 }
00196 
00197 /* convert a string into a timeval struct */
00198 static int string2time( str* time_str, struct timeval* time_value)
00199 {    
00200     char* dot_address = NULL;
00201     int dot_position = -1;
00202     char zero_terminated_value[TIME_STR_BUFFER_SIZE];
00203 
00204     if( !time_str)
00205     {
00206         LM_ERR( "time_str is empty!");
00207         return -1;
00208     }
00209     
00210     if( time_str->len >= TIME_STR_BUFFER_SIZE)
00211     {
00212         LM_ERR( "time_str is too long %d >= %d!",
00213                 time_str->len,
00214                 TIME_STR_BUFFER_SIZE);
00215         return -1;
00216     }
00217     
00218     memcpy( zero_terminated_value, time_str->s, time_str->len);
00219     zero_terminated_value[time_str->len] = '\0';
00220     
00221     dot_address = strchr( zero_terminated_value, time_separator);
00222     
00223     if( !dot_address)
00224     {
00225         LM_ERR( "failed to find separator('%c') in '%s'!\n",
00226                 time_separator,
00227                 zero_terminated_value);
00228         return -1;
00229     }
00230     
00231     dot_position = dot_address-zero_terminated_value + 1;
00232     
00233     if( dot_position >= strlen(zero_terminated_value) ||
00234         strchr(dot_address + 1, time_separator))
00235     {
00236         LM_ERR( "invalid time-string '%s'\n", zero_terminated_value);
00237         return -1;
00238     }
00239     
00240     time_value->tv_sec = strtol( zero_terminated_value, (char **)NULL, 10);
00241     time_value->tv_usec = strtol( dot_address + 1, (char **)NULL, 10) * 1000; // restore usec precision
00242     return 0;
00243 }
00244 
00245 /* convert a timeval struct into a string */
00246 static int time2string( struct timeval* time_value, str* time_str)
00247 {
00248     int buffer_length;
00249 
00250     if( !time_value)
00251     {
00252         LM_ERR( "time_value or any of its fields is empty!\n");
00253         return -1;
00254     }
00255 
00256     buffer_length = snprintf( time_buffer,
00257                               TIME_BUFFER_LENGTH,
00258                               "%ld%c%03d",
00259                               (long int)time_value->tv_sec,
00260                               time_separator,
00261                               (int)(time_value->tv_usec/1000));
00262 
00263     if( buffer_length < 0)
00264     {
00265         LM_ERR( "failed to write to buffer.\n");
00266         return -1;
00267     }
00268 
00269     time_str->s = time_buffer;
00270     time_str->len = buffer_length;
00271     return 0;
00272 }
00273 
00274 /* set the duration in the dialog struct */
00275 static int set_duration( struct dlg_cell* dialog)
00276 {
00277     struct timeval start_time;
00278     struct timeval end_time;
00279     struct timeval duration_time;
00280     str duration_str;
00281 
00282     if( !dialog)
00283     {
00284         LM_ERR("dialog is empty!\n");
00285         return -1;
00286     }
00287 
00288     if ( string2time( dlgb.get_dlg_var( dialog, (str*)&cdr_start_str), &start_time) < 0) {
00289         LM_ERR( "failed to extract start time\n");
00290         return -1;
00291     }
00292     if ( string2time( dlgb.get_dlg_var( dialog, (str*)&cdr_end_str), &end_time) < 0) {
00293         LM_ERR( "failed to extract end time\n");
00294         return -1;
00295     }
00296 
00297     timersub(&end_time, &start_time, &duration_time);
00298 
00299     if( time2string(&duration_time, &duration_str) < 0) {
00300         LM_ERR( "failed to convert current time to string\n");
00301         return -1;
00302     }
00303 
00304     if( dlgb.set_dlg_var( dialog,
00305                           (str*)&cdr_duration_str,
00306                           (str*)&duration_str) != 0)
00307     {
00308         LM_ERR( "failed to set duration time");
00309         return -1;
00310     }
00311 
00312     return 0;
00313 }
00314 
00315 /* set the current time as start-time in the dialog struct */
00316 static int set_start_time( struct dlg_cell* dialog)
00317 {
00318     struct timeval current_time;
00319     str start_time;
00320 
00321     if( !dialog)
00322     {
00323         LM_ERR("dialog is empty!\n");
00324         return -1;
00325     }
00326 
00327     if( gettimeofday( &current_time, NULL) < 0)
00328     {
00329         LM_ERR( "failed to get current time!\n");
00330         return -1;
00331     }
00332 
00333     if( time2string(&current_time, &start_time) < 0) {
00334         LM_ERR( "failed to convert current time to string\n");
00335         return -1;
00336     }
00337 
00338     if( dlgb.set_dlg_var( dialog,
00339                           (str*)&cdr_start_str,
00340                           (str*)&start_time) != 0)
00341     {
00342         LM_ERR( "failed to set start time\n");
00343         return -1;
00344     }
00345 
00346     if( dlgb.set_dlg_var( dialog,
00347                           (str*)&cdr_end_str,
00348                           (str*)&start_time) != 0)
00349     {
00350         LM_ERR( "failed to set initiation end time\n");
00351         return -1;
00352     }
00353 
00354     if( dlgb.set_dlg_var( dialog,
00355                           (str*)&cdr_duration_str,
00356                           (str*)&zero_duration) != 0)
00357     {
00358         LM_ERR( "failed to set initiation duration time\n");
00359         return -1;
00360     }
00361 
00362     return 0;
00363 }
00364 
00365 /* set the current time as end-time in the dialog struct */
00366 static int set_end_time( struct dlg_cell* dialog)
00367 {
00368     struct timeval current_time;
00369     str end_time;
00370 
00371     if( !dialog)
00372     {
00373         LM_ERR("dialog is empty!\n");
00374         return -1;
00375     }
00376 
00377     if( gettimeofday( &current_time, NULL) < 0)
00378     {
00379         LM_ERR( "failed to set time!\n");
00380         return -1;
00381     }
00382 
00383     if( time2string(&current_time, &end_time) < 0) {
00384         LM_ERR( "failed to convert current time to string\n");
00385         return -1;
00386     }
00387 
00388     if( dlgb.set_dlg_var( dialog,
00389                           (str*)&cdr_end_str,
00390                           (str*)&end_time) != 0)
00391     {
00392         LM_ERR( "failed to set start time");
00393         return -1;
00394     }
00395 
00396     return 0;
00397 }
00398 
00399 /* callback for a confirmed (INVITE) dialog. */
00400 static void cdr_on_start( struct dlg_cell* dialog,
00401                           int type,
00402                           struct dlg_cb_params* params)
00403 {
00404     if( !dialog || !params)
00405     {
00406         LM_ERR("invalid values\n!");
00407         return;
00408     }
00409 
00410     if( cdr_start_on_confirmed == 0)
00411     {
00412         return;
00413     }
00414 
00415     if( set_start_time( dialog) != 0)
00416     {
00417         LM_ERR( "failed to set start time!\n");
00418         return;
00419     }
00420 }
00421 
00422 /* callback for a failure during a dialog. */
00423 static void cdr_on_failed( struct dlg_cell* dialog,
00424                            int type,
00425                            struct dlg_cb_params* params)
00426 {
00427     struct sip_msg* msg = 0;
00428 
00429     if( !dialog || !params)
00430     {
00431         LM_ERR("invalid values\n!");
00432         return;
00433     }
00434 
00435     if( params->rpl && params->rpl != FAKED_REPLY)
00436     {
00437         msg = params->rpl;
00438     }
00439     else if( params->req)
00440     {
00441         msg = params->req;
00442     }
00443     else
00444     {
00445         LM_ERR( "request and response are invalid!");
00446         return;
00447     }
00448 
00449     if( write_cdr( dialog, msg) != 0)
00450     {
00451         LM_ERR( "failed to write cdr!\n");
00452         return;
00453     }
00454 }
00455 
00456 /* callback for the finish of a dialog (reply to BYE). */
00457 void cdr_on_end_confirmed( struct dlg_cell* dialog,
00458                         int type,
00459                         struct dlg_cb_params* params)
00460 {
00461     if( !dialog || !params || !params->req)
00462     {
00463         LM_ERR("invalid values\n!");
00464         return;
00465     }
00466 
00467     if( write_cdr( dialog, params->req) != 0)
00468     {
00469         LM_ERR( "failed to write cdr!\n");
00470         return;
00471     }
00472 }
00473 
00474 /* callback for the end of a dialog (BYE). */
00475 static void cdr_on_end( struct dlg_cell* dialog,
00476                         int type,
00477                         struct dlg_cb_params* params)
00478 {
00479     if( !dialog || !params || !params->req)
00480     {
00481         LM_ERR("invalid values\n!");
00482         return;
00483     }
00484 
00485     if( set_end_time( dialog) != 0)
00486     {
00487         LM_ERR( "failed to set end time!\n");
00488         return;
00489     }
00490 
00491     if( set_duration( dialog) != 0)
00492     {
00493         LM_ERR( "failed to set duration!\n");
00494         return;
00495     }
00496 }
00497 
00498 /* callback for a expired dialog. */
00499 static void cdr_on_expired( struct dlg_cell* dialog,
00500                             int type,
00501                             struct dlg_cb_params* params)
00502 {
00503     if( !dialog || !params)
00504     {
00505         LM_ERR("invalid values\n!");
00506         return;
00507     }
00508 
00509     LM_DBG("dialog '%p' expired!\n", dialog);
00510 }
00511 
00512 /* callback for the cleanup of a dialog. */
00513 static void cdr_on_destroy( struct dlg_cell* dialog,
00514                             int type,
00515                             struct dlg_cb_params* params)
00516 {
00517     if( !dialog || !params)
00518     {
00519         LM_ERR("invalid values\n!");
00520         return;
00521     }
00522 
00523     LM_DBG("dialog '%p' destroyed!\n", dialog);
00524 }
00525 
00526 /* callback for the creation of a dialog. */
00527 static void cdr_on_create( struct dlg_cell* dialog,
00528                            int type,
00529                            struct dlg_cb_params* params)
00530 {
00531     if( !dialog || !params || !params->req)
00532     {
00533         LM_ERR( "invalid values\n!");
00534         return;
00535     }
00536 
00537     if( cdr_enable == 0)
00538     {
00539         return;
00540     }
00541 
00542     if( dlgb.register_dlgcb( dialog, DLGCB_CONFIRMED, cdr_on_start, 0, 0) != 0)
00543     {
00544         LM_ERR("can't register create dialog CONFIRM callback\n");
00545         return;
00546     }
00547 
00548     if( dlgb.register_dlgcb( dialog, DLGCB_FAILED, cdr_on_failed, 0, 0) != 0)
00549     {
00550         LM_ERR("can't register create dialog FAILED callback\n");
00551         return;
00552     }
00553 
00554     if( dlgb.register_dlgcb( dialog, DLGCB_TERMINATED, cdr_on_end, 0, 0) != 0)
00555     {
00556         LM_ERR("can't register create dialog TERMINATED callback\n");
00557         return;
00558     }
00559 
00560     if( dlgb.register_dlgcb( dialog, DLGCB_TERMINATED_CONFIRMED, cdr_on_end_confirmed, 0, 0) != 0)
00561     {
00562         LM_ERR("can't register create dialog TERMINATED CONFIRMED callback\n");
00563         return;
00564     }
00565 
00566     if( dlgb.register_dlgcb( dialog, DLGCB_EXPIRED, cdr_on_expired, 0, 0) != 0)
00567     {
00568         LM_ERR("can't register create dialog EXPIRED callback\n");
00569         return;
00570     }
00571 
00572     if( dlgb.register_dlgcb( dialog, DLGCB_DESTROY, cdr_on_destroy, 0, 0) != 0)
00573     {
00574         LM_ERR("can't register create dialog DESTROY callback\n");
00575         return;
00576     }
00577 
00578     LM_DBG("dialog '%p' created!", dialog);
00579 
00580     if( set_start_time( dialog) != 0)
00581     {
00582         LM_ERR( "failed to set start time");
00583         return;
00584     }
00585 }
00586 /* convert the extra-data string into a list and store it */
00587 int set_cdr_extra( char* cdr_extra_value)
00588 {
00589     struct acc_extra* extra = 0;
00590     int counter = 0;
00591 
00592     if( cdr_extra_value && ( cdr_extra = parse_acc_extra( cdr_extra_value))==0)
00593     {
00594         LM_ERR("failed to parse crd_extra param\n");
00595         return -1;
00596     }
00597 
00598     /* fixed core attributes */
00599     cdr_attrs[ counter++] = cdr_start_str;
00600     cdr_attrs[ counter++] = cdr_end_str;
00601     cdr_attrs[ counter++] = cdr_duration_str;
00602 
00603     for(extra=cdr_extra; extra ; extra=extra->next)
00604     {
00605         cdr_attrs[ counter++] = extra->name;
00606     }
00607 
00608     return 0;
00609 }
00610 
00611 /* convert the facility-name string into a id and store it */
00612 int set_cdr_facility( char* cdr_facility_str)
00613 {
00614     int facility_id = -1;
00615 
00616     if( !cdr_facility_str)
00617     {
00618         LM_ERR( "facility is empty\n");
00619         return -1;
00620     }
00621 
00622     facility_id = str2facility( cdr_facility_str);
00623 
00624     if( facility_id == -1)
00625     {
00626         LM_ERR("invalid cdr facility configured\n");
00627         return -1;
00628     }
00629 
00630     cdr_facility = facility_id;
00631 
00632     return 0;
00633 }
00634 
00635 /* initialization of all necessary callbacks to track a dialog */
00636 int init_cdr_generation( void)
00637 {
00638     if( load_dlg_api( &dlgb) != 0)
00639     {
00640         LM_ERR("can't load dialog API\n");
00641         return -1;
00642     }
00643 
00644     if( dlgb.register_dlgcb( 0, DLGCB_CREATED, cdr_on_create, 0, 0) != 0)
00645     {
00646         LM_ERR("can't register create callback\n");
00647         return -1;
00648     }
00649 
00650     return 0;
00651 }
00652 
00653 /* convert the facility-name string into a id and store it */
00654 void destroy_cdr_generation( void)
00655 {
00656     if( !cdr_extra)
00657     {
00658         return;
00659     }
00660 
00661     destroy_extras( cdr_extra);
00662 }