## root/source/ialive.c

/* [<][>][^][v][top][bottom][index][help] */

## DEFINITIONS

This source file includes following definitions.

1. main
2. process
3. find_stats
4. get_sent_recv
5. parse_comline
6. c_initialize
7. c_initialize
8. c_ipc_socloc_init
9. app_out
10. term_app

    /* ialive - Program to test whether the Internet is up and active.
       Rick Smereka, Copyright (C) 2006.

       This program is free software; you can redistribute it and/or modify
       it under the terms of the GNU General Public License as published by
       the Free Software Foundation; either version 2 of the License, or
       (at your option) any later version.

       This program is distributed in the hope that it will be useful,
       but WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       GNU General Public License for more details.

       You should have received a copy of the GNU General Public License
       along with this program; if not, get a copy via the Internet at
       http://gnu.org/copyleft/gpl.html or write to the Free Software
       Foundation, Inc., 59 Temple Place, Suite 330, Boston,
       MA 02111-1307 USA

       You can contact the author via email at rsmereka@future-lab.com

       Program syntax:

          ialive [-c nn] [-b] [-q] [-p ping_dest] [-l [log_file]]

       Where:

       'c' is the number of pings to send ('nn')
       'b' record stats in 'bbuuzzb' table (see below)
       'q' run quietly
       'p' host name or IP address to ping
       'l' log name or destination

       This program works by pinging a specific host (default host is used if
       no host if specified using the 'p' switch. The results can be output to
       the screen, to a log file, and/or to a database table using the 'bbuuzzb'
       database. You may output to any combination of these three destinations.
       Output to the 'bbuuzzb' database uses the table name listed in the global
       constants. The data is stored where a single day is contained within a
       record. The field layout is as follows:

       field subfield subsubfield description

         1                        date (julian number)
         2       *         1      time (24hr, no colon)
         2       *         2      number of pings sent
         2       *         3      number of pings successfully received

       Since there is no support for 'exec' under MacOS, this program will
       not run under MacOS.

       Linux version based on original tcl program. Aug/2006, Rick Smereka */

    #include "stdhead.h"
    #include "flsocket.h"
    #include "appinit.h"
    #include "dbmess.h"
    #include "dbiocode.h"
    #include "dbcomm.h"

    #ifdef MULTIUSER
    #ifdef IPC_TCP
    #include "socloc.h"
    #include "sloc.h"
    #include "slconfig.h"
    #include "sliocode.h"
    #ifdef OS_WIN32
    WSADATA wsaData;
    #endif
    #endif
    #endif

    /* high level database API changes depending
       on whether we are compiling for stand-alone
       or multiuser */

    #ifdef MULTIUSER
    #include "dbcs.h"
    #include "dbcscfg.h"
    #else
    #include "dbeng.h"
    #include "dblocal.h"
    #include "dbengcfg.h"
    #include "dblocfg.h"
    #endif

    #define VERSION "1.21-2006.09.07"

    #ifdef MULTIUSER
    #define APNAME "ialivem"
    #else
    #define APNAME "ialive"
    #endif

    // #define DEBUG 1
    #define DEFAULT_HOST "www.yahoo.com"
    #define BBUUZZB_TABLE "ialive"
    #define DEFAULT_LOG_FILE "ialive.log"
    #define PING_OUTPUT_FILE "/work/logs/ping.log"

    // database field and subsubfield definitions

    #define FIELD_DATE 1
    #define FIELD_PING_DATA 2
    #define SUBSUBFIELD_TIME 1
    #define SUBSUBFIELD_PINGS_SENT 2
    #define SUBSUBFIELD_PINGS_RECV 3

    // global variables

    int tid;
    char log_dest[128];
    char ping_dest[128];
    int log_output = FALSE;
    int console_output = TRUE;
    int ping_count = 4;
    int use_bbuuzzb = FALSE;

    // function prototypes

    int main(int, char **);
    void process(void);
    void find_stats(char *);
    int get_sent_recv(char *, int, int *);
    int parse_comline(int, char **);
    int c_initialize(void);
    void app_out(char *,...);
    void term_app(void);

    #ifdef MULTIUSER
    #ifdef IPC_TCP
    int c_ipc_socloc_init(void);
    #endif
    #endif

    int main(int argc, char **argv)
    {
       char mes[128], mname[] = "main";
       int ret, lstatus;

       /* parse command line */

       if (!parse_comline(argc, argv))
          return(0);

       if (use_bbuuzzb)
          {
          // register application name with 'appinit'

          if (!appinit_register_name(APNAME))
             {
             printf("%s:fatal error registering app name with appinit\n", APNAME);
             return(0);
             }

          if (!c_initialize())
             {
             term_app();
             return(0);
             }
          }

       if (log_output)
          {
          if (logman_start(log_dest, APNAME))
             {
             printf("%s:unable to start log manager,program abort\n", APNAME);
             return(0);
             }

          logman_console(console_output);
          }

       /* build logo string based on platform */

    #ifndef OS_UNIX
       /* non-Unix */

       app_out("%s for %s Version %s", APNAME, PLATFORM_STRING,
               VERSION);
    #else
       /* Unix */

       app_out("%s for %s Version %s", APNAME, SUB_PLATFORM_STRING,
               VERSION);
    #endif

       app_out("By Rick Smereka, Copyright (c) 2006");
       app_out("%s comes with ABSOLUTELY NO WARRANTY", APNAME);
       app_out("This is free software, and you are welcome to redistribute it");
       app_out("under certain conditions; see 'gpl.txt' for information.");

       if (logman_is_active())
          {
          app_out("%s:logging is on", APNAME);
          app_out("%s:log name/destination is '%s'", APNAME, log_dest);
          }
       else
          app_out("%s:logging is off");

       app_out("%s:ping destination is '%s'", APNAME, ping_dest);
       app_out("%s:ping count is %d", APNAME, ping_count);
       (void)flag_2_logic_string(console_output, mes);
       app_out("%s:console output is %s", APNAME, mes);
       (void)flag_2_logic_string(use_bbuuzzb, mes);
       app_out("%s:Bbuuzzb database mode is %s", APNAME, mes);

       if (use_bbuuzzb)
          app_out("%s:output Bbuuzzb table is %s", APNAME, BBUUZZB_TABLE);

       process();
       term_app();
       return(0);
    }

    void process(void)
    {
       /* Execute the 'ping' command sending all program output to an
          output file. Read back the 'ping' output file to determine
          the response and possibly record the response in the
          'bbuuzzb' table. */

       char line[75], curdatime[15], curdate[9], curtime[5], mname[] = "process";
       char wrd[50];
       int ret, subfield, pings_sent, pings_recv;

       // build the command line

    #ifdef OS_UNIX
       sprintf(line, "ping -c %d %s >%s", ping_count, ping_dest, PING_OUTPUT_FILE);
    #else
       sprintf(line, "ping -n %d %s >%s", ping_count, ping_dest, PING_OUTPUT_FILE);
    #endif

       // execute the command

       app_out("%s:command=%s", mname, line);
       (void)system(line);

       // locate the stats line in the ping output file

       find_stats(line);

       if (!strlen(line))
          {
          // if there is no output from the 'ping' command, assume process gave up

          app_out("%s:output 'ping' line is empty,assuming nothing received",
                  mname);

          // mock-up output line

          sprintf(line, "sent %d,recv 0", ping_count);
          }

       app_out("%s:line=%s", mname, line);

       if (!use_bbuuzzb)
          return;

       /* store sent and received stats in daily record
          get the date and time now, use the hour and minute in the record and
          convert the date to julian */

       get_time(curdatime);
       strncpy(curtime, &curdatime[8], 4);
       curtime[4] = EOS;
       strncpy(curdate, curdatime, 8);
       curdate[8] = EOS;

       if ((ret = db_open(BBUUZZB_TABLE, &tid)) != DBENG_OK)
          {
          app_out("%s:unable to open table '%s'", mname, BBUUZZB_TABLE);
          return;
          }

       // attempt to locate existing record, if not present, create new record

       if ((ret = db_find_field(tid, 0, curdate, FIELD_DATE)) != DBENG_OK)
          {
          if (ret == DBENG_EOF)
             {
             app_out("%s:daily record does not exist,creating it", mname);

             if ((ret = db_new(tid)) != DBENG_OK)
                {
                app_out("%s:bad rc[%d] from db_new", mname, ret);
                return;
                }

             if ((ret = db_put_field(tid, FIELD_DATE, curdate)) != DBENG_OK)
                {
                app_out("%s:bad rc[%d] from db_put_field[date]", mname, ret);
                return;
                }

             subfield = 1;
             }
          else
             {
             app_out("%s:bad rc[%d] from db_find_field[date]", mname, ret);
             return;
             }
          }
       else
          {
          if ((ret = db_get_nsubfields(tid, FIELD_PING_DATA, &subfield))
              != DBENG_OK)
             {
             app_out("%s:bad rc[%d] from db_get_nsubfields", mname, ret);
             return;
             }

          subfield++;
          }

       // new subfields is assed onto the end of the list

       if ((ret = db_put_subsubfield(tid, FIELD_PING_DATA, subfield,
                                     SUBSUBFIELD_TIME, curtime)) != DBENG_OK)
          {
          app_out("%s:bad rc[%d] from db_put_subsubfield[time]", mname, ret);
          return;
          }

       /* extract send and received stats from ping output
          method chosen works on all platforms by grabbing
          the first number in the first two (comma delimited)
          words on the line */

       if (!get_sent_recv(line, 1, &pings_sent))
          return;

       if (!get_sent_recv(line, 2, &pings_recv))
          return;

       // put the rest of the table data and write

       sprintf(wrd, "%d", pings_sent);

       if ((ret = db_put_subsubfield(tid, FIELD_PING_DATA, subfield,
                                     SUBSUBFIELD_PINGS_SENT, wrd)) != DBENG_OK)
          {
          app_out("%s:bad rc[%d] from db_put_subsubfield[pings_sent]", mname, ret);
          return;
          }

       sprintf(wrd, "%d", pings_recv);

       if ((ret = db_put_subsubfield(tid, FIELD_PING_DATA, subfield,
                                     SUBSUBFIELD_PINGS_RECV, wrd)) != DBENG_OK)
          {
          app_out("%s:bad rc[%d] from db_put_subsubfield[pings_recv]", mname, ret);
          return;
          }

       if ((ret = db_write(tid)) != DBENG_OK)
          {
          app_out("%s:bad rc[%d] from db_write", mname, ret);
          return;
          }

       app_out("%s:curdate=%s,curtime=%s,subfield=%d", mname, curdate, curtime,
               subfield);
       app_out("%s:pings_sent=%d,pings_recv=%d", mname, pings_sent, pings_recv);
    }

    void find_stats(char *line)
    {
       /* Locate the line containing the stats. Returns the line containing
          the send and receive status (in 'line') upon success, an empty
          string otherwise. */

       FILE *in;
       char buf[128], mname[] = "find_stats";
       int nwords;

       line[0] = EOS;

       // open 'ping' output file

       if ((in = fopen(PING_OUTPUT_FILE, "r")) == (FILE *)NULL)
          {
          app_out("%s:unable to open ping output file '%s'",
                   mname, PING_OUTPUT_FILE);
          return;
          }

       /* read each line looking for the stats line (the first line with at
          least two comma delimited words) */

       get_rec(in, buf);

       while(!feof(in))
          {
          nwords = ll_words(buf, ',');

          if (nwords > 1)
             {
             strcpy(line, buf);
             fclose(in);
             return;
             }

          get_rec(in, buf);
          }

       fclose(in);
    }

    int get_sent_recv(char *line, int which, int *result)
    {
       /* Get the sent or received stats from the ping data. Returns 'TRUE'
          upon success with the result loaded into 'result', returns
          'FALSE' otherwise. */

       char parm[50], wrd[50], mname[] = "get_sent_recv";
       int nwords, i, tmp;

       *result = 0;

       if (!ll_word(line, parm, which, ','))
          {
          app_out("%s:bad rc(0) from ll_word[line(%d)]", mname, which);
          return(FALSE);
          }

       trim(parm, wrd);
       strcpy(parm, wrd);
       nwords = words(parm);

       for(i = 1; i <= nwords; i++)
          {
          if (!word(parm, wrd, i))
             {
             app_out("%s:bad rc[0] from word[%d]", mname, i);
             return(FALSE);
             }

          if (qatoi(wrd, &tmp))
             {
             *result = tmp;
             return(TRUE);
             }
          }

       app_out("%s:exit[0],count not found", mname);
       return(FALSE);
    }

    int parse_comline(int c_count, char **c_parm)
    {
       /* Parse the command line for parameters. Function
          returns 'TRUE' if no error was detected, 'FALSE'
          otherwise. */

       char mname[] = "parse_comline", tmp[25];
       int parms = 1, done = FALSE;

       /* set default log file */

       strcpy(log_dest, DEFAULT_LOG_FILE);
       strcpy(ping_dest, DEFAULT_HOST);

       if (c_count == 1)
          done = TRUE;

       while(!done)
          {
          if (c_parm[parms][0] == SWITCH_CHAR)
             {
             switch(toupper(c_parm[parms][1]))
                {
                case 'L':
                   if (strlen(c_parm[parms]) > 2)
                      printf("%s:%s:extraneous input with log switch, "
                             "ignoring\n", APNAME, mname);

                   parms++;

                   if (c_count > parms)
                      strcpy(log_dest, c_parm[parms++]);

                   log_output = TRUE;
                   break;

                case 'C':
                   if (strlen(c_parm[parms]) > 2)
                      printf("%s:%s:extraneous input with ping count switch, "
                             "ignoring\n", APNAME, mname);

                   parms++;

                   if (c_count <= parms)
                      {
                      printf("%s:%s:'c' switch present without ping count,"
                             "program abort\n", APNAME, mname);
                      return(FALSE);
                      }

                   strcpy(tmp, c_parm[parms++]);

                   if (!qatoi(tmp, &ping_count))
                      {
                      printf("%s:%s:non-numeric ping count,program abort\n",
                             APNAME, mname);
                      return(FALSE);
                      }

                   if (ping_count < 1)
                      {
                      printf("%s:%s:out of range ping count,s/b >0,abort\n",
                             APNAME, mname);
                      return(FALSE);
                      }

                   break;

                case 'B':
                   use_bbuuzzb = TRUE;
                   parms++;
                   break;

                case 'Q':
                   console_output = FALSE;
                   parms++;
                   break;

                case 'P':
                   if (strlen(c_parm[parms]) > 2)
                      printf("%s:%s:extraneous input with ping destination switch, "
                             "ignoring\n", APNAME, mname);

                   parms++;

                   if (c_count <= parms)
                      {
                      printf("%s:%s:'p' switch present without ping destination,"
                             "program abort\n", APNAME, mname);
                      return(FALSE);
                      }

                   strcpy(ping_dest, c_parm[parms++]);
                   break;

                default:
                   printf("%s:%s:unknown switch[%s],program abort\n",
                          APNAME, mname, c_parm[parms]);
                   return(FALSE);
                };
             }

          if (parms >= c_count)
             done = TRUE;
          }

       return(TRUE);
    }

    #ifdef MULTIUSER
    int c_initialize(void)
    {
       /* Initialize socket communication, form connection to
          'socloc' server (TCP only), update the config list and connect
          to a 'Bbuuzzb' server. Function returns 'TRUE' upon
          success, 'FALSE' otherwise. */

       char mname[] = "c_initialize";
       char mes[128];
       int ret;

    #ifdef IPC_TCP
       if (!c_ipc_socloc_init())
          return(FALSE);
    #endif

       /* attempt to locate a 'Bbuuzzb' server and connect to it */

       if ((ret = db_initialize()) != DBENG_OK)
          {
          db_io_code_string(ret, mes);
          printf("%s:error connecting to the Bbuuzzb server,rc[%s]. "
                 "Program abort\n", mname, mes);
          return(FALSE);
          }

       /* display current connected server host name and
          TCP port number */

    #ifdef IPC_TCP
       (void)db_get_active(mes, &ret);
       printf("%s:connected to Bbuuzzb server %s on TCP port %d (decimal)\n",
              mname, mes, ret);
    #endif

       return(TRUE);
    }
    #endif

    #ifndef MULTIUSER
    int c_initialize(void)
    {
       /* Start the 'DBENG' local engine, read the config file
          and display the result settings. Function returns
          'TRUE' upon success, 'FALSE' otherwise. */

       char mname[] = "c_initialize";
       char *mes;
       int ret, flag;

    #ifdef DEBUG
    #ifdef IPC_TCP
       if (!c_ipc_socloc_init())
          return(FALSE);
    #endif
    #endif

       if ((mes = (char *)malloc(DBENG_PATH_LIMIT)) == (char *)NULL)
          {
          printf("%s:alloc fail[mes]. Program abort\n", mname);
          return(FALSE);
          }

       ret = db_initialize();

       /* interpret code from init which may be
          a fatal config error */

       switch(ret)
          {
          case DBENG_OK:
             printf("%s:read config file successfully\n", mname);
             break;

          case DBENG_CONFIG_UNABLE_TO_OPEN:
             printf("%s:config file not found, loaded defaults\n", mname);
             break;

          default:
             db_io_code_string(ret, mes);
             printf("%s:fatal config error[%s]\n", mname, mes);
             printf("%s:terminating\n", mname);
             free(mes);
             return(FALSE);
             break;
          };

       (void)db_config_get_tmp_path(mes);
       printf("%s:tmp path is '%s'\n", mname, mes);
       (void)db_config_get_log(mes);
       printf("%s:log is '%s'\n", mname, mes);

       if ((ret = db_config_get_catalog(mes)) == DBENG_OK)
          printf("%s:catalog is '%s'\n", mname, mes);
       else
          printf("%s:catalog name not loaded\n", mname);

       (void)db_config_get_catalog_flag(&flag);

       if (flag_2_logic_string(flag, mes))
          printf("%s:catalog is %s\n", mname, mes);

       free(mes);
       return(TRUE);
    }
    #endif

    #ifdef MULTIUSER
    #ifdef IPC_TCP
    int c_ipc_socloc_init(void)
    {
       /* Initialize socket communication, form connection to
          'socloc' server (TCP only) and update the config list.
          Function returns 'TRUE' upon success, 'FALSE' otherwise. */

       char mname[] = "c_ipc_socloc_init";
       char *thelist, mes[128];
       int ret;

       /* startup WinSock (if Windoze) */

    #ifdef OS_WIN32
       if (WSAStartup(WINSOCK_VERSION, &wsaData))
          {
          printf("%s:unable to start WinSock. "
                "Program abort\n", mname);
          return(FALSE);
          }
    #endif

       /* attempt to connect to a 'socloc' server */

       if ((ret = sloc_initialize()) != SL_OK)
          {
          sl_code_string(ret, mes);
          printf("%s:error initializing socloc interface,rc[%s]. "
                "Program abort\n", mname, mes);
          return(FALSE);
          }

       /* get config list from 'socloc' server and update
          the client list with it */

       if ((thelist = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          printf("%s:alloc fail[thelist]. Program abort\n", mname);
          return(FALSE);
          }

       if ((ret = sloc_config_get_list(thelist)) != SL_OK)
          {
          free(thelist);
          sl_code_string(ret, mes);
          printf("%s:error getting socloc config list,rc[%s]. "
                 "Program abort\n", mname, mes);
          return(FALSE);
          }

       if ((ret = sl_config_put_list(thelist)) != SL_OK)
          {
          sl_code_string(ret, mes);
          printf("%s:error putting socloc config list,rc[%s]. ",
                 "Program abort\n", mname, mes);
          free(thelist);
          return(FALSE);
          }

       free(thelist);
       return(TRUE);
    }
    #endif
    #endif

    void app_out(char *fmt,...)
    {
       /* Output a message to the console and/or the log file. */

       va_list argptr;
       char *mes, mname[] = "app_out";
       int ret;

       /* if no output, return */

       if (!console_output && !log_output)
          return;

       va_start(argptr, fmt);

       if ((mes = (char *)malloc(BUFSIZE)) == (char *)NULL)
          {
          logman("%s:%s:alloc fail[mes]", APNAME, mname);
          return;
          }

       /* format message */

       memset(mes, 0, BUFSIZE);
       vsprintf(mes, fmt, argptr);

       if (log_output)
          logman_nf(mes);

       if (!log_output && console_output)
          printf("%s\n", mes);

       free(mes);
    }

    void term_app(void)
    {
       // Prepare to terminate the application. Shutdown all IPC API's.

       if (logman_is_active())
          logman_end();

       if (use_bbuuzzb)
          {
          (void)db_close(tid);
          db_end();

    #ifdef MULTIUSER
    #ifdef IPC_TCP
          if (sloc_is_init())
             sloc_term_api();

    #ifdef OS_WIN32
          WSACleanup();
    #endif
    #endif
    #endif

          (void)appinit_remove_name();
          }
    }

/* [<][>][^][v][top][bottom][index][help] */