/* $Id: isdnlog.c,v 1.11 1997/05/09 23:30:47 luethje Exp $ * * ISDN accounting for isdn4linux. (log-module) * * Copyright 1995, 1997 by Andreas Kool (akool@Kool.f.EUnet.de) * and Stefan Luethje (luethje@sl-gw.lake.de) * * 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, 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, write to the Free Software * * $Log: isdnlog.c,v $ * Revision 1.11 1997/05/09 23:30:47 luethje * isdnlog: new switch -O * isdnrep: new format %S * bugfix in handle_runfiles() * * Revision 1.10 1997/05/06 22:13:26 luethje * bugfixes in HTML-Code of the isdnrep * * Revision 1.9 1997/05/04 20:19:47 luethje * README completed * isdnrep finished * interval-bug fixed * * Revision 1.8 1997/04/17 20:09:32 luethje * patch of Ingo Schneider * * Revision 1.7 1997/04/08 21:56:48 luethje * Create the file isdn.conf * some bug fixes for pid and lock file * make the prefix of the code in `isdn.conf' variable * * Revision 1.6 1997/04/08 00:02:14 luethje * Bugfix: isdnlog is running again ;-) * isdnlog creates now a file like /var/lock/LCK..isdnctrl0 * README completed * Added some values (countrycode, areacode, lock dir and lock file) to * the global menu * */ #define _ISDNLOG_C_ #include #include "isdnlog.h" #ifdef POSTGRES #include "postgres.h" #endif #define FD_SET_MAX(desc, set, max) { if (desc > max) max=desc; FD_SET(desc,set); } /*****************************************************************************/ /* Letzte Exit-Nummer: 45 */ /*****************************************************************************/ static void loop(void); static void init_variables(int argc, char* argv[]); static int set_options(int argc, char* argv[]); static void hup_handler(int isig); static void exit_on_signal(int Sign); int print_in_modules(const char *fmt, ...); static int read_param_file(char *FileName); /*****************************************************************************/ static char usage[] = "%s: usage: %s [ -%s ] file\n"; #ifdef Q931 static char options[] = "av:sp:x:m:l:rt:c:C:w:SVTDPMh:nW:H:f:bL:NqFA:2:O:"; #else static char options[] = "av:sp:x:m:l:rt:c:C:w:SVTDPMh:nW:H:f:bL:NFA:2:O:"; #endif static char msg1[] = "%s: Can't open %s (%s)\n"; static char *ptty = NULL; static section *opt_dat = NULL; /*****************************************************************************/ static void exit_on_signal(int Sign) { print_msg(PRT_NORMAL, "Got signal %d\n", Sign); Exit(7); } /* exit_on_signal */ /*****************************************************************************/ static void hup_handler(int isig) { print_msg(PRT_INFO, "re-reading %s\n", CONFFILE); discardconfig(); if (readconfig(myname) != 0) Exit(41); signal(SIGHUP, hup_handler); } /* hup_handler */ /*****************************************************************************/ static void loop(void) { auto fd_set readmask; auto fd_set exceptmask; auto int maxdesc = 0; auto int Cnt; auto int len; auto int queuenumber; auto int NewSocket; auto int NewClient = 0; auto struct sockaddr incoming; auto int Interval; if (xinfo) { first_descr++; if ((NewSocket = listening(port)) >= 0) { if (add_socket(&sockets, NewSocket)) Exit(11); } else Exit(17); } else if (add_socket(&sockets, -1)) Exit(11); while (1) { if (replay) { if (trace) dotrace(); if (!morectrl(0)) break; } else { FD_ZERO(&readmask); FD_ZERO(&exceptmask); queuenumber = socket_size(sockets); if (NewClient) { /* Damit sich der neue Client anmelden kann, ohne das was anderes dazwischen funkt ... */ if (sockets[NewClient].descriptor >= 0) FD_SET_MAX(sockets[NewClient].descriptor, &readmask, maxdesc); NewClient = 0; } else { for (Cnt = 0; Cnt < queuenumber; Cnt++) if (sockets[Cnt].descriptor >= 0) FD_SET_MAX(sockets[Cnt].descriptor, &readmask, maxdesc); for (Cnt = first_descr; Cnt < queuenumber; Cnt++) if (sockets[Cnt].descriptor >= 0) FD_SET_MAX(sockets[Cnt].descriptor, &exceptmask, maxdesc); } /* else */ if (newcps && ((ifo[0].u & ISDN_USAGE_MASK) + (ifo[1].u & ISDN_USAGE_MASK))) Interval = wakeup; /* AK:06-Jun-96 */ else Interval = 0; while ((Cnt = select(maxdesc+1, &readmask, NULL, &exceptmask, Get_Interval(Interval))) < 0 && (errno == EINTR)); if ((Cnt < 0) && (errno != EINTR)) { /* kill -HUP ausgeschlossen */ print_msg(PRT_DEBUG_CS, "Error select: %s\n", strerror(errno)); Exit(12); } /* if */ processrate(); if (!Cnt) /* Abbruch durch Timeout -> Programm starten */ Start_Interval(); now(); for (Cnt = first_descr; Cnt < socket_size(sockets); Cnt++) { if (FD_ISSET(sockets[Cnt].descriptor, &exceptmask)) { if (sockets[Cnt].fp == NULL) { disconnect_client(Cnt); break; } else { int event = sockets[Cnt].call_event; int cur_call = sockets[Cnt].chan; info_args *infoarg = sockets[Cnt].info_arg; Ring(NULL, NULL, Cnt, 0); if (infoarg->flag & RING_LOOP && call[cur_call].cur_event == event) Start_Process(cur_call, infoarg, event); break; } /* else */ } else if (FD_ISSET(sockets[Cnt].descriptor, &readmask)) if (sockets[Cnt].fp == NULL) { eval_message(Cnt); /* Arbeite immer nur ein Client ab, du weisst nicht, ob der naechste noch lebt */ break; } else Print_Cmd_Output(Cnt); } /* for */ if (xinfo && FD_ISSET(sockets[IN_PORT].descriptor, &readmask)) { len = sizeof(incoming); if ((NewSocket = accept(sockets[IN_PORT].descriptor, &incoming, &len)) == -1) print_msg(PRT_DEBUG_CS, "Error accept: %s\n", strerror(errno)); else { if (add_socket(&sockets, NewSocket)) Exit(13); queuenumber = socket_size(sockets); sockets[queuenumber - 1].f_hostname = GetHostByAddr(&incoming); sockets[queuenumber - 1].waitstatus = WF_ACC; NewClient = queuenumber - 1; } /* else */ } else if (FD_ISSET(sockets[ISDNINFO].descriptor, &readmask)) moreinfo(); else if (FD_ISSET(sockets[ISDNCTRL].descriptor, &readmask)) (void)morectrl(0); else if (FD_ISSET(sockets[ISDNCTRL2].descriptor, &readmask)) (void)morectrl(1); } /* else */ } /* while */ } /* loop */ /*****************************************************************************/ int print_in_modules(const char *fmt, ...) { auto va_list ap; auto char String[LONG_STRING_SIZE]; va_start(ap, fmt); (void)vsnprintf(String, LONG_STRING_SIZE, fmt, ap); va_end(ap); return print_msg(PRT_ERR, "%s", String); } /* print_in_modules */ /*****************************************************************************/ static void init_variables(int argc, char* argv[]) { flog = NULL; /* /var/adm/isdn.log */ fcons = NULL; /* /dev/ttyX (or stderr) */ fprot = NULL; /* /tmp/isdnctrl0 */ isdnctrl = NULL; fout = NULL; first_descr = FIRST_DESCR; message = PRT_NORMAL | PRT_WARN | PRT_ERR | PRT_INFO; syslogmessage = PRT_NOTHING; xinfo = 0; sound = 0; trace = 0; isdaemon = 0; imon = 0; port = 0; wakeup = 1; fullhour = 0; tty_dv = 0; net_dv = 0; inf_dv = 0; settime = 0; replay = 0; replaydev = 0; verbose = 0; synclog = 0; any = 1; stdoutput = 0; allflags = 0; newcps = 0; chans = 2; hupctrl = 0; hup1 = hup2 = 0; bilingual = 0; mcalls = MAX_CALLS_IN_QUEUE; xlog = MAX_PRINTS_IN_QUEUE; outfile = NULL; sockets = NULL; known = NULL; opt_dat = NULL; newline = 1; width = 0; watchdog = 0; use_new_config = 1; #ifdef Q931 q931dmp = 0; #endif CityWeekend = 0; sprintf(mlabel, "%%s%s %%s%%s", "%e.%b %T %I"); amtsholung = NULL; dual = 0; myname = argv[0]; myshortname = basename(myname); } /* init_variables */ /*****************************************************************************/ #ifdef Q931 static void traceoptions() { q931dmp++; use_new_config = 0; replay++; port = 20012; } /* traceoptions */ #endif /*****************************************************************************/ int set_options(int argc, char* argv[]) { register int c; register char *p; auto int defaultmsg = PRT_NORMAL | PRT_WARN | PRT_ERR | PRT_INFO; auto int newmessage = 0; if (!message) message = defaultmsg; #ifdef Q931 if (!strcmp(myshortname, "trace")) traceoptions(); #endif while ((c = getopt(argc, argv, options)) != EOF) switch (c) { case 'V' : print_version(myshortname); exit(0); break; case 'v' : verbose = strtol(optarg, NIL, 0); break; case 's' : synclog++; break; case 'p' : port = strtol(optarg, NIL, 0); break; case 'm' : newmessage = strtol(optarg, NIL, 0); if (!newmessage) { newmessage = defaultmsg; printf("%s: WARNING: \"-m\" Option now requires numeric Argument\n", myshortname); } /* if */ break; case 'l' : syslogmessage = strtol(optarg, NIL, 0); if (!syslogmessage) { syslogmessage = defaultmsg; printf("%s: WARNING: \"-l\" Option requires numeric Argument\n", myshortname); } /* if */ break; case 'x' : xinfo = strtol(optarg, NIL, 0); if (!xinfo) xinfo = defaultmsg; break; case 'r' : replay++; break; case 't' : settime = strtol(optarg, NIL, 0); /* 1=once, 2=ever */ break; case 'C' : ptty = strdup(optarg); break; case 'M' : imon++; break; case 'S' : sound++; break; case 'w' : wakeup = strtol(optarg, NIL, 0); break; case 'D' : isdaemon++; break; case 'T' : trace++; break; case 'a' : any = 0; break; case 'P' : stdoutput = PRT_LOG; break; case 'h' : hupctrl++; if ((p = strchr(optarg, ':'))) { *p = 0; hup1 = atoi(optarg); hup2 = atoi(p + 1); } else printf("%s: WARNING: \"-h\" Option requires two Arguments\n", myshortname); break; case 'b' : bilingual++; break; case 'c' : mcalls = strtol(optarg, NIL, 0); break; case 'L' : xlog = strtol(optarg, NIL, 0); break; case 'f' : read_param_file(optarg); break; case 'n' : newline = 0; break; case 'W' : width = strtol(optarg, NIL, 0); break; case 'H' : watchdog = strtol(optarg, NIL, 0); break; case 'N' : use_new_config = 0; break; #ifdef Q931 case 'q' : traceoptions(); break; #endif case 'F' : CityWeekend++; break; case 'A' : amtsholung = strdup(optarg); break; case '2' : dual = strtol(optarg, NIL, 0); break; case 'O' : outfile = strdup(optarg); break; case '?' : printf(usage, myshortname, myshortname, options); exit(1); } /* switch */ if (newmessage) message = newmessage; if (trace && isdaemon) { printf("%s","Can not trace daemonized!\n"); exit(20); } /* if */ if (stdoutput && isdaemon) { printf("%s","Can not write to stdout as daemon!\n"); exit(21); } /* if */ if (isdaemon) { if (syslogmessage == -1) syslogmessage = defaultmsg; switch (fork()) { case -1 : print_msg(PRT_ERR,"%s","Can not fork()!\n"); Exit(18); break; case 0 : break; default : _exit(0); } /* Wenn message nicht explixit gesetzt wurde, dann gibt es beim daemon auch kein Output auf der Console/ttyx */ if (!newmessage && ptty == NULL) message = 0; } /* if */ allflags = syslogmessage | message | xinfo | stdoutput; return optind; } /* set_options */ /*****************************************************************************/ static int read_param_file(char *FileName) { register char *p; auto section *SPtr; auto entry *Ptr; if (opt_dat != NULL) { print_msg(PRT_ERR, "Only one option file allowed (file %s)!\n", FileName); return -1; } if ((SPtr = opt_dat = read_file(NULL, FileName, 0)) == NULL) return -1; while (SPtr != NULL) { if (!strcmp(SPtr->name,CONF_SEC_OPT)) { Ptr = SPtr->entries; while (Ptr != NULL) { if (!strcmp(Ptr->name,CONF_ENT_DEV)) isdnctrl = Ptr->value; else if (!strcmp(Ptr->name,CONF_ENT_LOG)) verbose = (int)strtol(Ptr->value, NIL, 0); else if (!strcmp(Ptr->name,CONF_ENT_FLUSH)) synclog = toupper(*(Ptr->value)) == 'Y'?1:0; else if (!strcmp(Ptr->name,CONF_ENT_PORT)) port = strtol(Ptr->value, NIL, 0); else if (!strcmp(Ptr->name,CONF_ENT_STDOUT)) message = strtol(Ptr->value, NIL, 0); else if (!strcmp(Ptr->name,CONF_ENT_SYSLOG)) syslogmessage = strtol(Ptr->value, NIL, 0); else if (!strcmp(Ptr->name,CONF_ENT_XISDN)) { xinfo = strtol(Ptr->value, NIL, 0); } else if (!strcmp(Ptr->name,CONF_ENT_TIME)) settime = (int)strtol(Ptr->value, NIL, 0); else if (!strcmp(Ptr->name,CONF_ENT_CON)) ptty = Ptr->value; else if (!strcmp(Ptr->name,CONF_ENT_START)) sound = toupper(*(Ptr->value)) == 'Y'?1:0; else if (!strcmp(Ptr->name,CONF_ENT_THRU)) wakeup = strtol(Ptr->value, NIL, 0); else if (!strcmp(Ptr->name,CONF_ENT_DAEMON)) isdaemon = toupper(*(Ptr->value)) == 'Y'?1:0; else if (!strcmp(Ptr->name,CONF_ENT_MON)) imon = toupper(*(Ptr->value)) == 'Y'?1:0; else if (!strcmp(Ptr->name,CONF_ENT_PIPE)) stdoutput = toupper(*(Ptr->value)) == 'Y'?PRT_LOG:0; else if (!strcmp(Ptr->name,CONF_ENT_MON)) imon = toupper(*(Ptr->value)) == 'Y'?1:0; else if (!strcmp(Ptr->name,CONF_ENT_HANGUP)) { hupctrl++; if ((p = strchr(Ptr->value, ':'))) { *p = 0; hup1 = atoi(Ptr->value); hup2 = atoi(p + 1); } /* if */ } else if (!strcmp(Ptr->name,CONF_ENT_BI)) bilingual = toupper(*(Ptr->value)) == 'Y'?1:0; else if (!strcmp(Ptr->name,CONF_ENT_CALLS)) mcalls = strtol(Ptr->value, NIL, 0); else if (!strcmp(Ptr->name,CONF_ENT_XLOG)) xlog = strtol(Ptr->value, NIL, 0); else if (!strcmp(Ptr->name,CONF_ENT_NL)) newline = toupper(*(Ptr->value)) == 'Y'?1:0; else if (!strcmp(Ptr->name,CONF_ENT_WIDTH)) width = (int)strtol(Ptr->value, NIL, 0); else if (!strcmp(Ptr->name,CONF_ENT_CW)) CityWeekend = toupper(*(Ptr->value)) == 'Y'?1:0; else if (!strcmp(Ptr->name,CONF_ENT_DUAL)) dual = (int)strtol(Ptr->value, NIL, 0); else if (!strcmp(Ptr->name,CONF_ENT_AMT)) amtsholung = strdup(Ptr->value); else #ifdef Q931 if (!strcmp(Ptr->name,CONF_ENT_Q931)) { if(toupper(*(Ptr->value)) == 'Y') traceoptions(); } else #endif if (!strcmp(Ptr->name,CONF_ENT_WD)) watchdog = (int)strtol(Ptr->value, NIL, 0); else if (!strcmp(Ptr->name,CONF_ENT_OUTFILE)) outfile = Ptr->value; else print_msg(PRT_ERR,"Error: Invalid entry `%s'!\n",Ptr->name); Ptr = Ptr->next; } } else print_msg(PRT_ERR,"Error: Invalid section `%s'!\n",SPtr->name); SPtr = SPtr->next; } return 0; } /*****************************************************************************/ static void restoreCharge() { register int i, j, n = 0, nx = max(knowns, 1); auto FILE *f; auto char fn[BUFSIZ]; typedef struct { char who[16]; int day; double charge; double scharge; int month; double online; double sonline; double bytes; double sbytes; } CHARGE; auto CHARGE *charge; if ((charge = (CHARGE *)malloc(sizeof(CHARGE) * nx)) != (CHARGE *)NULL) { sprintf(fn, "%s/%s", confdir(), CHARGEFILE); if ((f = fopen(fn, "r")) != (FILE *)NULL) { while (fscanf(f, "%s %d %lg %lg %d %lg %lg %lg %lg\n", charge[n].who, &charge[n].day, &charge[n].charge, &charge[n].scharge, &charge[n].month, &charge[n].online, &charge[n].sonline, &charge[n].bytes, &charge[n].sbytes) == 9) { n++; if (n == nx) { nx++; if ((charge = (CHARGE *)realloc((void *)charge, sizeof(CHARGE) * nx)) == (CHARGE *)NULL) { fclose(f); return; } /* if */ } /* if */ } /* while */ if (n) { for (i = 0; i < knowns; i++) for (j = 0; j < n; j++) if (!strcmp(known[i]->who, charge[j].who)) { known[i]->day = charge[j].day; known[i]->charge = charge[j].charge; known[i]->scharge = charge[j].scharge; known[i]->month = charge[j].month; known[i]->online = charge[j].online; known[i]->sonline = charge[j].sonline; known[i]->bytes = charge[j].bytes; known[i]->sbytes = charge[j].sbytes; break; } /* if */ } /* if */ fclose(f); } /* if */ free(charge); } /* if */ } /* restoreCharge */ /*****************************************************************************/ int main(int argc, char *argv[], char *envp[]) { register char *p; register int i, res = 0; auto int lastarg; auto char fn[BUFSIZ]; auto char rlogfile[PATH_MAX]; auto char **devices = NULL; #ifdef TESTCENTER extern void test_center(void); #endif if (getuid() != 0) { fprintf(stderr,"Can only run as root!\n"); exit(35); } /* if */ set_print_fct_for_lib(print_in_modules); init_variables(argc, argv); lastarg = set_options(argc,argv); if (outfile != NULL) { if (!message) { print_msg(PRT_ERR,"Can not set outfile `%s', when -m is not set!\n",outfile); Exit(44); } else { if ((fout = fopen(outfile,"w")) == NULL) { print_msg(PRT_ERR,"Can not open file `%s': %s!\n",outfile, strerror(errno)); Exit(45); } } } if (lastarg < argc) isdnctrl = argv[lastarg]; if (isdnctrl == NULL) { print_msg(PRT_ERR,"There is no valid input device!\n"); Exit(31); } else { p = strrchr(isdnctrl, C_SLASH); if (add_socket(&sockets, -1) || /* reserviert fuer isdnctrl */ add_socket(&sockets, -1) || /* reserviert fuer isdnctrl2 */ add_socket(&sockets, -1) ) /* reserviert fuer isdninfo */ Exit(19); if (replay) { verbose = 0; synclog = 0; settime = 0; xinfo = 0; *isdnctrl2 = 0; } else { if (strcmp(isdnctrl, "-") && dual) { strcpy(isdnctrl2, isdnctrl); p = strrchr(isdnctrl2, 0) - 1; if (!isdigit(*p)) /* "/dev/isdnctrl" */ strcat(++p, "0"); if (isdigit(*(p - 1))) p--; i = atoi(p); sprintf(p, "%d", i + 2); } else *isdnctrl2 = 0; } /* else */ openlog(myshortname, LOG_NDELAY, LOG_DAEMON); #ifdef TESTCENTER test_center(); #endif if (xinfo && read_user_access()) Exit(22); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, hup_handler); signal(SIGINT, exit_on_signal); signal(SIGTERM, exit_on_signal); signal(SIGALRM, exit_on_signal); signal(SIGQUIT, exit_on_signal); signal(SIGILL, exit_on_signal); if (!(allflags & PRT_DEBUG_GENERAL)) signal(SIGSEGV, exit_on_signal); sockets[ISDNCTRL].descriptor = !strcmp(isdnctrl, "-") ? fileno(stdin) : open(isdnctrl, O_RDONLY | O_NONBLOCK); if (*isdnctrl2) sockets[ISDNCTRL2].descriptor = open(isdnctrl2, O_RDONLY | O_NONBLOCK); if ((sockets[ISDNCTRL].descriptor >= 0) && (!*isdnctrl2 || (sockets[ISDNCTRL2].descriptor >= 0))) { if (ptty != NULL) fcons = fopen(ptty, "a"); if ((ptty == NULL) || (fcons != (FILE *)NULL)) { if (verbose) { if ((p = strrchr(isdnctrl, '/'))) p++; else p = argv[lastarg]; sprintf(fn, "%s/%s", TMPDIR, p); } /* if */ if (!verbose || ((fprot = fopen(fn, "a")) != (FILE *)NULL)) { for (i = 0; i < MAXCHAN; i++) clearchan(i, 1); #ifdef Q931 if (q931dmp) { mymsns = 3; mycountry = "+49"; myarea = "6408"; currency = NULL; dual = 1; chargemax = 0.0; connectmax = 0.0; bytemax = 0.0; connectmaxmode = 0; bytemaxmode = 0; knowns = retnum = 0; known = (KNOWN **)NULL; start_procs.infoargs = NULL; start_procs.flags = 0; setDefaults(); } else #endif { if (readconfig(myshortname) < 0) Exit(30); restoreCharge(); } /* if */ if (replay) { sprintf(rlogfile, "%s.rep", logfile); logfile = rlogfile; } else { append_element(&devices,isdnctrl); switch (i = handle_runfiles(myshortname,devices,START_PROG)) { case 0 : break; case -1 : print_msg(PRT_ERR,"Can not open pid/lock file: %s!\n", strerror(errno)); Exit(36); default : Exit(37); } /* switch */ } /* if */ if (replay || ((sockets[ISDNINFO].descriptor = open(INFO, O_RDONLY | O_NONBLOCK)) >= 0)) { now(); #ifdef Q931 if (!q931dmp) #endif print_msg(PRT_NORMAL, "%s Version %s loaded\n", myshortname, VERSION); #ifdef POSTGRES dbOpen(); #endif loop(); if (sockets[ISDNINFO].descriptor >= 0) close(sockets[ISDNINFO].descriptor); } else { print_msg(PRT_ERR, msg1, myshortname, INFO, strerror(errno)); res = 7; } /* else */ if (verbose) fclose(fprot); } else { print_msg(PRT_ERR, msg1, myshortname, fn, strerror(errno)); res = 5; } /* else */ if (ptty != NULL) fclose(fcons); } else { print_msg(PRT_ERR, msg1, myshortname, fn, strerror(errno)); res = 3; } /* else */ if (strcmp(isdnctrl, "-")) close(sockets[ISDNCTRL].descriptor); if (*isdnctrl2) close(sockets[ISDNCTRL2].descriptor); } else { print_msg(PRT_ERR, msg1, myshortname, isdnctrl, strerror(errno)); res = 2; } /* else */ #ifdef Q931 if (!q931dmp) #endif print_msg(PRT_NORMAL, "%s Version %s exiting\n", myshortname, VERSION); } /* else */ Exit(res); return(res); } /* main */