/* * Copyright (c) 2013-2016 Wind River Systems, Inc. * * SPDX-License-Identifier: Apache-2.0 * */ /** * @file * Wind River CGTS Platform Maintenance ... * * /var/run/.pid * * Also upon request can create a health check info file. * * /var/run/.info * */ #include /* for .. system */ #include /* for .. close and usleep */ #include #include #include #include #include #include #include using namespace std; #include "daemon_common.h" #include "daemon_option.h" #include "nodeBase.h" /* GNU Extension * program_invocation_name contains the name that was used to invoke the * calling program. This is the same as the value of argv[0] in main(), * with the difference that the scope of program_invocation_name is global. * program_invocation_short_name contains the basename component of name * that was used to invoke the calling program. That is, it is the same * value as program_invocation_name, with all text up to and including * the final slash (/), if any, removed. * These variables are automatically initialized by the glibc run-time * startup code. * * The daemon name info */ extern char *program_invocation_name; extern char *program_invocation_short_name; static char pid_filename [MAX_FILENAME_LEN] ; static char hc_filename [MAX_FILENAME_LEN] ; void daemon_files_fini ( void ) { close_syslog(); } void daemon_health_test ( void ) { char file_str[2000] ; sprintf ( &file_str[0], "I'm healthy: Here is my config ...\n%s\n", daemon_stream_info ()); daemon_healthcheck (&file_str[0]); } bool daemon_is_file_present ( const char * filename ) { struct stat p ; memset ( &p, 0 , sizeof(struct stat)); stat ( filename, &p ) ; if ((p.st_ino != 0 ) && (p.st_dev != 0)) return (true); else return (false); } void daemon_healthcheck ( const char * sig ) { FILE * hc_file_stream ; snprintf ( &hc_filename[0], MAX_FILENAME_LEN, "/var/run/%s.info", program_invocation_short_name ); /* remove the old file */ unlink (hc_filename); /* Create daemon healthcheck file */ hc_file_stream = fopen (hc_filename, "w" ) ; if ( hc_file_stream == NULL ) { wlog("Failed to open %s\n", hc_filename); } else if ( !fprintf (hc_file_stream,"%s\n", sig )) { wlog ("Failed to write to %s\n", hc_filename); } if ( hc_file_stream ) { fflush (hc_file_stream); fclose (hc_file_stream); } } #define BUFFER 1024 int daemon_log_value ( const char * filename , const char * str, int val ) { FILE * file_stream = fopen (filename, "a" ) ; if ( file_stream != NULL ) { fprintf ( file_stream,"%s %d\n", str, val ); fflush (file_stream); fclose (file_stream); return (PASS); } return (FAIL_FILE_OPEN); } int daemon_log_value ( const char * filename , int val ) { FILE * file_stream = fopen (filename, "w" ) ; if ( file_stream != NULL ) { fprintf ( file_stream,"%d\n", val ); fflush (file_stream); fclose (file_stream); return (PASS); } return (FAIL_FILE_OPEN); } int daemon_log ( const char * filename , const char * str ) { FILE * file_stream = fopen (filename, "a" ) ; if ( file_stream != NULL ) { fprintf ( file_stream,"%s\n", str ); fflush (file_stream); fclose (file_stream); return (PASS); } return (FAIL_FILE_OPEN); } /* reads the first line of a file and if it contains a string * that represents an integer value then return it */ int daemon_get_file_int ( const char * filename ) { int value = 0 ; FILE * __stream = fopen ( filename, "r" ); if ( __stream != NULL ) { int rc ; char buffer [MAX_CHARS_IN_INT]; memset(buffer, 0 , MAX_CHARS_IN_INT); if ( fgets (buffer,MAX_CHARS_IN_INT, __stream) != NULL ) { rc = sscanf ( &buffer[0], "%d", &value ); if ( rc >= 1 ) { dlog ("%s contains number %d\n", filename, value ); } else { wlog ("failed to sscanf integer from file:%s\n", filename ); } } else { wlog ("failed to read integer from file:%s\n", filename ); } fclose(__stream); } else { wlog ("failed to open file:%s\n", filename ); } return ( value ); } /* reads the first line of a file and returns it as a string */ string daemon_get_file_str ( const char * filename ) { string value = "null" ; FILE * __stream = fopen ( filename, "r" ); if ( __stream != NULL ) { int rc ; char buffer [MAX_CHARS_ON_LINE]; char data [MAX_CHARS_ON_LINE]; memset(buffer, 0 , MAX_CHARS_ON_LINE); memset(data, 0 , MAX_CHARS_ON_LINE); if ( fgets (buffer,MAX_CHARS_ON_LINE, __stream) != NULL ) { rc = sscanf ( &buffer[0], "%s", &data[0] ); if ( rc >= 1 ) { value = data ; dlog ("%s contains '%s'\n", filename, value.c_str()); } else { wlog ("failed to sscanf string from file:%s\n", filename ); } } else { wlog ("failed to read string from file:%s\n", filename ); } fclose(__stream); } else { wlog ("failed to open file:%s\n", filename ); } return ( value ); } /* Stay here till we get the data we need */ /* Warning: Don't enable logging here */ string daemon_nodetype ( void ) { for ( ; ; ) { char buffer[BUFFER]; int line = 0 ; string nodetype = "unknown" ; char nodetype_str[BUFFER]; memset ( &nodetype_str[0], 0 , BUFFER ); /* open the configuration file */ FILE * cfg_file_stream = fopen ( PLATFORM_CONF_FILE, "rb" ); if ( cfg_file_stream != NULL ) { int rc ; while ( fgets (buffer, BUFFER, cfg_file_stream) != NULL ) { char* s = strstr ( buffer, "subfunction"); if(s!=NULL) { rc = sscanf ( &buffer[0], "subfunction=%1023s", &nodetype_str[0] ); if ( rc == 1 ) { nodetype = nodetype_str ; fclose(cfg_file_stream); return ( nodetype ) ; } } line++ ; } /* Close the file */ fclose(cfg_file_stream); } usleep (5000000); } return ( "" ); } /* Read the integer value of rmem_max from /proc/sys/net/core/rmem_max */ int daemon_get_rmem_max ( void ) { #define RMEM_MAX_VALUE_SIZE (64) #define RMEM_MAX_FILENAME ((const char *)("/proc/sys/net/core/rmem_max")) int value = 0 ; FILE * __stream = fopen ( RMEM_MAX_FILENAME, "r" ); if ( __stream != NULL ) { int rc ; char buffer [RMEM_MAX_VALUE_SIZE]; memset(buffer, 0 , RMEM_MAX_VALUE_SIZE); if ( fgets (buffer,RMEM_MAX_VALUE_SIZE, __stream) != NULL ) { rc = sscanf ( &buffer[0], "%d", &value ); if ( rc == 1 ) { dlog1 ("mem_max:%d\n", value ); } } fclose(__stream); } return ( value ); } /* * Read the platform.conf file looking for the management_interface label. * If found return that value as string ; otherwise return empty string. * * const char infra_mgmt_label [] = {"management_interface"} ; * * Stay here till we get the data we need * Warning: Don't enable logging here * **/ string daemon_mgmnt_iface ( void ) { for ( ; ; ) { char iface_str[BUFFER]; char buffer [BUFFER]; int line = 0 ; string iface = "" ; memset ( iface_str, 0 , BUFFER ); FILE * cfg_file_stream = fopen ( PLATFORM_CONF_FILE, "r" ); if ( cfg_file_stream != NULL ) { int rc ; while ( fgets (buffer, BUFFER, cfg_file_stream) != NULL ) { char* s = strstr ( buffer, "management_interface"); if(s!=NULL) { rc = sscanf ( &buffer[0], "management_interface=%1023s", &iface_str[0] ); if ( rc == 1 ) { iface = iface_str ; fclose(cfg_file_stream); // ilog("Mgmnt iface : %s\n", iface.c_str() ); return ( iface ) ; } } line++ ; } /* Close the file */ fclose(cfg_file_stream); } usleep (5000000); } ilog("Mgmnt iface : none\n"); return ( "" ); } /**************************************************************************** * * Name : daemon_system_type * * Purpose : Learn the system type * * Description: Read the platform.conf file looking for system type. * If type is not Standard then refine the system type to * one of the AIO variations. * *****************************************************************************/ #define SYSTEM_TYPE_PREFIX ((const char *)("System Type :")) system_type_enum daemon_system_type ( void ) { char buffer [BUFFER]; system_type_enum system_type = SYSTEM_TYPE__CPE_MODE__SIMPLEX ; FILE * cfg_file_stream = fopen ( PLATFORM_CONF_FILE, "r" ); if ( cfg_file_stream != NULL ) { MEMSET_ZERO(buffer); while ( fgets (buffer, BUFFER, cfg_file_stream) != NULL ) { if ( strstr ( buffer, "system_type") != NULL ) { char type_str[BUFFER]; MEMSET_ZERO(type_str); if ( sscanf ( &buffer[0], "system_type=%1023s", &type_str[0] ) == 1 ) { string type_string = type_str ; if ( !type_string.empty() && ( type_string == "Standard")) { system_type = SYSTEM_TYPE__NORMAL ; } break ; } } MEMSET_ZERO(buffer); } } if ( cfg_file_stream ) { /* Close the file */ fclose(cfg_file_stream); cfg_file_stream = NULL ; } /* If system_type is updated to NORMAL then we are done. * Otherwise lets see what kind of AIO system mode we are running in. */ if ( system_type != SYSTEM_TYPE__NORMAL ) { cfg_file_stream = fopen ( PLATFORM_CONF_FILE, "r" ); if ( cfg_file_stream != NULL ) { MEMSET_ZERO(buffer); while ( fgets (buffer, BUFFER, cfg_file_stream) != NULL ) { if ( strstr ( buffer, "system_mode") != NULL ) { char mode_str[BUFFER]; MEMSET_ZERO(mode_str); if ( sscanf ( &buffer[0], "system_mode=%1023s", &mode_str[0] ) == 1 ) { string mode = mode_str ; if ( !mode.empty() ) { if ( mode.compare("duplex") == 0 ) system_type = SYSTEM_TYPE__CPE_MODE__DUPLEX ; else if ( mode.compare("duplex-direct") == 0 ) system_type = SYSTEM_TYPE__CPE_MODE__DUPLEX_DIRECT ; else if ( mode.compare("simplex") == 0 ) system_type = SYSTEM_TYPE__CPE_MODE__SIMPLEX ; else { elog ("%s All-In-One system type ; mode unknown\n", SYSTEM_TYPE_PREFIX ); wlog ("... %s\n", buffer ); } } else { elog ("%s All-In-One system type ; mode empty\n", SYSTEM_TYPE_PREFIX ); wlog ("... %s\n", buffer ); } } break ; } MEMSET_ZERO(buffer); } } } if ( cfg_file_stream ) { /* Close the file */ fclose(cfg_file_stream); } switch ( system_type ) { case SYSTEM_TYPE__NORMAL: { ilog("%s Standard System\n", SYSTEM_TYPE_PREFIX); break ; } case SYSTEM_TYPE__CPE_MODE__DUPLEX_DIRECT: { ilog ("%s All-in-one Duplex Direct Connect\n", SYSTEM_TYPE_PREFIX ); break ; } case SYSTEM_TYPE__CPE_MODE__DUPLEX: { ilog ("%s All-in-one Duplex\n", SYSTEM_TYPE_PREFIX ); break ; } case SYSTEM_TYPE__CPE_MODE__SIMPLEX: default: { ilog ("%s All-in-one Simplex \n", SYSTEM_TYPE_PREFIX ); system_type = SYSTEM_TYPE__CPE_MODE__SIMPLEX ; break ; } } return ( system_type ); } /* ******************************************************************** * * Name: daemon_infra_iface * * Description: Read the platform.conf file looking for the infra * interface label. If found return that value as string ; * otherwise return empty string. **/ string daemon_infra_iface ( void ) { char buffer [BUFFER]; int line = 0 ; string iface = "" ; FILE * cfg_file_stream = fopen ( PLATFORM_CONF_FILE, "r" ); if ( cfg_file_stream != NULL ) { int rc ; while ( fgets (buffer, BUFFER, cfg_file_stream) != NULL ) { char* s = strstr ( buffer, "infrastructure_interface" ); if(s!=NULL) { char iface_str[BUFFER]; memset ( iface_str, 0 , BUFFER ); rc = sscanf ( &buffer[0], "infrastructure_interface=%1023s", &iface_str[0] ); if ( rc == 1 ) { iface = iface_str ; fclose(cfg_file_stream); // ilog("Infra iface : %s\n", iface.c_str() ); return ( iface ) ; } } line++ ; } /* Close the file */ fclose(cfg_file_stream); } dlog("Infra iface : none\n"); return ( "" ); } /* ********************************************************************************* * * Name : daemon_sw_version * * Description: read the /etc/build.info file and extract the SW_VERSION value. * * Note: If the value is found surrounded by quotes then they are stripped. * Note: if the lable is not found then a empty string is returned. * * @return SW_VERSION string value * * Here is the head of the file with that label. * * root@controller-0:/opt# cat /etc/build.info * * SW_VERSION="14.08" <----------------------- * BUILD_TARGET="Unknown" * BUILD_TYPE="Informal" * BUILD_ID="n/a" * ************************************************************************************/ string daemon_sw_version ( void ) { char version_str[BUFFER]; string version = "" ; int line = 0 ; const char build_info_filename[] = { "/etc/build.info" }; FILE * fp = fopen (&build_info_filename[0], "r" ) ; if ( fp ) { int rc = 0 ; char buffer[BUFFER]; while ( fgets (buffer, BUFFER, fp ) != NULL ) { memset ( version_str, 0 , BUFFER ); char* s = strstr ( buffer, "SW_VERSION"); if(s!=NULL) { rc = sscanf ( &buffer[0], "SW_VERSION=%1023s", &version_str[0] ); if ( rc == 1 ) { /* Chop off the surrounding quotes if they exist */ if ( version_str[0] == '"' ) { /* "12.34" -> 12.34 */ int len = strlen (version_str); string temp = version_str ; version = temp.substr ( 1, len-2 ) ; } else { version = version_str ; } fclose(fp); return ( version ) ; } } line++ ; } /* Close the file */ fclose(fp); } return (""); } /**************************************************************************** * * Name : daemon_bmc_hosts_file * * Description: Insert the software version into the path to the bmc hosts * file and return the file path as a string. * ****************************************************************************/ string daemon_bmc_hosts_dir ( void ) { string fn = "/opt/platform/config/" ; string version = daemon_sw_version(); if ( !version.empty() ) { version.append("/"); } fn.append(version); return(fn); } string daemon_bmc_hosts_file ( void ) { string fn = "/opt/platform/config/" ; string version = daemon_sw_version(); if ( !version.empty() ) { version.append("/"); } fn.append(version); fn.append(BM_DNSMASQ_FILENAME); return(fn); } /**************************************************************************** * * Name : daemon_get_iface_master * * Description: Get the master interface name for the supplied * physical interface. * ****************************************************************************/ char master_interface [BUFFER] ; char * daemon_get_iface_master ( char * iface_slave_ptr ) { FILE * file_ptr ; char buffer[BUFFER]; string iface_master = "/sys/class/net/" ; iface_master.append (iface_slave_ptr) ; iface_master.append ("/master/uevent"); /* Create daemon healthcheck file */ file_ptr = fopen ( iface_master.data(), "r" ) ; if ( file_ptr != NULL ) { while ( fgets ( buffer, BUFFER, file_ptr ) != NULL ) { char* s = strstr ( buffer, "INTERFACE"); if ( s != NULL ) { memset ( &master_interface[0], 0, BUFFER ); int rc = sscanf ( &buffer[0], "INTERFACE=%1023s", &master_interface[0] ); if ( rc == 1 ) { fclose(file_ptr); return ( &master_interface[0] ); } } } fclose(file_ptr); } return ( iface_slave_ptr ); } string daemon_md5sum_file ( const char * file ) { struct stat p ; string md5sum = "" ; memset ( &p, 0 , sizeof(struct stat)); stat ( file, &p ) ; if ((p.st_ino != 0 ) && (p.st_dev != 0)) { /* add 256 bytes to the buffer just in case there are * additions to the file by the time we start reading it */ int len = p.st_size+0x100 ; char * buf_ptr = (char*)malloc(len) ; char * buf_ptr_save = buf_ptr ; if ( buf_ptr ) { dlog ("%s is %ld bytes\n", file, p.st_size ); /* Open and read the file data */ FILE * file_ptr = fopen ( file, "r" ) ; if ( file_ptr != NULL ) { size_t l ; unsigned char digest[MD5_DIGEST_LENGTH]; char md5str [MD5_STRING_LENGTH]; memset ( &digest, 0, MD5_DIGEST_LENGTH); memset ( &md5str, 0, MD5_STRING_LENGTH); while ( fgets ( (char*)buf_ptr, BUFFER, file_ptr ) != NULL ) { l = strnlen ( buf_ptr_save, len ); buf_ptr = buf_ptr_save+l ; } MD5 ( (unsigned char*)buf_ptr_save, strlen(buf_ptr_save), (unsigned char*)&digest); for(int i = 0; i < MD5_DIGEST_LENGTH; i++) sprintf(&md5str[i*2], "%02x", (unsigned int)digest[i]); dlog ("md5 digest: %s\n", md5str ); dlog3 ("file: %s\n", buf_ptr_save ); fclose(file_ptr); md5sum = md5str ; } else { elog ("%s file open failed\n", file ); } free (buf_ptr_save); } else { elog ("failed to allocate buffer memory for %s file md5sum calc\n", file ); } } else { wlog ("%s file not present\n", file ); } return ( md5sum ); } // generate a md5sum signature for the Shadow entry // returns the password hash and aging in the shadowinfo string get_shadow_signature ( char * shadowfile , const char * username, char *shadowinfo, size_t infolen) { char buffer[BUFFER]; /* Open the specified file file Create daemon healthcheck file */ FILE * file_ptr = fopen ( shadowfile, "r" ) ; if ( file_ptr != NULL ) { /* Clear the buffer - start fresh */ memset ( &buffer, 0, BUFFER ); while ( fgets ( buffer, BUFFER, file_ptr ) != NULL ) { char* s = strstr ( buffer, username); if ( s != NULL ) { int result; char user[BUFFER], password[BUFFER], aging[BUFFER]; unsigned char digest[MD5_DIGEST_LENGTH]; char md5str [MD5_STRING_LENGTH]; /* Fields are separated by ':'. The first field is the * user. We need to only isolate the password and aging * fields since these are the only ones that'd be * propagated across host nodes. By specifically tracking * these we prevent config-out-of-date alarms for other fields */ /* * The following line should be changed to add width limits: * (However, not changing it yet because of risk.) * result = sscanf(buffer, "%1023[^:]:%1023[^:]:%*[^:]:%*[^:]:%1023[^:]", */ result = sscanf(buffer, "%[^:]:%[^:]:%*[^:]:%*[^:]:%[^:]", user, password, aging); if ( result != 3 || strcmp(user, username) != 0 ) { /* Sanity */ continue; } /* at max, both password[] and aging[] include BUFFER chars (BUFFER-1 * meaningful chars and one "\0" as tail). when they are combined with * ":" and put into shadowEntry by snprintf (..., "%s:%s", ...), * shadowEntry has 2 chars (":" + "\0") at least and BUFFER*2 chars at most: * BUFFER-1 chars copied from password * ":" * BUFFER-1 chars copied from aging, and * one tail "\0" */ char shadowEntry[BUFFER*2] = {0}; snprintf (shadowEntry, sizeof(shadowEntry), "%s:%s", password, aging); int ret = snprintf(shadowinfo, infolen, "%s", shadowEntry); if (ret >= (int)infolen) { fclose(file_ptr); elog("insufficient space in shadow buffer(%d) for %d bytes\n", (int)infolen, ret); return ( "" ); } memset ( &digest, 0, MD5_DIGEST_LENGTH ); memset ( &md5str, 0, MD5_STRING_LENGTH ); MD5 ((unsigned char*) shadowEntry, strlen(shadowEntry), (unsigned char*)&digest); for(int i = 0; i < MD5_DIGEST_LENGTH; i++) sprintf(&md5str[i*2], "%02x", (unsigned int)digest[i]); dlog ("user value: %s\n", shadowEntry); dlog ("md5 digest: %s\n", md5str ); fclose(file_ptr); string md5sum = md5str ; return ( md5sum ); } else { /* Clear the buffer - start fresh */ memset ( &buffer, 0, BUFFER ); } } fclose(file_ptr); } return ( "" ); } /* Introduced for process monitor daemon to allow quiet * close of the log file before a process respawn */ void daemon_logfile_close ( void ) { return ; } /* Introduced for process monitor daemon to allow quiet * re-open of the log file after a respawn of a process */ void daemon_logfile_open ( void ) { return ; } /**************************************************************************** * * Name : daemon_remove_file * * Description: Remove the specified file if it exists. * *****************************************************************************/ void daemon_remove_file ( const char * filename ) { if ( daemon_is_file_present ( filename )) { if ( remove(filename) ) { elog ("failed to remove file '%s' ; (%d:%m)\n", filename, errno); } else { if ( daemon_is_file_present ( filename ) ) { elog ("failed to remove file '%s' ; still present\n", filename ); } else { dlog3 ("removed %s\n", filename ); } } } else { dlog3 ("no remove required ; file '%s' not present\n", filename ); } } /**************************************************************************** * * Name : daemon_make_dir * * Description: Create the specified full path directory. * *****************************************************************************/ void daemon_make_dir ( const char * dir ) { struct stat st ; MEMSET_ZERO(st); if (stat(dir, &st) == -1) { if ( mkdir (dir, 0755) != 0 ) { elog ("failed to create directory '%s' ; (%d:%m)\n", dir, errno ); } } } /**************************************************************************** * * Name : daemon_rename_file * * Description: Rename the specified file from old to new name. * * Warnings : If the target rename filename exists then it is removed first. * *****************************************************************************/ void daemon_rename_file ( const char * path, const char * old_filename, const char * new_filename ) { if ( path ) { string _old = path ; _old.append("/"); _old.append(old_filename); if ( daemon_is_file_present ( _old.data() ) == true ) { string _new = path ; _new.append("/"); _new.append(new_filename); if ( daemon_is_file_present ( _new.data() ) == true ) { dlog ("removing '%s' prior to rename\n", _new.c_str()); daemon_remove_file ( _new.data() ); } if ( rename ( _old.data(), _new.data()) == 0) { dlog ("file rename : %s -> %s\n", old_filename, new_filename); } else { elog ( "Error renaming %s -> %s (%d:%m)\n", old_filename, new_filename, errno ); } } else { elog ("rename failed ; '%s' not found\n", _old.c_str()); } } } void daemon_remove_pidfile ( void ) { const char* cmd_str = "rm -f "; size_t cmd_len = sizeof(cmd_str); char str [MAX_FILENAME_LEN+cmd_len] ; snprintf (str, sizeof(str), "rm -f %s", pid_filename); int rc = system (str); if ( rc ) { wlog ("system command to remove %s returned %d\n", &pid_filename[0], rc ); } } int daemon_create_pidfile ( void ) { FILE * pid_file_stream = (FILE *)(NULL); snprintf ( &pid_filename[0], MAX_FILENAME_LEN, "/var/run/%s.pid", program_invocation_short_name ); /* Create PID file */ pid_t mypid = getpid(); /* Check for another instance running by trying to open in read only mode. * If it opens then there "may" be another process running. * If it opens then read the pid and see if that pID exists. * If it does then this is a duplicate process so exit. */ pid_file_stream = fopen (&pid_filename[0], "r" ) ; if ( pid_file_stream ) { int rc = 0 ; pid_t pid = 0 ; char buffer[BUFFER]; if ( fgets ( buffer, BUFFER, pid_file_stream) != NULL ) { rc = sscanf ( &buffer[0], "%d", &pid ); if ( rc == 1 ) { rc = kill ( pid, 0 ); if ( rc == 0 ) { syslog ( LOG_INFO, "Refusing to start duplicate process (pid:%d)\n", pid); fclose (pid_file_stream); daemon_files_fini (); exit (0); } } } } if ( pid_file_stream ) fclose (pid_file_stream); /* if we got here then we are ok to run */ pid_file_stream = fopen (pid_filename, "w" ) ; if ( pid_file_stream == NULL ) { syslog ( LOG_ERR, "Failed to open or create %s\n", pid_filename); return ( FAIL_PID_OPEN ); } else if (!fprintf (pid_file_stream,"%d", mypid)) { syslog ( LOG_ERR, "Failed to write pid file for %s\n", pid_filename ); fclose ( pid_file_stream ) ; return ( FAIL_FILE_WRITE ) ; } fflush (pid_file_stream); fclose (pid_file_stream); return (PASS); } string daemon_read_file ( const char * filename ) { string data = "" ; int lines = 0 ; if ( daemon_is_file_present ( filename ) == true ) { FILE * _stream = fopen ( filename, "r" ); if ( _stream ) { char buffer [BUFFER]; MEMSET_ZERO(buffer); while ( fgets (buffer, BUFFER, _stream) ) { data.append(buffer); if ( ++lines > 100 ) { wlog ("%s file to big ; aborting\n", filename ); break ; } MEMSET_ZERO(buffer); } fclose (_stream); } } return data ; } /*************************************************************************** * * Don't return from this call until the specified file exists * or the timeout is exceeded. In the timeout case a FAIL_TIMEOUT * is returned. * * Warning: Timeout timer is not yet implemented * ***************************************************************************/ int daemon_wait_for_file ( const char * filename, int timeout ) { UNUSED(timeout) ; if ( filename ) { int count = 0 ; for ( ; ; ) { daemon_signal_hdlr (); if ( daemon_is_file_present( filename )) return PASS ; sleep (MTC_SECS_2); wlog_throttled ( count, 60, "Waiting for %s\n", filename); } } return (FAIL_TIMEOUT); } int daemon_files_init ( void ) { /* Create PID file */ pid_t mypid = getpid(); ilog ("--- Daemon Start-Up --- pid:%d\n", mypid); daemon_init_fit (); return ( PASS ); } /***************************************************************************************** * * ####### ### ####### ##### # # ###### ###### ####### ###### ####### * # # # # # # # # # # # # # # # # * # # # # # # # # # # # # # # # * ##### # # ##### # # ###### ###### # # ###### # * # # # # # # # # # # # # # * # # # # # # # # # # # # # # * # ### # ##### ##### # # ####### # # # * *****************************************************************************************/ #ifdef WANT_FIT_TESTING static daemon_fit_type __fit_info ; #endif #ifdef WANT_HIT_THROTTLE int throttle_max ; int throttle_count ; #endif void daemon_print_fit ( void ) { #ifdef WANT_FIT_TESTING if ( __fit_info.code ) { if ( !daemon_get_cfg_ptr()->testmode ) { slog ("%s FIT Mode Not Enabled ; need to enable testmode\n", __fit_info.host.c_str()); daemon_init_fit ( ); } else if ( __fit_info.hits == 0 ) { daemon_init_fit (); } else { slog ("%s FIT Add:%d '%s' with '%s' %d times\n", __fit_info.host.empty() ? "any" : __fit_info.host.c_str(), __fit_info.code, __fit_info.name.empty() ? "n/a" : __fit_info.name.c_str(), __fit_info.data.empty() ? "n/a" : __fit_info.data.c_str(), __fit_info.hits); } } #endif } void daemon_hits_fit ( int hits ) { #ifdef WANT_FIT_TESTING __fit_info.hits += hits ; daemon_print_fit (); #else UNUSED(hits); #endif } void daemon_init_fit ( void ) { #ifdef WANT_FIT_TESTING ilog ("FIT Inactive\n"); __fit_info.code = 0 ; __fit_info.host.clear() ; __fit_info.name.clear() ; __fit_info.proc.clear() ; __fit_info.data.clear() ; __fit_info.hits = 0 ; /* Indicate that the fit is unloaded */ if ( daemon_is_file_present ( "/var/run/fit") ) daemon_log ( "/var/run/fit/fitdone", "done" ); #endif } /* called by processes that don't match the fit proc name */ void daemon_ignore_fit ( void ) { #ifdef WANT_FIT_TESTING __fit_info.code = 0 ; __fit_info.host.clear() ; __fit_info.name.clear() ; __fit_info.proc.clear() ; __fit_info.data.clear() ; __fit_info.hits = 0 ; #endif } void daemon_handle_hit ( void ) { #ifdef WANT_FIT_TESTING if ( __fit_info.code ) { --__fit_info.hits ; #ifdef WANT_HIT_THROTTLE ilog_throttled (throttle_count, throttle_max, "%s FIT Hit:%d '%s' with '%s' %d times remaining\n", __fit_info.host.empty() ? "any" : __fit_info.host.c_str(), __fit_info.code, __fit_info.name.empty() ? "n/a" : __fit_info.name.c_str(), __fit_info.data.empty() ? "n/a" : __fit_info.data.c_str(), __fit_info.hits); #else slog ("%s FIT Hit:%d '%s' with '%s' %d times remaining\n", __fit_info.host.empty() ? "any" : __fit_info.host.c_str(), __fit_info.code, __fit_info.name.empty() ? "n/a" : __fit_info.name.c_str(), __fit_info.data.empty() ? "n/a" : __fit_info.data.c_str(), __fit_info.hits); #endif if ( __fit_info.hits == 0 ) daemon_ignore_fit (); else daemon_log_value ( "/var/run/fit/fithits", "hits =", __fit_info.hits ); } #endif } /* Read the fit file if its present and load its fit info */ int daemon_load_fit ( void ) { #ifdef WANT_FIT_TESTING if ( __fit_info.code ) { return (PASS); } if ( daemon_is_file_present ( FIT__INIT_FILE ) == true ) { daemon_rename_file ( FIT__INIT_FILEPATH, FIT__INIT_FILENAME, FIT__INIT_FILENAME_RENAMED ); daemon_init_fit (); } if ( daemon_is_file_present ( FIT__INFO_FILE ) == false ) return (PASS); bool correct_process = false ; bool valid_code = false ; FILE * _stream = fopen ( FIT__INFO_FILE, "r" ); if ( _stream ) { char buffer [BUFFER]; memset (buffer, 0 , BUFFER ); while ( fgets (buffer, BUFFER, _stream) ) { if (( correct_process == false ) && ( strstr ( buffer, "proc=" ))) { char _str[BUFFER]; memset ( _str, 0 , BUFFER ); int chars = 0 ; if ( ( chars = sscanf ( &buffer[0], "proc=%1023s", &_str[0] ) ) == 1 ) { string proc = program_invocation_short_name ; if ( proc.compare(_str) == 0 ) { __fit_info.proc = proc ; correct_process = true ; } else { break ; } } else { daemon_ignore_fit (); // ilog ("%d [%s:%s]\n", chars, program_invocation_short_name , _str); } } else if ( strstr ( buffer, "code=" ) ) { if ( sscanf ( &buffer[0], "code=%d", &__fit_info.code ) == 1 ) { valid_code = true ; } } else if ( strstr ( buffer, "hits=" ) ) { if ( sscanf ( &buffer[0], "hits=%d", &__fit_info.hits ) == 1 ) { #ifdef WANT_HIT_THROTTLE throttle_count = 0 ; if ( __fit_info.hits > 10 ) throttle_max = 10 ; else throttle_max = 1 ; #else ; #endif } } else if ( strstr ( buffer, "host=" ) ) { char _str1[MAX_CHARS_HOSTNAME+1]; char _str2[MAX_CHARS_HOSTNAME+1]; char _str3[MAX_CHARS_HOSTNAME+1]; char _str4[MAX_CHARS_HOSTNAME+1]; memset ( _str1, 0 , MAX_CHARS_HOSTNAME+1 ); memset ( _str2, 0 , MAX_CHARS_HOSTNAME+1 ); memset ( _str3, 0 , MAX_CHARS_HOSTNAME+1 ); memset ( _str4, 0 , MAX_CHARS_HOSTNAME+1 ); int rc = sscanf ( &buffer[0], "host=%32s %32s %32s %32s", &_str1[0], &_str2[0], &_str3[0], &_str4[0] ); if ( rc ) { __fit_info.host = _str1 ; if ( rc > 1 ) { __fit_info.host.append(" "); __fit_info.host.append(_str2 ); } if ( rc > 2 ) { __fit_info.host.append(" "); __fit_info.host.append(_str3); } if ( rc > 3 ) { __fit_info.host.append(" "); __fit_info.host.append(_str4); } } } else if ( strstr ( buffer, "name=" ) ) { char _str1[60]; char _str2[60]; char _str3[60]; memset ( _str1, 0 , 60 ); memset ( _str2, 0 , 60 ); memset ( _str3, 0 , 60 ); int rc = sscanf ( &buffer[0], "name=%59s %59s %59s", &_str1[0], &_str2[0], &_str3[0] ); if ( rc ) { __fit_info.name = _str1 ; if ( rc > 1 ) { __fit_info.name.append(" "); __fit_info.name.append(_str2 ); } if ( rc > 2 ) { __fit_info.name.append(" "); __fit_info.name.append(_str3); } } } else if ( strstr ( buffer, "data=" ) ) { char _str[BUFFER]; memset ( _str, 0 , BUFFER ); if ( sscanf ( &buffer[0], "data=%1023s", &_str[0] ) == 1 ) { __fit_info.data = _str ; } } memset (buffer, 0 , BUFFER ); } /* end while */ fclose(_stream); } if ( !correct_process ) { return (PASS); } daemon_print_fit (); if ( !valid_code ) { elog ( "FIT file parse error (%d)\n", valid_code ); daemon_init_fit (); } else { daemon_log_value ( "/var/run/fit/fithits", "hits =", __fit_info.hits ); daemon_remove_file ( "/var/run/fit/fitdone" ); } daemon_rename_file ( FIT__INFO_FILEPATH, FIT__INFO_FILENAME, FIT__INFO_FILENAME_RENAMED ); #endif return (PASS); } /* Check for fault insertion */ bool daemon_want_fit ( int code ) { #ifdef WANT_FIT_TESTING if ( __fit_info.hits > 0) { if ( daemon_get_cfg_ptr()->testmode ) { if ( __fit_info.code ) { //ilog ("hits %d code %d:%d\n", __fit_info.hits, __fit_info.code, code); if ( __fit_info.code == code ) { daemon_handle_hit (); return (true) ; } } } } #else UNUSED(code); #endif return (false); } bool daemon_want_fit ( int code, string host ) { #ifdef WANT_FIT_TESTING if ( __fit_info.hits > 0 ) { if ( daemon_get_cfg_ptr()->testmode ) { if ( __fit_info.code == code ) { //ilog ("%s:%s hits %d code %d:%d\n", host.c_str(), __fit_info.host.c_str() , __fit_info.hits, __fit_info.code, code); if (( __fit_info.code == code ) && ( __fit_info.host.find(host) != std::string::npos )) { daemon_handle_hit (); return (true) ; } } } } #else UNUSED(code); UNUSED(host); #endif return (false); } bool daemon_want_fit ( int code, string host, string name ) { #ifdef WANT_FIT_TESTING if ( __fit_info.hits > 0 ) { if ( daemon_get_cfg_ptr()->testmode ) { if ( __fit_info.code == code ) { //ilog ("%s:%s <%s:%s> hits %d code %d:%d\n", host.c_str(), __fit_info.host.c_str(), name.c_str(), __fit_info.name.c_str(), __fit_info.hits, __fit_info.code, code); if (( __fit_info.code == code ) && (( __fit_info.host.find(host) != std::string::npos ) || ( host == "any" )) && ( __fit_info.name.find(name) != std::string::npos )) { daemon_handle_hit (); return (true) ; } } } } #else UNUSED(code); UNUSED(host); UNUSED(name); #endif return (false); } bool daemon_want_fit ( int code, string host, string name, string & data ) { #ifdef WANT_FIT_TESTING if ( __fit_info.hits > 0 ) { if ( daemon_get_cfg_ptr()->testmode ) { if ( __fit_info.code == code ) { //ilog ("%s:%s <%s:%s> hits %d code %d:%d\n", host.c_str(), __fit_info.host.c_str(), name.c_str(), __fit_info.name.c_str(), __fit_info.hits, __fit_info.code, code); if (( __fit_info.code == code ) && (( __fit_info.host.find(host) != std::string::npos ) || ( host == "any" )) && ( __fit_info.name.find(name) != std::string::npos)) { data = __fit_info.data ; daemon_handle_hit (); return (true) ; } } } } #else UNUSED(code); UNUSED(host); UNUSED(name); UNUSED(data); #endif return (false); }