/* Copyright (c) 2010, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above opyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file fsnmpcmd.c The fsnmpcmd module in the fsnmp program. */ /** Inside the fsnmpcmd module. */ #define FSNMPCMD_C 1 #include "fsnmp.h" $(trace-include) /** Name of environment variable for printcap entry. */ static char env_name_printcap_entry[] = { "PRINTCAP_ENTRY" }; /** Printcap key for output filter. */ static char printcap_key_of[] = { ":of=" }; /** Default configuration file name. */ static char default_config_file[] = { DK_SYSCONFDIR "/fsnmp/fsnmp.conf" }; /** Command: lp. */ static char *cmd00[] = { "lp", NULL }; /** Command: SNMP version. */ static char *cmd01[] = { "snmp", "version", NULL }; /** Command: SNMP community. */ static char *cmd02[] = { "snmp", "community", NULL }; /** Command: Ctrl-D at start. */ static char *cmd03[] = { "ctrl-d", "at", "start", NULL }; /** Command: Ctrl-D at end. */ static char *cmd04[] = { "ctrl-d", "at", "end", NULL }; /** Command: Shutdown data socket. */ static char *cmd05[] = { "shutdown", "data", "socket", NULL }; /** Command: Minimum pagecount. */ static char *cmd06[] = { "minimum", "pagecount", "time", NULL }; /** Command: Shutdown accounting socket. */ static char *cmd07[] = { "shutdown", "accounting", "socket", NULL }; /** Command: Data transfer port range. */ static char *cmd08[] = { "data", "transfer", "port", "range", NULL }; /** Command: accounting port. */ static char *cmd09[] = { "accounting", "port", "range", NULL }; /** Command: accounting destination. */ static char *cmd10[] = { "accounting", "destination", NULL }; /** Command in configuration file. */ static char **config_commands[] = { cmd00, cmd01, cmd02, cmd03, cmd04, cmd05, cmd06, cmd07, cmd08, cmd09, cmd10, NULL }; /** SNMP versions. */ static char *snmp_versions[] = { "1", "2", "2c", "2p", "3", NULL }; /** Check whether log file is already open. @param fc Fsnmp job. */ static void check_logging DK_P1(FC *,fc) { char *pce; if(!((fc->flags) & FSNMP_FLAG_OF)) { pce = getenv(env_name_printcap_entry); if(pce) { if(strstr(pce, printcap_key_of)) { fc->flags |= FSNMP_FLAG_HAVE_LOG; } } } } /** Initialize fsnmp job. @param fc Fsnmp job to initialize. */ void fsnmpcmd_init DK_P1(FC *,fc) { $? "+ fsnmpcmd_init" // default flags fc->flags = FSNMP_FLAGS_DEFAULTS; // assume normal operation as long as there is no error yet fc->exit_code = EXIT_SUCCESS; fc->buffer_in = NULL; fc->sz_buffer_in = 0; fc->buffer_out = NULL; fc->sz_buffer_out = 0; fc->sz_bo_used = 0; fc->a_hostport = NULL; fc->hostname = NULL; fc->portnumber = 0; fc->a_acctdest = NULL; fc->temp_file = NULL; fc->sz_temp_file = 0; fc->a_community = NULL; fc->snmp_vers = SNMP_VERSION_1; fc->argv = NULL; fc->arg_0 = NULL; fc->os = -1; fc->last_timestamp = 0; (fc->shd).tv_sec = 0; (fc->shd).tv_usec = 0; (fc->shp).tv_sec = 0; (fc->shp).tv_usec = 0; fc->mintimepc = 60; fc->oid_ps = NULL; fc->oid_ds = NULL; fc->oid_pc = NULL; fc->sz_oid_ps = 0; fc->sz_oid_ds = 0; fc->sz_oid_pc = 0; fc->pc1 = 0UL; fc->pc2 = 0UL; fc->dt_pmin = 0; fc->dt_pmax = 0; fc->ac_pmin = 0; fc->ac_pmax = 0; $? "- fsnmpcmd_init" } /** Retrieve one command line option argument. @param fc Fsnmp job. @param k Key character to find argument for. @return Argument on success, NULL on error. */ char * fsnmpcmd_get_argv DK_P2(FC *,fc, char,k) { char *back = NULL; $? "+ fsnmpcmd_get_argv %c", k if((k >= 'a') && (k <= 'z')) { back = (fc->argv)[k - 'a']; } else { if((k >= 'A') && (k <= 'Z')) { back = (fc->argv)[26 + k - 'A']; } } $? "- fsnmpcmd_get_argv %s", TR_STR(back) return back; } /** Apply command line arguments. @param fc Fsnmp job. @param argc Number of command line arguments. @param argv Command line arguments array. */ void fsnmpcmd_apply_argv DK_P3(FC *,fc, int,argc, char **,argv) { int i; char *ptr, *vptr, **lfdptr; $? "+ fsnmpcmd_apply_argv" // traverse all arguments lfdptr = argv; lfdptr++; i = 1; while(i < argc) { ptr = *lfdptr; $? ". argv[%d]=\"%s\"", i, ptr if(*ptr == '-') { ptr++; vptr = ptr; vptr++; if((*ptr >= 'a') && (*ptr <= 'z')) { (fc->argv)[*ptr - 'a'] = vptr; } else { if((*ptr >= 'A') && (*ptr <= 'Z')) { (fc->argv)[26 + *ptr - 'A'] = vptr; } } } else { fc->arg_0 = ptr; } lfdptr++; i++; } // if we have -Fo we are running as of filter ptr = fsnmpcmd_get_argv(fc, 'F'); if(ptr) { if(*ptr == 'o') { fc->flags |= FSNMP_FLAG_OF; } } // only one of the filters may overwrite the status file check_logging(fc); $? "- fsnmpcmd_apply_argv" } /** Create file name for temporary file. @param fc Fsnmp job. */ void fsnmpcmd_create_temp_file_name DK_P1(FC *,fc) { pid_t mypid; unsigned long test_number = 0UL; int cc; /* struct stat stbuf; */ dk_stat_t dkstatbuf; $? "+ fsnmpcmd_create_temp_file_name" if(fc->exit_code == EXIT_SUCCESS) { fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[10]); cc = 1; mypid = getpid(); do { // create file name candidate sprintf(fc->temp_file, "tempfile-%lu-%lu.dat", (unsigned long)mypid, test_number); // check whether file name candidate can be used if(!dkstat_get(&dkstatbuf, fc->temp_file)) { cc = 0; } // increase number for next candidate, abort on number overflow if(cc) { test_number++; if(test_number == 0UL) { cc = 0; fc->exit_code = EXIT_FAILED; } } } while(cc && fsnmp_cc(fc)); if(fc->exit_code == EXIT_SUCCESS) { fsnmplog(fc, PRIO_INFO, fsnmp_kw[12], fc->temp_file); } else { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[11]); } } $? "- fsnmpcmd_create_temp_File_name %s", fc->temp_file } /** Process one configuration entry. @param fc Fsnmp job. @param l Input line to process. @return 1 on success, 0 on error. */ static int add_config_line DK_P2(FC *,fc, char *,l) { char *p1, *p2, *x, *nv, *splitted[16]; size_t sz; int i = 0; int back = 0; $? "+ add_config_line" p1 = l; p2 = dkstr_chr(p1, '='); if(p2) { *(p2++) = '\0'; p2 = dkstr_start(p2, NULL); p1 = dkstr_start(p1, NULL); if(p1 && p2) { dkstr_chomp(p1, NULL); sz = dkstr_explode(splitted, 15, p1, NULL); if(sz > 0) { i = dkstr_find_multi_part_cmd(splitted, config_commands, 1); switch(i) { case 0: { // lp nv = dkstr_dup(p2); if(nv) { if(fc->a_hostport) { x = fc->a_hostport; dk_delete(x); fc->a_hostport = NULL; } fc->a_hostport = nv; back = 1; } else { fc->exit_code = EXIT_FAILED; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[52]); } } break; case 1: { // snmp version switch(dkstr_array_index(snmp_versions, p2, 0)) { case 0: fc->snmp_vers = SNMP_VERSION_1; back = 1; break; case 1: case 2: fc->snmp_vers = SNMP_VERSION_2c; back = 1; break; case 3: fc->snmp_vers = SNMP_VERSION_2p; back = 1; break; case 4: fc->snmp_vers = SNMP_VERSION_3; back = 1; break; } } break; case 2: { // snmp community back = 1; nv = dkstr_dup(p2); if(nv) { if(fc->a_community) { x = fc->a_community; dk_delete(x); fc->a_community = NULL; } fc->a_community = nv; } else { fc->exit_code = EXIT_FAILED; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[52]); } } break; case 3: { // ctrl-d at start if(dkstr_is_bool(p2)) { back = 1; if(dkstr_is_on(p2)) { fc->flags |= FSNMP_FLAG_CTRL_D_START; } else { fc->flags &= (~(FSNMP_FLAG_CTRL_D_START)); } } } break; case 4: { // ctrl-d at end if(dkstr_is_bool(p2)) { back = 1; if(dkstr_is_on(p2)) { fc->flags |= FSNMP_FLAG_CTRL_D_END; } else { fc->flags &= (~(FSNMP_FLAG_CTRL_D_END)); } } } break; case 5: { // shutdown data socket if(dkstr_is_bool(p2)) { if(dkstr_is_on(p2)) { fc->flags |= FSNMP_SHUTDOWN_TRANSFER; } else { fc->flags &= (~(FSNMP_SHUTDOWN_TRANSFER)); } fc->flags &= (~(FSNMP_TIMEOUT_TRANSFER)); back = 1; } else { double d; int ok; if(sscanf(p2, "%lf", &d) == 1) { back = 1; ok = 0; fc->flags |= (FSNMP_SHUTDOWN_TRANSFER | FSNMP_TIMEOUT_TRANSFER); d = fabs(d); (fc->shd).tv_sec = dkma_double_to_ul_ok(floor(d), &ok); (fc->shd).tv_usec = dkma_double_to_ul_ok(1000000.0 * (d - floor(d)), &ok); if(ok) { fc->exit_code = EXIT_ABORT; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[53]); } } } } break; case 6: { // minimum pagecount time int i; if(sscanf(p2, "%d", &i) == 1) { back = 1; if(i < 0) i = 0 - i; fc->mintimepc = i; } } break; case 7: { // shutdown accounting socket if(dkstr_is_bool(p2)) { if(dkstr_is_on(p2)) { fc->flags |= FSNMP_SHUTDOWN_PAGECOUNT; } else { fc->flags &= (~(FSNMP_SHUTDOWN_PAGECOUNT)); } fc->flags &= (~(FSNMP_TIMEOUT_PAGECOUNT)); back = 1; } else { double d; int ok; if(sscanf(p2, "%lf", &d) == 1) { back = 1; ok = 0; fc->flags |= (FSNMP_SHUTDOWN_PAGECOUNT | FSNMP_TIMEOUT_PAGECOUNT); d = fabs(d); (fc->shp).tv_sec = dkma_double_to_ul_ok(floor(d), &ok); (fc->shp).tv_usec = dkma_double_to_ul_ok(1000000.0 *(d - floor(d)), &ok); if(ok) { fc->exit_code = EXIT_ABORT; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[53]); } } } } break; case 8: { // data transfer port range char *ptr1, *ptr2; unsigned u1, u2; if(p2) { ptr1 = dkstr_start(p2, NULL); if(ptr1) { ptr2 = dkstr_next(ptr1, NULL); if(ptr2) { if(sscanf(ptr1, "%u", &u1) == 1) { if(sscanf(ptr2, "%u", &u2) == 1) { back = 1; fc->dt_pmin = u1; fc->dt_pmax = u2; } } } } } } break; case 9: { // accounting port range char *ptr1, *ptr2; unsigned u1, u2; if(p2) { ptr1 = dkstr_start(p2, NULL); if(ptr1) { ptr2 = dkstr_next(ptr1, NULL); if(ptr2) { if(sscanf(ptr1, "%u", &u1) == 1) { if(sscanf(ptr2, "%u", &u2) == 1) { back = 1; fc->ac_pmin = u1; fc->ac_pmax = u2; } } } } } } break; case 10: { // accounting destination char *mynv; mynv = dkstr_dup(p2); if(mynv) { if(fc->a_acctdest) { char *x; x = fc->a_acctdest; dk_delete(x); fc->a_acctdest = NULL; } fc->a_acctdest = mynv; back = 1; } else { fc->exit_code = EXIT_FAILED; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[52]); } } break; } } else { } } } $? "- add_config_line %d", back return back; } /** Get host address from text (IP4 address in dotted decimal notation). @param hn IP4 address as text. @return IP4 address in host representation on success, 0UL on error. */ unsigned long fsnmp_dotted_string_to_ip DK_P1(char *, hn) { unsigned long back = 0UL; unsigned long u1 = 0UL, u2 = 0UL, u3 = 0UL, u4 = 0UL, u = 0UL; int ende, state; char *ptr; $? "+ dotted_string_to_ip %s", TR_STR(hn) if(hn) { state = 0; u = u1 = u2 = u3 = u4 = 0UL; ptr = hn; ende = 0; while(!ende) { if(*ptr) { if(isdigit(*ptr)) { u = 0UL; switch(*ptr) { case '0': u = 0UL; break; case '1': u = 1UL; break; case '2': u = 2UL; break; case '3': u = 3UL; break; case '4': u = 4UL; break; case '5': u = 5UL; break; case '6': u = 6UL; break; case '7': u = 7UL; break; case '8': u = 8UL; break; case '9': u = 9UL; break; } switch(state) { case 0: u1 = 10UL * u1 + u; break; case 1: u2 = 10UL * u2 + u; break; case 2: u3 = 10UL * u3 + u; break; case 3: u4 = 10UL * u4 + u; break; } } else { if(*ptr == '.') { state++; if(state >= 4) { ende = 1; } } } ptr++; } else { ende = 1; } } } u1 = u1 << 24; u1 = u1 & 0xFF000000UL; u2 = u2 << 16; u2 = u2 & 0x00FF0000UL; u3 = u3 << 8; u3 = u3 & 0x0000FF00UL; u4 = u4 & 0x000000FFUL; back = u1 | u2 | u3 | u4; $? "- dotted_string_to_ip %lu.%lu.%lu.%lu", u1, u2, u3, u4 return back; } /** Look up host name. @param hn Host name to search. @return IP4 address on success, 0UL on error. */ unsigned long fsnmp_lookup_host DK_P1(char *,hn) { unsigned long back = 0UL, *ulptr; struct hostent *he; char **xptr; $? "+ lookup_host" he = gethostbyname(hn); if(he) { if(he->h_addrtype == AF_INET) { if(he->h_length == 4) { if(he->h_addr_list) { xptr = he->h_addr_list; ulptr = (unsigned long *)(*xptr); if(ulptr) { back = *ulptr; $? ". host found" } } } } } $? "- lookup_host" return back; } /** Check host name and port. @param fc Fsnmp job. */ static void check_host_name_and_port DK_P1(FC *,fc) { char *p1; unsigned u; unsigned long ipaddr; if(!((fc->flags) & FSNMP_FLAG_STDOUT)) { if(fc->a_hostport) { p1 = strchr(fc->a_hostport, '%'); if(p1) { fc->hostname = fc->a_hostport; *(p1++) = '\0'; if(sscanf(p1, "%u", &u) == 1) { fc->portnumber = u; p1 = dkstr_start(fc->a_hostport, NULL); if(p1) { dkstr_chomp(p1, NULL); ipaddr = fsnmp_dotted_string_to_ip(fc->hostname); if(ipaddr) { ipaddr = htonl(ipaddr); } else { ipaddr = fsnmp_lookup_host(fc->hostname); } if(ipaddr) { (fc->sain).sin_family = AF_INET; (fc->sain).sin_port = htons(fc->portnumber); (fc->sain).sin_addr.s_addr = ipaddr; } else { fc->exit_code = EXIT_ABORT; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[54], fc->hostname); } } else { fc->exit_code = EXIT_ABORT; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[55]); } } else { fc->exit_code = EXIT_ABORT; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[56], p1); } } else { fc->exit_code = EXIT_ABORT; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[57]); } } else { fc->exit_code = EXIT_ABORT; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[58]); } } } /** Read configuration file. @param fc Fsnmp job. */ void fsnmpcmd_read_config_file DK_P1(FC *,fc) { char *pn, *fn, *p1, *p2; char buffer[4096]; FILE *fipo; int state; unsigned long lineno = 0UL; $? "+ fsnmpcmd_read_config_file" fn = default_config_file; pn = fsnmpcmd_get_argv(fc, 'P'); if(!pn) { pn = fsnmpcmd_get_argv(fc, 'Q'); } if(pn) { fsnmplog(fc, PRIO_INFO, fsnmp_kw[5], fn, pn); fipo = dksf_fopen(fn, "r"); if(fipo) { state = 1; while(fgets(buffer, sizeof(buffer), fipo) && fsnmp_cc(fc)) { lineno++; p1 = dkstr_start(buffer, NULL); if(p1) { dkstr_delcomm(buffer, '#'); p1 = dkstr_start(p1, NULL); if(p1) { dkstr_chomp(p1, NULL); if(*p1 == '[') { state = 0; p2 = dkstr_chr(p1, ']'); if(p2) { *p2 = '\0'; p1++; if(strcmp(p1, pn) == 0) { state = 1; } } } else { if(state) { if(!add_config_line(fc, p1)) { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[8], fn, lineno); } } } } } } fclose(fipo); fipo = NULL; check_host_name_and_port(fc); } else { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[6]); fc->exit_code = EXIT_ABORT; } } else { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[7]); fc->exit_code = EXIT_ABORT; } $? "- fsnmpcmd_read_config_file" }