lib/kcore/statistics.c

Go to the documentation of this file.
00001 /*
00002  * $Id$
00003  *
00004  * Copyright (C) 2006 Voice Sistem SRL
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  * History:
00024  * ---------
00025  *  2006-01-16  first version (bogdan)
00026  *  2006-11-28  added get_stat_var_from_num_code() (Jeffrey Magder -
00027  *              SOMA Networks)
00028  *  2010-08-08  removed all the parts emulated by kstats_wrapper.[ch] (andrei)
00029  */
00030 
00037 #include <string.h>
00038 #include <stdio.h>
00039 #include <stdlib.h>
00040 
00041 #include "../../ut.h"
00042 #include "../../dprint.h"
00043 #include "../../socket_info.h"
00044 #include "statistics.h"
00045 
00046 #ifdef STATISTICS
00047 
00048 
00058 stat_var *get_stat_var_from_num_code(unsigned int numerical_code, int out_codes)
00059 {
00060         static char msg_code[INT2STR_MAX_LEN+4];
00061         str stat_name;
00062 
00063         stat_name.s = int2bstr( (unsigned long)numerical_code, msg_code, 
00064                 &stat_name.len);
00065         stat_name.s[stat_name.len++] = '_';
00066 
00067         if (out_codes) {
00068                 stat_name.s[stat_name.len++] = 'o';
00069                 stat_name.s[stat_name.len++] = 'u';
00070                 stat_name.s[stat_name.len++] = 't';
00071         } else {
00072                 stat_name.s[stat_name.len++] = 'i';
00073                 stat_name.s[stat_name.len++] = 'n';
00074         }
00075 
00076         return get_stat(&stat_name);
00077 }
00078 
00079 
00080 #endif /*STATISTICS*/
00081 
00082 #define MAX_PROC_BUFFER 256
00083 
00115 int get_socket_list_from_proto(int **ipList, int protocol) {
00116 
00117         struct socket_info  *si;
00118         struct socket_info** list;
00119 
00120         int num_ip_octets   = 4;
00121         int numberOfSockets = 0;
00122         int currentRow      = 0;
00123 
00124         /* I hate to use #ifdefs, but this is necessary because of the way 
00125          * get_sock_info_list() is defined.  */
00126 #ifndef USE_TCP
00127         if (protocol == PROTO_TCP) 
00128         {
00129                 return 0;
00130         }
00131 #endif
00132 
00133 #ifndef USE_TLS
00134         if (protocol == PROTO_TLS)
00135         {
00136                 return 0;
00137         }
00138 #endif
00139 
00140         /* Retrieve the list of sockets with respect to the given protocol. */
00141         list=get_sock_info_list(protocol);
00142 
00143         /* Find out how many sockets are in the list.  We need to know this so
00144          * we can malloc an array to assign to ipList. */
00145         for(si=list?*list:0; si; si=si->next){
00146                 /* We only support IPV4 at this point. */
00147                 if (si->address.af == AF_INET) {
00148                         numberOfSockets++;
00149                 }
00150         }
00151 
00152         /* There are no open sockets with respect to the given protocol. */
00153         if (numberOfSockets == 0)
00154         {
00155                 return 0;
00156         }
00157 
00158         *ipList = pkg_malloc(numberOfSockets * (num_ip_octets + 1) * sizeof(int));
00159 
00160         /* We couldn't allocate memory for the IP List.  So all we can do is
00161          * fail. */
00162         if (*ipList == NULL) {
00163                 LM_ERR("no more pkg memory");
00164                 return 0;
00165         }
00166 
00167 
00168         /* We need to search the list again.  So find the front of the list. */
00169         list=get_sock_info_list(protocol);
00170 
00171         /* Extract out the IP Addresses and ports.  */
00172         for(si=list?*list:0; si; si=si->next){
00173 
00174                 /* We currently only support IPV4. */
00175                 if (si->address.af != AF_INET) {
00176                         continue;
00177                 }
00178 
00179                 (*ipList)[currentRow*(num_ip_octets + 1)  ] = 
00180                         si->address.u.addr[0];
00181                 (*ipList)[currentRow*(num_ip_octets + 1)+1] = 
00182                         si->address.u.addr[1];
00183                 (*ipList)[currentRow*(num_ip_octets + 1)+2] = 
00184                         si->address.u.addr[2];
00185                 (*ipList)[currentRow*(num_ip_octets + 1)+3] = 
00186                         si->address.u.addr[3];
00187                 (*ipList)[currentRow*(num_ip_octets + 1)+4] = 
00188                         si->port_no;
00189                 
00190                 currentRow++;
00191         }
00192 
00193         return numberOfSockets;
00194 }
00195 
00206 static int parse_proc_net_line(char *line, int *ipAddress, int *rx_queue) 
00207 {
00208         int i;
00209 
00210         int ipOctetExtractionMask = 0xFF;
00211 
00212         char *currColonLocation;
00213         char *nextNonNumericalChar;
00214         char *currentLocationInLine = line;
00215 
00216         int parsedInteger[4];
00217 
00218         /* Example line from /proc/net/tcp or /proc/net/udp:
00219          *
00220          *      sl  local_address rem_address   st tx_queue rx_queue  
00221          *      21: 5A0A0B0A:CAC7 1C016E0A:0016 01 00000000:00000000
00222          *
00223          * Algorithm:
00224          *
00225          *      1) Find the location of the first  ':'
00226          *      2) Parse out the IP Address into an integer
00227          *      3) Find the location of the second ':'
00228          *      4) Parse out the port number.
00229          *      5) Find the location of the fourth ':'
00230          *      6) Parse out the rx_queue.
00231          */
00232 
00233         for (i = 0; i < 4; i++) {
00234 
00235                 currColonLocation = strchr(currentLocationInLine, ':'); 
00236 
00237                 /* We didn't find all the needed ':', so fail. */
00238                 if (currColonLocation == NULL) {
00239                         return 0;
00240                 }
00241 
00242                 /* Parse out the integer, keeping the location of the next 
00243                  * non-numerical character.  */
00244                 parsedInteger[i] = 
00245                         (int) strtol(++currColonLocation, &nextNonNumericalChar,
00246                                         16);
00247 
00248                 /* strtol()'s specifications specify that the second parameter
00249                  * is set to the first parameter when a number couldn't be
00250                  * parsed out.  This means the parse was unsuccesful.  */
00251                 if (nextNonNumericalChar == currColonLocation) {
00252                         return 0;
00253                 }
00254                 
00255                 /* Reset the currentLocationInLine to the last non-numerical 
00256                  * character, so that next iteration of this loop, we can find
00257                  * the next colon location. */
00258                 currentLocationInLine = nextNonNumericalChar;
00259 
00260         }
00261 
00262         /* Extract out the segments of the IP Address.  They are stored in
00263          * reverse network byte order. */
00264         for (i = 0; i < NUM_IP_OCTETS; i++) {
00265                 
00266                 ipAddress[i] = 
00267                         parsedInteger[0] & (ipOctetExtractionMask << i*8); 
00268 
00269                 ipAddress[i] >>= i*8;
00270 
00271         }
00272 
00273         ipAddress[NUM_IP_OCTETS] = parsedInteger[1];
00274 
00275         *rx_queue = parsedInteger[3];
00276         
00277         return 1;
00278  
00279 }
00280 
00281 
00289 static int match_ip_and_port(int *ipOne, int *ipArray, int sizeOf_ipArray) 
00290 {
00291         int curIPAddrIdx;
00292         int curOctetIdx;
00293         int ipArrayIndex;
00294 
00295         /* Loop over every IP Address */
00296         for (curIPAddrIdx = 0; curIPAddrIdx < sizeOf_ipArray; curIPAddrIdx++) {
00297 
00298                 /* Check for octets that don't match.  If one is found, skip the
00299                  * rest.  */
00300                 for (curOctetIdx = 0; curOctetIdx < NUM_IP_OCTETS + 1; curOctetIdx++) {
00301                         
00302                         /* We've encoded a 2D array as a 1D array.  So find out
00303                          * our position in the 1D array. */
00304                         ipArrayIndex = 
00305                                 curIPAddrIdx * (NUM_IP_OCTETS + 1) + curOctetIdx;
00306 
00307                         if (ipOne[curOctetIdx] != ipArray[ipArrayIndex]) {
00308                                 break;
00309                         }
00310                 }
00311 
00312                 /* If the index from the inner loop is equal to NUM_IP_OCTETS
00313                  * + 1, then that means that every octet (and the port with the
00314                  * + 1) matched. */
00315                 if (curOctetIdx == NUM_IP_OCTETS + 1) {
00316                         return 1;
00317                 }
00318 
00319         }
00320 
00321         return 0;
00322 }
00323 
00324 
00336 static int get_used_waiting_queue(
00337                 int forTCP, int *interfaceList, int listSize) 
00338 {
00339         FILE *fp;
00340         char *fileToOpen;
00341         
00342         char lineBuffer[MAX_PROC_BUFFER];
00343         int  ipAddress[NUM_IP_OCTETS+1];
00344         int  rx_queue;
00345         
00346         int  waitingQueueSize = 0;
00347 
00348         /* Set up the file we want to open. */
00349         if (forTCP) {
00350                 fileToOpen = "/proc/net/tcp";
00351         } else {
00352                 fileToOpen = "/proc/net/udp";
00353         }
00354         
00355         fp = fopen(fileToOpen, "r");
00356 
00357         if (fp == NULL) {
00358                 LM_ERR("Could not open %s. openserMsgQueu eDepth and its related"
00359                                 " alarms will not be available.\n", fileToOpen);
00360                 return 0;
00361         }
00362 
00363         /* Read in every line of the file, parse out the ip address, port, and
00364          * rx_queue, and compare to our list of interfaces we are listening on.
00365          * Add up rx_queue for those lines which match our known interfaces. */
00366         while (fgets(lineBuffer, MAX_PROC_BUFFER, fp)!=NULL) {
00367 
00368                 /* Parse out the ip address, port, and rx_queue. */
00369                 if(parse_proc_net_line(lineBuffer, ipAddress, &rx_queue)) {
00370 
00371                         /* Only add rx_queue if the line just parsed corresponds 
00372                          * to an interface we are listening on.  We do this
00373                          * check because it is possible that this system has
00374                          * other network interfaces that OpenSER has been told
00375                          * to ignore. */
00376                         if (match_ip_and_port(ipAddress, interfaceList, listSize)) {
00377                                 waitingQueueSize += rx_queue;
00378                         }
00379                 }
00380         }
00381 
00382         fclose(fp);
00383 
00384         return waitingQueueSize;
00385 }
00386 
00395 int get_total_bytes_waiting(void) 
00396 {
00397         int bytesWaiting = 0;
00398 
00399         int *UDPList  = NULL;
00400         int *TCPList  = NULL;
00401         int *TLSList  = NULL;
00402 
00403         int numUDPSockets  = 0;
00404         int numTCPSockets  = 0; 
00405         int numTLSSockets  = 0;
00406 
00407         /* Extract out the IP address address for UDP, TCP, and TLS, keeping
00408          * track of the number of IP addresses from each transport  */
00409         numUDPSockets  = get_socket_list_from_proto(&UDPList,  PROTO_UDP);
00410         numTCPSockets  = get_socket_list_from_proto(&TCPList,  PROTO_TCP);
00411         numTLSSockets  = get_socket_list_from_proto(&TLSList,  PROTO_TLS);
00412 
00413         /* Find out the number of bytes waiting on our interface list over all
00414          * UDP and TCP transports. */
00415         bytesWaiting  += get_used_waiting_queue(0, UDPList,  numUDPSockets);
00416         bytesWaiting  += get_used_waiting_queue(1, TCPList,  numTCPSockets);
00417         bytesWaiting  += get_used_waiting_queue(1, TLSList,  numTLSSockets);
00418 
00419         /* get_socket_list_from_proto() allocated a chunk of memory, so we need
00420          * to free it. */
00421         if (numUDPSockets > 0)
00422         {
00423                 pkg_free(UDPList);
00424         }
00425 
00426         if (numTCPSockets > 0) 
00427         {
00428                 pkg_free(TCPList);
00429         }
00430 
00431         if (numTLSSockets > 0)
00432         {
00433                 pkg_free(TLSList);
00434         }
00435 
00436         return bytesWaiting;
00437 }
00438 
00439