/*----------------------------------------------------------------------------+ | | | HEYU Configuration | | Copyright 2002,2003,2004-2007 Charles W. Sullivan | | All Rights Reserved | | | | | | This software is licensed free of charge for non-commercial distribution | | and for personal and internal business use only. Inclusion of this | | software or any part thereof in a commercial product is prohibited | | without the prior written permission of the author. You may copy, use, | | and distribute this software subject to the following restrictions: | | | | 1) You may not charge money for it. | | 2) You may not remove or alter this license, copyright notice, or the | | included disclaimers. | | 3) You may not claim you wrote it. | | 4) If you make improvements (or other changes), you are requested | | to send them to the Heyu maintainer so there's a focal point for | | distributing improved versions. | | | | As used herein, HEYU is a trademark of Daniel B. Suthers. | | X10, CM11A, and ActiveHome are trademarks of X-10 (USA) Inc. | | The author is not affiliated with either entity. | | | | Charles W. Sullivan | | Co-author and Maintainer | | Greensboro, North Carolina | | Email ID: cwsulliv01 | | Email domain: -at- heyu -dot- org | | | | Disclaimers: | | THERE IS NO ASSURANCE THAT THIS SOFTWARE IS FREE OF DEFECTS AND IT MUST | | NOT BE USED IN ANY SITUATION WHERE THERE IS ANY CHANCE THAT ITS | | PERFORMANCE OR FAILURE TO PERFORM AS EXPECTED COULD RESULT IN LOSS OF | | LIFE, INJURY TO PERSONS OR PROPERTY, FINANCIAL LOSS, OR LEGAL LIABILITY. | | | | TO THE EXTENT ALLOWED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED "AS IS",| | WITH NO EXPRESS OR IMPLIED WARRANTY, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.| | | | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL THE AUTHOR BE LIABLE | | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL | | DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SOFTWARE EVEN IF | | THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | | | +----------------------------------------------------------------------------*/ #include #include #include #include #include #include #if defined(SYSV) || defined(FREEBSD) || defined(OPENBSD) #include #else #include #endif #include #include #include "x10.h" #include "process.h" #include "sun.h" extern int line_no, verbose; extern int i_am_relay, i_am_aux, i_am_state; extern char heyu_path[PATH_LEN + 1]; extern char alt_path[PATH_LEN + 1]; extern char heyu_script[PATH_LEN + 1]; extern long std_tzone; extern char default_housecode; extern char heyu_config[PATH_LEN + 1]; char statefile[PATH_LEN + 1]; char enginelockfile[PATH_LEN + 1]; char auxfile[PATH_LEN + 1]; unsigned int transceive_list[16]; CONFIG config; CONFIG *configp = NULL; enum { OldAlias, Alias, UserSyn, Launcher, Script, Scene, RFBursts, /* Directives with multiple instances allowed */ /* must be before 'Separator'. */ Separator, Tty, TtyAux, HouseCode, ForceAddr, NewFormat, ScheduleFile, MaxPParms, RcsTemp, StatusTimeout, SpfTimeout, DispExpMac, AckHails, Mode, ProgramDays, AsIfDate, AsIfTime, CombineEvents, CompressMacros, FebKluge, Latitude, Longitude, DawnOption, DuskOption, DawnSubstitute, DuskSubstitute, MinDawn, MaxDawn, MinDusk, MaxDusk, CheckFiles, ReportPath, ReplDelay, Ignored, ResvTimers, TrigTag, XrefApp, MacTerm, AutoChain, ResOverlap, ModuleTypes, LaunchMode, FunctionMode, LaunchSrc, DefaultModule, ScriptShell, ScriptMode, LogDir, IsDarkOffset, EnvAliasPrefix, DevType, SunMode, Fix5A, AutoFetch, PfailUpdate, BitDelay, DefRFBursts, BurstSpacing, TimerTweak, RFPostDelay, RFFarbDelay, RFFarwDelay, DispRFX, LoopCount, RestrictDims, StateFmt, /* StateDir, */PfailScript, CM11PostDelay, StartEngine, IgnoreSilent, NoSwitch, RespoolPerms, SpoolMax, CheckRILine, RIdisable, SendRetries, ScriptCtrl, Transceive, TransMissp, RFForward, TransDim, StateCtrl, RFFuncMask, RingCtrl, AuxRepcounts, HideUnchg, DispRFNoise, ArmMaxDelay, DispRawRF, ArmLogic, InactiveTimeout, HeyuUmask, AutoWait, FullBright, EnginePoll }; #define CSTP 1 /* Minimal config for stop command */ #define CCAL 2 /* Minimal config for syscheck command */ #define CMLT 4 /* Multiple instances allowed */ #define COVR 8 /* Allow override in sched file */ #define CHLP 16 /* Minimal config for help command */ static struct conf { char *name; int mintoken; int maxtoken; unsigned char isparsed; unsigned char override; /* Allow override in sched file */ unsigned int flags; unsigned char switchval; } command[] = { {"(_alias_)", 3, 3, 0, 0, CMLT, OldAlias }, {"ALIAS", 3,16, 0, 0, CMLT, Alias }, {"LAUNCHER", 2,-1, 0, 0, CMLT, Launcher}, {"SCRIPT", 2,-1, 0, 0, CMLT, Script }, {"USERSYN", 2,-1, 0, 0, CMLT, UserSyn }, {"SCENE", 2,-1, 0, 0, CMLT, Scene }, {"RF_BURSTS", 3, 3, 0, 0, CMLT, RFBursts}, /* Multiple instances OK for switchval above */ {"TTY", 2, 4, 0, 0, CSTP, Tty }, {"TTY_AUX", 3, 3, 0, 0, CSTP, TtyAux}, {"HOUSECODE", 2, 2, 0, 0, 0, HouseCode }, {"FORCE_ADDR", 2, 2, 0, 0, 0, ForceAddr }, {"NEWFORMAT", 1, 1, 0, 0, 0, Ignored }, {"MACROXREF", 2, 2, 0, 0, 0, Ignored }, {"SCHEDULE_FILE", 2, 2, 0, 0, 0, ScheduleFile }, {"MAX_PPARMS", 2, 2, 0, 0, 0, MaxPParms }, {"STATUS_TIMEOUT", 2, 2, 0, 0, 0, StatusTimeout }, {"SPF_TIMEOUT", 2, 2, 0, 0, 0, SpfTimeout }, {"RCS_TEMPERATURE", 2, 2, 0, 0, 0, RcsTemp }, {"RCS_DECODE", 2, 2, 0, 0, 0, RcsTemp }, {"DISPLAY_EXP_MACROS", 2, 2, 0, 0, 0, Ignored }, {"ACK_HAILS", 2, 2, 0, 0, 0, AckHails }, {"MODE", 2, 2, 0, 1, COVR, Mode }, {"PROGRAM_DAYS", 2, 2, 0, 1, COVR, ProgramDays }, {"ASIF_DATE", 2, 2, 0, 1, COVR, AsIfDate }, {"ASIF_TIME", 2, 2, 0, 1, COVR, AsIfTime }, {"COMBINE_EVENTS", 2, 2, 0, 1, COVR, CombineEvents }, {"COMPRESS_MACROS", 2, 2, 0, 1, COVR, CompressMacros }, {"FEB_KLUGE", 2, 2, 0, 1, COVR, FebKluge }, {"LATITUDE", 2, 2, 0, 1, COVR, Latitude }, {"LONGITUDE", 2, 2, 0, 1, COVR, Longitude }, {"DAWN_OPTION", 2, 2, 0, 1, COVR, DawnOption }, {"DUSK_OPTION", 2, 2, 0, 1, COVR, DuskOption }, {"DAWN_SUBSTITUTE", 2, 2, 0, 1, COVR, Ignored }, {"DUSK_SUBSTITUTE", 2, 2, 0, 1, COVR, Ignored }, {"MIN_DAWN", 2, 2, 0, 1, COVR, MinDawn }, {"MAX_DAWN", 2, 2, 0, 1, COVR, MaxDawn }, {"MIN_DUSK", 2, 2, 0, 1, COVR, MinDusk }, {"MAX_DUSK", 2, 2, 0, 1, COVR, MaxDusk }, {"WRITE_CHECK_FILES", 2, 2, 0, 1, COVR, CheckFiles }, {"REPORT_PATH", 2, 2, 0, 1, COVR, ReportPath }, {"REPL_DELAYED_MACROS", 2, 2, 0, 1, COVR, ReplDelay }, {"RESERVED_TIMERS", 2, 2, 0, 1, COVR, ResvTimers }, /* WIP */ {"TRIGGER_TAG", 2, 2, 0, 1, COVR, Ignored }, {"XREF_APPEND", 2, 2, 0, 1, COVR, Ignored }, {"MACTERM", 2, 2, 0, 1, COVR, MacTerm }, /* WIP */ {"RESOLVE_OVERLAP", 2, 2, 0, 1, COVR, ResOverlap }, {"DAWNDUSK_DEF", 2, 2, 0, 1, COVR, SunMode }, {"SCRIPT_MODE", 2, 2, 0, 0, 0, ScriptMode }, {"SCRIPT_SHELL", 2, 2, 0, 0, 0, ScriptShell }, {"LAUNCH_SOURCE", 2, 6, 0, 0, 0, LaunchSrc }, {"DEFAULT_MODULE", 2, 2, 0, 0, 0, DefaultModule }, {"LOG_DIR", 2, 3, 0, 0, 0, LogDir }, {"ISDARK_OFFSET", 2, 2, 0, 0, 0, IsDarkOffset }, {"ENV_ALIAS_PREFIX", 2, 2, 0, 0, 0, EnvAliasPrefix }, {"FIX_5A", 2, 2, 0, 0, 0, Fix5A }, {"AUTOFETCH", 2, 2, 0, 0, 0, AutoFetch }, {"POWERFAIL_UPDATE", 2, 2, 0, 0, 0, PfailUpdate }, {"CM17A_BIT_DELAY", 2, 2, 0, 0, 0, BitDelay }, {"DEF_RF_BURSTS", 2, 2, 0, 0, 0, DefRFBursts }, {"TIMER_LOOPCOUNT", 2, 2, 0, 0, 0, LoopCount }, {"RF_BURST_SPACING", 2, 2, 0, 0, 0, BurstSpacing }, {"RF_TIMER_TWEAK", 2, 2, 0, 0, 0, TimerTweak }, {"RF_POST_DELAY", 2, 2, 0, 0, 0, RFPostDelay }, {"RF_FARB_DELAY", 2, 2, 0, 0, 0, RFFarbDelay }, {"RF_FARW_DELAY", 2, 2, 0, 0, 0, RFFarwDelay }, {"DISPLAY_RF_XMIT", 2, 2, 0, 0, 0, DispRFX }, {"RESTRICT_DIMS", 2, 2, 0, 0, 0, RestrictDims }, {"STATE_FORMAT", 2, 2, 0, 0, 0, StateFmt }, {"RELAY_POWERFAIL_SCRIPT", 2,-1, 0, 0, 0, PfailScript}, {"CM11_POST_DELAY", 2, 2, 0, 0, 0, CM11PostDelay}, {"START_ENGINE", 2, 2, 0, 0, 0, StartEngine}, {"SECTION", 1,-1, 0, 0, COVR|CMLT, IgnoreSilent}, {"RF_NOSWITCH", 2, 2, 0, 0, 0, NoSwitch}, {"RESPOOL_PERMISSIONS", 2, 2, 0, 0, 0, RespoolPerms}, {"SPOOLFILE_MAX", 2, 2, 0, 0, 0, SpoolMax}, {"CHECK_RI_LINE", 2, 2, 0, 0, 0, CheckRILine}, {"RI_DISABLE", 2, 2, 0, 0, 0, RIdisable}, {"SEND_RETRIES", 2, 2, 0, 0, 0, SendRetries}, {"TRANSCEIVE", 2, 3, 0, 0, 0, Transceive}, {"TRANSCIEVE", 2, 3, 0, 0, 0, TransMissp}, {"RFFORWARD", 2, 3, 0, 0, 0, RFForward}, {"TRANS_DIMLEVEL", 2, 2, 0, 0, 0, TransDim}, {"SCRIPT_CTRL", 2, 2, 0, 0, 0, ScriptCtrl}, {"STATE_CTRL", 2, 2, 0, 0, 0, StateCtrl}, {"RING_CTRL", 2, 2, 0, 0, 0, RingCtrl}, {"RF_FUNCMASK", 2,-1, 0, 0, 0, RFFuncMask}, {"AUX_REPCOUNTS", 4, 4, 0, 0, 0, AuxRepcounts}, {"HIDE_UNCHANGED", 2, 2, 0, 0, 0, HideUnchg}, {"DISPLAY_RF_NOISE", 2, 2, 0, 0, 0, DispRFNoise}, {"ARM_MAX_DELAY", 2, 2, 0, 0, 0, ArmMaxDelay}, {"DISPLAY_RAW_RF", 2, 2, 0, 0, 0, DispRawRF}, {"ARM_LOGIC", 2, 2, 0, 0, 0, ArmLogic}, {"INACTIVE_TIMEOUT", 2, 2, 0, 0, 0, InactiveTimeout}, {"HEYU_UMASK", 2, 2, 0, 0, 0, HeyuUmask}, {"AUTO_WAIT", 2, 2, 0, 0, 0, AutoWait}, {"FULL_BRIGHT", 2, 2, 0, 0, 0, FullBright}, {"ENGINE_POLL", 2, 2, 0, 0, 0, EnginePoll}, }; int ncommands = ( sizeof(command)/sizeof(struct conf) ); /* Dawn/Dusk options */ static struct ddopt { char *label; unsigned char value; } dd_option[] = { {"FIRST", FIRST }, {"EARLIEST", EARLIEST }, {"LATEST", LATEST }, {"AVERAGE", AVERAGE }, {"MEDIAN", MEDIAN }, }; int nddopt = ( sizeof(dd_option)/sizeof(struct ddopt) ); /* Launch source options - set default sources of signal */ /* permitted to launch a script. */ static struct lsopt { char *label; unsigned int value; } ls_option[] = { {"SNDC", SNDC }, /* Sent from command line */ {"SNDM", SNDM }, /* Sent from macro executed by Timer */ {"SNDT", SNDT }, /* Sent from macro executed by Trigger */ {"SNDP", SNDP }, /* Sent from a relay powerfail script */ {"SNDS", SNDS }, /* Sent from a script */ {"RCVI", RCVI }, /* Received from the interface */ {"RCVT", RCVT }, /* Trigger which executed a macro */ {"SNDA", SNDA }, /* Transceived by aux daemon */ {"RCVA", RCVA }, /* Received via sptty from aux daemon */ {"ANYSRC", LSALL }, /* Any of the above (SNDS and SNDP not included!) */ {"NOSRC", LSNONE }, /* No sources are the default */ }; int nlsopt = ( sizeof(ls_option)/sizeof(struct lsopt) ); /* CM17A function labels. Note: CONFIG.rf_bursts[] in */ /* process.h must be at least the size of nrflabels below */ static struct cm17a_label { char *label; unsigned char subcode; } rf_label[] = { {"falloff", 0 }, {"flightson", 1 }, {"fon", 2 }, {"foff", 3 }, {"fdim", 4 }, {"fbright", 5 }, {"flightsoff", 6 }, {"_", 7 }, {"_", 8 }, {"farb", 9 }, {"farw", 10 }, }; int nrflabels = ( sizeof(rf_label)/sizeof(struct cm17a_label) ); /*---------------------------------------------------------------------+ | Reset the isparsed flags in the conf struct array. | +---------------------------------------------------------------------*/ void reset_isparsed_flags ( void ) { int j; for ( j = 0; j < ncommands; j++ ) command[j].isparsed = 0; return; } /*---------------------------------------------------------------------+ | Return 1 if all valid characters within alias label, otherwise 0 | +---------------------------------------------------------------------*/ int is_valid_alias_label ( char *label, char **sp ) { *sp = label; while ( **sp ) { if ( isalnum((int)(**sp)) || strchr("-_.", **sp) ) { (*sp)++; continue; } return 0; } return 1; } /*---------------------------------------------------------------------+ | Initialize the CONFIG structure with default values. | +---------------------------------------------------------------------*/ void initialize_config ( void ) { char *sp1; int j; /* Load default configuration values */ configp->read_flag = 0; configp->mode = DEF_MODE; (void) strncpy2(configp->schedfile, DEF_SCHEDFILE, sizeof(config.schedfile) - 1); configp->asif_date = -1; configp->asif_time = -1; configp->program_days_in = DEF_PROGRAM_DAYS; configp->program_days = DEF_PROGRAM_DAYS; configp->combine_events = DEF_COMBINE_EVENTS; configp->compress_macros = DEF_COMPRESS_MACROS; configp->feb_kluge = DEF_FEB_KLUGE; configp->housecode = DEF_HOUSECODE; configp->force_addr = DEF_FORCE_ADDR; configp->loc_flag = 0; configp->dawn_option = DEF_DAWN_OPTION; configp->dusk_option = DEF_DUSK_OPTION; configp->dawn_substitute = DEF_DAWN_SUBSTITUTE; configp->dusk_substitute = DEF_DUSK_SUBSTITUTE; configp->min_dawn = DEF_MIN_DAWN; configp->max_dawn = DEF_MAX_DAWN; configp->min_dusk = DEF_MIN_DUSK; configp->max_dusk = DEF_MAX_DUSK; strncpy2(configp->tty, DEF_TTY, sizeof(config.tty) - 1); configp->ttyaux[0] = '\0'; configp->suffixaux[0] = '\0'; configp->auxdev = 0; configp->newformat = 0; configp->checkfiles = DEF_CHECK_FILES; configp->repl_delay = DEF_REPL_DELAYED_MACROS; configp->reserved_timers = DEF_RESERVED_TIMERS; configp->aliasp = NULL; configp->scenep = NULL; configp->scriptp = NULL; configp->launcherp = NULL; configp->pfail_script = NULL; configp->max_pparms = DEF_MAX_PPARMS; configp->rcs_temperature = DEF_RCS_TEMPERATURE; configp->trigger_tag = DEF_TRIGGER_TAG; configp->display_offset = DEF_DISPLAY_OFFSET; configp->xref_append = DEF_XREF_APPEND; configp->ack_hails = DEF_ACK_HAILS; configp->macterm = DEF_MACTERM; configp->status_timeout = DEF_STATUS_TIMEOUT; configp->spf_timeout = DEF_SPF_TIMEOUT; configp->disp_exp_mac = DEF_DISPLAY_EXP_MACROS; configp->module_types = DEF_MODULE_TYPES; configp->launch_mode = DEF_LAUNCH_MODE; configp->function_mode = DEF_FUNCTION_MODE; configp->launch_source = DEF_LAUNCH_SOURCE; configp->res_overlap = DEF_RES_OVERLAP; configp->default_module = lookup_module_type(DEF_DEFAULT_MODULE); configp->script_mode = DEF_SCRIPT_MODE; if ( (sp1 = getenv("SHELL")) != NULL ) strncpy2(configp->script_shell, sp1, sizeof(config.script_shell) - 1); else strncpy2(configp->script_shell, "/bin/sh", sizeof(config.script_shell) - 1); *configp->logfile = '\0'; configp->isdark_offset = DEF_ISDARK_OFFSET; strncpy2(configp->env_alias_prefix, DEF_ENV_ALIAS_PREFIX, sizeof(config.env_alias_prefix) - 1); configp->sunmode = DEF_SUNMODE; configp->fix_5a = DEF_FIX_5A; configp->cm11_post_delay = DEF_CM11_POST_DELAY; configp->pfail_update = DEF_PFAIL_UPDATE; configp->cm17a_bit_delay = DEF_CM17A_BIT_DELAY; configp->rf_burst_spacing = DEF_RF_BURST_SPACING; configp->rf_timer_tweak = DEF_RF_TIMER_TWEAK; configp->rf_post_delay = DEF_RF_POST_DELAY; configp->rf_farb_delay = DEF_RF_FARB_DELAY; configp->rf_farw_delay = DEF_RF_FARW_DELAY; configp->disp_rf_xmit = DEF_DISP_RF_XMIT; configp->def_rf_bursts = DEF_RF_BURSTS; for ( j = 0; j < nrflabels; j++ ) configp->rf_bursts[j] = DEF_RF_BURSTS; configp->timer_loopcount = 0; configp->restrict_dims = DEF_RESTRICT_DIMS; configp->state_format = DEF_STATE_FORMAT; configp->start_engine = DEF_START_ENGINE; configp->rf_noswitch = DEF_RF_NOSWITCH; configp->respool_perms = DEF_RESPOOL_PERMS; configp->spool_max = DEF_SPOOLFILE_MAX; configp->check_RI_line = DEF_CHECK_RI_LINE; configp->send_retries = DEF_SEND_RETRIES; configp->script_ctrl = DEF_SCRIPT_CTRL; configp->transceive = DEF_TRANSCEIVE; configp->rfforward = DEF_RFFORWARD; configp->trans_dim = DEF_TRANS_DIMLEVEL; configp->state_ctrl = DEF_STATE_CTRL; configp->ring_ctrl = DEF_RING_CTRL; configp->heyu_umask = DEF_HEYU_UMASK; configp->log_umask = DEF_LOG_UMASK; configp->rf_funcmask = DEF_RF_FUNCMASK; configp->aux_repcounts[0] = 0; /* Value set at finalize_config() */ configp->aux_repcounts[1] = DEF_AUX_REPCOUNT; configp->aux_repcounts[2] = DEF_AUX_MAXCOUNT; configp->aux_repcounts[3] = DEF_AUX_FLOODREP; configp->hide_unchanged = DEF_AUX_HIDE_UNCHANGED; configp->disp_rf_noise = DEF_DISP_RF_NOISE; configp->arm_max_delay = DEF_ARM_MAX_DELAY; configp->disp_raw_rf = DEF_DISP_RAW_RF; configp->arm_logic = DEF_ARM_LOGIC; configp->inactive_timeout = DEF_INACTIVE_TIMEOUT; configp->device_type = DEF_DEVICE_TYPE; configp->auto_wait = DEF_AUTO_WAIT; configp->full_bright = DEF_FULL_BRIGHT; configp->engine_poll = DEF_ENGINE_POLL; return; } /*---------------------------------------------------------------------+ | Parse the users configuration file and save info in CONFIG struct | | for only the minimal directives required for some specific commands.| | E.g., the 'heyu stop' command needs only the TTY directive. | +---------------------------------------------------------------------*/ int parse_minimal_config ( FILE *fd_conf, unsigned char mode, unsigned char source ) { char buffer[LINE_LEN]; char *sp1; int err, errors; CONFIG configtmp; configp = &configtmp; /* Make sure the isparsed flags are reset in the command list */ reset_isparsed_flags(); /* Load some default configuration values */ initialize_config(); line_no = 0; errors = 0; while ( fgets(buffer, LINE_LEN, fd_conf) != NULL ) { line_no++ ; buffer[LINE_LEN - 1] = '\0'; /* Get rid of comments and blank lines */ if ( (sp1 = strchr(buffer, '#')) != NULL ) *sp1 = '\0'; (void) strtrim(buffer); if ( buffer[0] == '\0' ) continue; err = parse_config_tail(buffer, source); if ( err || *error_message() != '\0' ) { fprintf(stderr, "Config Line %02d: %s\n", line_no, error_message()); clear_error_message(); } errors += err; if ( errors > MAX_ERRORS ) return 1; } /* Determine and load the user's timezone */ get_std_timezone(); configp->tzone = std_tzone; /* Determine if Daylight Time is ever in effect and if so, */ /* the minutes after midnight it goes into and out of effect. */ configp->isdst = get_dst_info(0); /* Add configuration items from environment */ errors += environment_config(); errors += finalize_config(mode); if ( *error_message() != '\0' ) { fprintf(stderr, "%s\n", error_message()); clear_error_message(); } /* Free the allocated memory in the original config structure */ free_all_arrays(&config); /* Copy the temporary config into the global structure */ memcpy(&config, configp, sizeof(config)); configp = &config; /* Done with config file */ line_no = 0; return errors; } /*---------------------------------------------------------------------+ | Parse the users configuration file and save info in CONFIG struct. | | First check to see if it's already been read. | +---------------------------------------------------------------------*/ int parse_config ( FILE *fd_conf, unsigned char mode ) { char buffer[LINE_LEN]; char *sp1; int err, errors; int j; SCENE *scenep; extern char *typename[]; CONFIG configtmp; /* If the configuration file has already been read into memory, */ /* just return. */ if ( config.read_flag != 0 ) { return 0; } configp = &configtmp; /* Make sure the isparsed flags are reset in the command list */ reset_isparsed_flags(); /* Load some default configuration values */ initialize_config(); line_no = 0; errors = 0; while ( fgets(buffer, LINE_LEN, fd_conf) != NULL ) { line_no++ ; buffer[LINE_LEN - 1] = '\0'; /* Get rid of comments and blank lines */ if ( (sp1 = strchr(buffer, '#')) != NULL ) *sp1 = '\0'; (void) strtrim(buffer); if ( buffer[0] == '\0' ) continue; err = parse_config_tail(buffer, SRC_CONFIG); if ( err || *error_message() != '\0' ) { fprintf(stderr, "Config Line %02d: %s\n", line_no, error_message()); clear_error_message(); } errors += err; if ( errors > MAX_ERRORS ) return 1; } /* Everything has now been read from the config file and stored */ /* Verify the syntax of user-defined scenes/usersyns */ scenep = configp->scenep; j = 0; while ( scenep && scenep[j].line_no > 0 ) { if ( verify_scene(scenep[j].body) != 0 ) { fprintf(stderr, "Config Line %02d: %s '%s': %s\n", scenep[j].line_no, typename[scenep[j].type], scenep[j].label, error_message()); clear_error_message(); if ( ++errors > MAX_ERRORS ) return errors; } else if ( *error_message() != '\0' ) { /* Check warning messages */ fprintf(stderr, "Config Line %02d: %s '%s': %s\n", scenep[j].line_no, typename[scenep[j].type], scenep[j].label, error_message()); clear_error_message(); } j++; } /* Configure module masks */ set_module_masks( configp->aliasp ); /* Use the Heyu path if the user hasn't provided an alternate */ if ( !(*alt_path) ) (void)strncpy2(alt_path, heyu_path, sizeof(alt_path) - 1); /* Determine and load the user's timezone */ get_std_timezone(); configp->tzone = std_tzone; /* Determine if Daylight Time is ever in effect and if so, */ /* the minutes after midnight it goes into and out of effect. */ configp->isdst = get_dst_info(0); /* Add configuration items from environment */ errors += environment_config(); errors += finalize_config(mode); if ( *error_message() != '\0' ) { fprintf(stderr, "%s\n", error_message()); clear_error_message(); } configp->read_flag = (errors == 0) ? 1 : 0; if ( errors > 0 ) return errors; /* Free the allocated memory in the original config structure */ free_all_arrays(&config); /* Copy the temporary config into the global structure */ memcpy(&config, configp, sizeof(config)); configp = &config; /* Done with config file */ line_no = 0; return errors; } /*---------------------------------------------------------------------+ | Parse the config line in buffer. Argument 'source' can be | | SRC_CONFIG if called from parse_config() or SRC_SCHED if called | | from parse_sched(), for configuration items which may be overridden | | in the latter. | +---------------------------------------------------------------------*/ int parse_config_tail ( char *buffer, unsigned char source ) { char errbuffer[80]; char searchstr[128]; char directive[128]; char label[(NAME_LEN + SCENE_LEN + MACRO_LEN + 1)]; char token[50]; char hc; char *bufp, *sp; int errors = 0; int num, value, hour, minut, len, modtype; long longvalue; int bursts; int j, k, commj, switchval; int tokc; char **tokv = NULL; int perms; bufp = buffer; get_token(directive, &bufp, " \t", sizeof(directive)/sizeof(char)); strtrim(bufp); strncpy2(searchstr, directive, sizeof(searchstr) - 1); strupper(searchstr); /* Search list of configuration commands starting */ /* past "oldalias". */ commj = 0; switchval = OldAlias; for ( j = 1; j < ncommands; j++ ) { if ( !strcmp(searchstr, command[j].name) ) { switchval = command[j].switchval; commj = j; break; } } /* See if override in schedule file is permitted */ if ( (source == SRC_SCHED || source == SRC_ENVIRON) && (command[commj].flags & COVR) == 0 ) { if ( commj ) sprintf(errbuffer, "Configuration directive %s not allowed here.", command[commj].name); else sprintf(errbuffer, "Invalid configuration directive"); store_error_message(errbuffer); return 1; } /* Minimal configurations for some commands */ if ( source == SRC_STOP && (command[commj].flags & CSTP) == 0 ) return 0; if ( source == SRC_HELP && (command[commj].flags & CHLP) == 0 ) return 0; if ( source == SRC_SYSCHECK && (command[commj].flags & CCAL) == 0 ) return 0; /* If the directive has a fixed number of tokens (as denoted */ /* by maxtokens > 0) or _might_ be an old-style alias, */ /* tokenize the tail immediately. */ if ( commj == 0 || command[j].maxtoken > 0 ) { tokenize(bufp, " \t", &tokc, &tokv); } else { tokc = (*bufp == '\0') ? 0 : 1 ; } /* If not found in list and it doesn't have the correct */ /* number of items for an alias, reject it. */ if ( commj == 0 && tokc != 2 ) { sprintf(errbuffer, "Invalid Directive '%s'", directive); store_error_message(errbuffer); return 1; } /* Unless allowed, check to see it's not a duplicate */ /* of an earlier directive. */ if ( command[commj].isparsed & source && (command[commj].flags & CMLT) == 0) { sprintf(errbuffer, "Directive '%s' appears more than once in the file", searchstr); store_error_message(errbuffer); return 1; } else { command[commj].isparsed |= source; } /* Check that commands found on the list have the correct */ /* number of tokens on the line. */ if ( (tokc + 1) < command[commj].mintoken ) { store_error_message("Too few items on line."); return 1; } else if ( (command[commj].maxtoken != -1) && ((tokc + 1) > command[commj].maxtoken) ) { store_error_message("Too many items on line."); return 1; } errors = 0; switch ( switchval ) { case Ignored : sprintf(errbuffer, "Directive %s is obsolete and is being ignored.", searchstr); store_error_message(errbuffer); break; case Mode : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "COMPATIBLE") ) configp->mode = COMPATIBLE; else if ( !strcmp(tokv[0], "HEYU") ) configp->mode = HEYU_MODE; else { store_error_message("MODE must be COMPATIBLE or HEYU"); errors++; } break; case OldAlias : /* Possible alias, else invalid command */ /* Check if it matches the form of an alias */ if ( tokc != 2 || strlen(tokv[0]) != 1 || (hc = toupper((int)(*tokv[0]))) < 'A' || hc > 'P' ) { store_error_message("Invalid Directive."); errors++; break; } if ( !is_valid_alias_label(directive, &sp) ) { sprintf(errbuffer, "Invalid character '%c' in alias label.", *sp); store_error_message(errbuffer); errors++; break; } if ( strcmp(tokv[0], "macro") == 0 ) { store_error_message("An alias label may not be the word \"macro\"."); errors++; break; } if ( strchr("_-", *directive ) ) { store_error_message("Alias labels may not begin with '_' or '-'"); errors++; break; } if ( add_alias(&configp->aliasp, directive, line_no, hc, tokv[1], -1) < 0 ) { errors++; } break; case Alias : /* New alias format using ALIAS directive */ /* Expects housecode and unit code(s) to be concatenated, */ /* the same as they are for the command line or macro. */ if ( !is_valid_alias_label(tokv[0], &sp) ) { sprintf(errbuffer, "Invalid character '%c' in alias label.", *sp); store_error_message(errbuffer); errors++; break; } if ( strcmp(tokv[0], "macro") == 0 ) { store_error_message("An alias label may not be the word \"macro\"."); errors++; break; } if ( strchr("_-", *tokv[0] ) ) { store_error_message("Alias labels may not begin with '_' or '-'"); errors++; break; } hc = toupper((int)(*tokv[1])); *tokv[1] = ' '; strtrim(tokv[1]); modtype = -1; if ( tokc > 2 ) { if ( (modtype = lookup_module_type(tokv[2])) < 0 ) { sprintf(errbuffer, "Invalid module model '%s'", tokv[2]); store_error_message(errbuffer); errors++; break; } } if ( (j = add_alias(&configp->aliasp, tokv[0], line_no, hc, tokv[1], modtype)) < 0 ) { errors++; break; } if ( modtype >= 0 && tokc >= 3 ) { if ( add_module_options(configp->aliasp, j, tokv + 3, tokc - 3) != 0 ) { errors++; break; } } break; case UserSyn : get_token(label, &bufp, " \t", sizeof(label)/sizeof(char)); if ( add_scene(&configp->scenep, label, line_no, bufp, F_USYN) < 0 ) { errors++; } break; case Scene : get_token(label, &bufp, " \t", sizeof(label)/sizeof(char)); if ( add_scene(&configp->scenep, label, line_no, bufp, F_SCENE) < 0 ) { errors++; } break; case AsIfDate : configp->asif_date = strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->asif_date < 19700101 || configp->asif_date > 20380101 ) { store_error_message("ASIF_DATE must be yyyymmdd between 19700101 and 20380101"); errors++; } break; case AsIfTime : num = sscanf(tokv[0], "%d:%d", &hour, &minut) ; value = 60 * hour + minut ; configp->asif_time = value; if ( num != 2 || value < 0 || value > 1439 ) { store_error_message("ASIF_TIME must be hh:mm between 00:00-23:59"); errors++; } break; case ScheduleFile : strncpy2(configp->schedfile, tokv[0], sizeof(config.schedfile) - 1); break; case ProgramDays : strupper(tokv[0]); configp->program_days_in = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->program_days_in < 1 || configp->program_days_in > 366 ) { store_error_message("PROGRAM_DAYS outside range 1-366"); errors++; } break; case CombineEvents : strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->combine_events = YES; else if ( !strcmp(tokv[0], "NO") ) configp->combine_events = NO; else { store_error_message("COMBINE_EVENTS must be YES or NO"); errors++; } break; case CompressMacros : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->compress_macros = YES; else if ( !strcmp(tokv[0], "NO") ) configp->compress_macros = NO; else { store_error_message("COMPRESS_MACROS must be YES or NO"); errors++; } break; case FebKluge : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->feb_kluge = YES; else if ( !strcmp(tokv[0], "NO") ) configp->feb_kluge = NO; else { store_error_message("FEB_KLUGE must be YES or NO"); errors++; } break; case Latitude : errors += parse_latitude(tokv[0]); break; case Longitude : errors += parse_longitude(tokv[0]); break; case DawnOption : (void) strupper(tokv[0]); for ( j = 0; j < nddopt; j++ ) { if ( !strcmp(tokv[0], dd_option[j].label) ) break; } if ( j == nddopt ) { store_error_message("Invalid DAWN_OPTION"); errors++; break; } configp->dawn_option = dd_option[j].value; break; case DuskOption : (void) strupper(tokv[0]); for ( j = 0; j < nddopt; j++ ) { if ( !strcmp(tokv[0], dd_option[j].label) ) break; } if ( j == nddopt ) { store_error_message("Invalid DUSK_OPTION"); errors++; break; } configp->dusk_option = dd_option[j].value; break; case DawnSubstitute : num = sscanf(tokv[0], "%d:%d", &hour, &minut) ; value = 60 * hour + minut ; if ( num != 2 || value < 0 || value > 1439 ) { store_error_message("DAWN_SUBSTITUTE - must be 00:00-23:59 (hh:mm)"); errors++; break; } configp->dawn_substitute = value; break; case DuskSubstitute : num = sscanf(tokv[0], "%d:%d", &hour, &minut) ; value = 60 * hour + minut ; if ( num != 2 || value < 0 || value > 1439 ) { store_error_message("DUSK_SUBSTITUTE must be 00:00-23:59 (hh:mm)"); errors++; break; } configp->dusk_substitute = value; break; case MinDawn : if ( strcmp(strupper(tokv[0]), "OFF") == 0 ) { configp->min_dawn = OFF; break; } num = sscanf(tokv[0], "%d:%d", &hour, &minut); value = 60 * hour + minut ; if ( num != 2 || value < 0 || value > 1439 ) { store_error_message("MIN_DAWN must be 00:00-23:59 (hh:mm) or OFF"); errors++; break; } configp->min_dawn = value; break; case MaxDawn : if ( strcmp( strupper(tokv[0]), "OFF") == 0 ) { configp->max_dawn = OFF; break; } num = sscanf(tokv[0], "%d:%d", &hour, &minut); value = 60 * hour + minut ; if ( num != 2 || value < 0 || value > 1439 ) { store_error_message("MAX_DAWN must be 00:00-23:59 (hh:mm) or OFF"); errors++; break; } configp->max_dawn = value; break; case MinDusk : if ( strcmp( strupper(tokv[0]), "OFF") == 0 ) { configp->min_dusk = OFF; break; } num = sscanf(tokv[0], "%d:%d", &hour, &minut); value = 60 * hour + minut ; if ( num != 2 || value < 0 || value > 1439 ) { store_error_message("MIN_DUSK must be 00:00-23:59 (hh:mm) or OFF"); errors++; break; } configp->min_dusk = value; break; case MaxDusk : if ( strcmp( strupper(tokv[0]), "OFF") == 0 ) { configp->max_dusk = OFF; break; } num = sscanf(tokv[0], "%d:%d", &hour, &minut); value = 60 * hour + minut ; if ( num != 2 || value < 0 || value > 1439 ) { store_error_message("MAX_DUSK must be 00:00-23:59 (hh:mm) or OFF"); errors++; break; } configp->max_dusk = value; break; case Tty : (void) strncpy2(configp->tty, tokv[0], sizeof(config.tty) - 1); for ( j = 1; j < tokc; j++) { strupper(tokv[j]); if ( strncmp(tokv[j], "CM10A", 4) == 0 ) { configp->device_type |= DEV_CM10A; } else if ( strncmp(tokv[j], "CM17A", 4) == 0 ) { configp->device_type |= DEV_CM17A; } else if ( !(strncmp(tokv[j], "CM11A", 4) == 0 || strncmp(tokv[j], "CM12U", 4) == 0) ) { sprintf(errbuffer, "Unsupported interface type '%s'", tokv[j]); store_error_message(errbuffer); errors++; break; } } break; case TtyAux : (void) strncpy2(configp->ttyaux, tokv[0], sizeof(config.ttyaux) - 1); strupper(tokv[1]); if ( strncmp(tokv[1], "W800RF32", 8) == 0 ) { configp->auxdev = DEV_W800RF32; } else if ( strncmp(tokv[1], "MR26A", 4) == 0 ) { configp->auxdev = DEV_MR26A; } else if ( strncmp(tokv[1], "RFXCOM", 4) == 0 ) { configp->auxdev = DEV_RFXCOM; } else { sprintf(errbuffer, "Unsupported aux device '%s'", tokv[1]); store_error_message(errbuffer); errors++; break; } break; case HouseCode : /* Base housecode */ hc = toupper((int)(*tokv[0])); if ( (int)strlen(tokv[0]) > 1 || hc < 'A' || hc > 'Z' ) { store_error_message("Invalid HOUSECODE - must be A though P"); errors++; break; } configp->housecode = hc; default_housecode = hc; break; case ForceAddr : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->force_addr = YES; else if ( !strcmp(tokv[0], "NO") ) configp->force_addr = NO; else { store_error_message("FORCE_ADDR must be YES or NO"); errors++; } break; case NewFormat : /* New Format - allow for additional choices in future */ if ( (tokc + 1) == 1 ) configp->newformat = 1; else configp->newformat = (unsigned char)strtol(tokv[0], NULL, 10); break; case CheckFiles : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->checkfiles = YES; else if ( !strcmp(tokv[0], "NO") ) configp->checkfiles = NO; else { store_error_message("WRITE_CHECK_FILES must be YES or NO"); errors++; } break; case ReportPath : /* Alternate path for report files */ (void)strncpy2(alt_path, tokv[0], sizeof(alt_path) - 1); if ( alt_path[strlen(alt_path) - 1] != '/' ) (void)strncat(alt_path, "/", sizeof(alt_path) - 1 - strlen(alt_path)); break; case ReplDelay : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->repl_delay = YES; else if ( !strcmp(tokv[0], "NO") ) configp->repl_delay = NO; else { store_error_message("REPL_DELAYED_MACROS must be YES or NO"); errors++; } break; case ResvTimers : (void) strupper(tokv[0]); configp->reserved_timers = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->reserved_timers < 0 || configp->reserved_timers > 50 ) { store_error_message("RESERVED_TIMERS outside range 0-50"); errors++; } break; case TrigTag : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->trigger_tag = YES; else if ( !strcmp(tokv[0], "NO") ) configp->trigger_tag = NO; else { store_error_message("TRIGGER_TAG must be YES or NO"); errors++; } break; case MaxPParms : (void) strupper(tokv[0]); configp->max_pparms = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->max_pparms < 1 || configp->max_pparms > 999 ) { store_error_message("MAX_PPARMS outside range 1-999"); errors++; } break; case RcsTemp : /* Housecodes for which Preset commands received from */ /* thermostats are to be decoded into temperature. */ /* Information is stored as a housecode bitmap. */ sp = tokv[0]; (void) strlower(sp); configp->rcs_temperature = 0; if ( !strcmp(sp, "off") || !strcmp(sp, "none") ) { break; } if ( !strcmp(sp, "all") || !strcmp(sp, "yes") ) { configp->rcs_temperature = 0xffff; break; } len = strlen(sp); if ( *sp == '[' && *(sp + len - 1) == ']' ) { /* Create the housecode bitmap */ for ( j = 1; j < len - 1; j++ ) { /* Ignore spaces and commas */ if ( sp[j] == ' ' || sp[j] == ',' ) continue; if ( sp[j] < 'a' || sp[j] > 'p' ) break; configp->rcs_temperature |= (1 << hc2code(sp[j])); } if ( j != len - 1 ) { store_error_message("Invalid character in RCS_DECODE housecode list"); errors++; break; } break; } store_error_message("RCS_DECODE must be OFF or [] or ALL"); errors++; break; case TransMissp : store_error_message("TRANSCEIVE misspelled"); case Transceive : value = 0; /* Housecodes to be transceived by the aux daemon */ sp = tokv[0]; (void) strlower(sp); if ( !strcmp(sp, "all") ) { configp->transceive = TR_ALL; break; } if ( !strcmp(sp, "none") ) { configp->transceive = TR_NONE; break; } if ( !strcmp(sp, "allexcept") ) { if ( tokc < 2 ) { store_error_message("Too few parameters for TRANSCEIVE ALLEXCEPT"); errors++; break; } sp = tokv[1]; strlower(sp); value = 1; } len = strlen(sp); if ( *sp == '[' && *(sp + len - 1) == ']' ) { /* Create the housecode bitmap */ configp->transceive = 0; for ( j = 1; j < len - 1; j++ ) { /* Ignore periods and commas */ if ( sp[j] == '.' || sp[j] == ',' ) continue; if ( sp[j] < 'a' || sp[j] > 'p' ) { store_error_message("Invalid character in TRANSCEIVE housecode list"); errors++; break; } configp->transceive |= (1 << hc2code(sp[j])); } if ( value == 1 ) configp->transceive = 0xffff & ~configp->transceive; break; } store_error_message("TRANSCEIVE must be ALL or NONE or ALLEXCEPT []"); errors++; break; case RFForward : value = 0; /* Housecodes to be transceived by the aux daemon */ sp = tokv[0]; (void) strlower(sp); if ( !strcmp(sp, "all") ) { configp->rfforward = TR_ALL; break; } if ( !strcmp(sp, "none") ) { configp->rfforward = TR_NONE; break; } if ( !strcmp(sp, "allexcept") ) { if ( tokc < 2 ) { store_error_message("Too few parameters for RFFORWARD ALLEXCEPT"); errors++; break; } sp = tokv[1]; strlower(sp); value = 1; } len = strlen(sp); if ( *sp == '[' && *(sp + len - 1) == ']' ) { /* Create the housecode bitmap */ configp->rfforward = 0; for ( j = 1; j < len - 1; j++ ) { /* Ignore periods and commas */ if ( sp[j] == '.' || sp[j] == ',' ) continue; if ( sp[j] < 'a' || sp[j] > 'p' ) { store_error_message("Invalid character in RFFORWARD housecode list"); errors++; break; } configp->rfforward |= (1 << hc2code(sp[j])); } if ( value == 1 ) configp->rfforward = 0xffff & ~configp->rfforward; break; } store_error_message("RFFORWARD must be ALL or NONE or ALLEXCEPT []"); errors++; break; case TransDim : configp->trans_dim = (unsigned char)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->trans_dim < 1 || configp->trans_dim > 22 ) { store_error_message("TRANS_DIMLEVEL outside range 1-22"); errors++; } break; case StatusTimeout : configp->status_timeout = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->status_timeout < 1 || configp->status_timeout > 5 ) { store_error_message("STATUS_TIMEOUT outside range 1-5"); errors++; } break; case SpfTimeout : configp->spf_timeout = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->spf_timeout < 1 || configp->spf_timeout > 10 ) { store_error_message("SPF_TIMEOUT outside range 1-10"); errors++; } break; case XrefApp : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->xref_append = YES; else if ( !strcmp(tokv[0], "NO") ) configp->xref_append = NO; else { store_error_message("XREF_APPEND must be YES or NO"); errors++; } break; case AckHails : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->ack_hails = YES; else if ( !strcmp(tokv[0], "NO") ) configp->ack_hails = NO; else { store_error_message("ACK_HAILS must be YES or NO"); errors++; } break; case DispExpMac : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->disp_exp_mac = YES; else if ( !strcmp(tokv[0], "NO") ) configp->disp_exp_mac = NO; else { store_error_message("DISPLAY_EXP_MACROS must be YES or NO"); errors++; } break; case MacTerm : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->macterm = YES; else if ( !strcmp(tokv[0], "NO") ) configp->macterm = NO; else { store_error_message("MACTERM must be YES or NO"); errors++; } break; case AutoChain : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->auto_chain = YES; else if ( !strcmp(tokv[0], "NO") ) configp->auto_chain = NO; else { store_error_message("AUTO_CHAIN must be YES or NO"); errors++; } break; case Launcher : if ( add_launchers(&configp->launcherp, line_no, bufp) < 0 ) errors++; break; case Script : if ( add_script(&configp->scriptp, &configp->launcherp, line_no, bufp) < 0 ) errors++; break; case ModuleTypes : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->module_types = YES; else if ( !strcmp(tokv[0], "NO") ) configp->module_types = NO; else { store_error_message("MODULE_TYPES must be YES or NO"); errors++; } break; case FunctionMode : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "ACTUAL") ) configp->function_mode = FUNC_ACTUAL; else if ( !strcmp(tokv[0], "GENERIC") ) configp->function_mode = FUNC_GENERIC; else { store_error_message("FUNCTION_MODE must be ACTUAL or GENERIC"); errors++; } break; case LaunchMode : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "SIGNAL") ) configp->launch_mode = TMODE_SIGNAL; else if ( !strcmp(tokv[0], "MODULE") ) configp->launch_mode = TMODE_MODULE; else { store_error_message("LAUNCH_MODE must be SIGNAL or MODULE"); errors++; } break; case LaunchSrc : value = 0; for ( j = 0; j < tokc; j++ ) { strncpy2(token, tokv[j], sizeof(token)); strupper(token); if ( strcmp(token, "SNDS") == 0 ) { sprintf(errbuffer, "LAUNCH_SOURCE '%s' not allowed as a default.", tokv[j]); store_error_message(errbuffer); errors++; break; } for ( k = 0; k < nlsopt; k++ ) { if ( strcmp(token, ls_option[k].label) == 0 ) { value |= ls_option[k].value; break; } } if ( k >= nlsopt ) { sprintf(errbuffer, "Invalid LAUNCH_SOURCE option '%s'", tokv[j]); store_error_message(errbuffer); errors++; break; } } if ( value & LSNONE ) { if ( value & ~LSNONE ) store_error_message("Warning: LAUNCH_SOURCE 'nosrc' cancels all others on line."); configp->launch_source = 0; } else configp->launch_source = (unsigned char)value; break; case ResOverlap : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "OLD") ) configp->res_overlap = RES_OVLAP_COMBINED; else if ( !strcmp(tokv[0], "NEW") ) configp->res_overlap = RES_OVLAP_SEPARATE; else { store_error_message("RESOLVE_OVERLAP must be OLD or NEW"); errors++; } break; case DefaultModule : if ( (configp->default_module = lookup_module_type(tokv[0])) < 0 ) { sprintf(errbuffer, "Module type '%s' is unknown.", tokv[0]); store_error_message(errbuffer); errors++; } break; case ScriptShell : if ( access(tokv[0], X_OK) == 0 ) { strncpy2(configp->script_shell, tokv[0], sizeof(config.script_shell) - 1); } else { sprintf(errbuffer, "An executable shell '%s' is not found.", tokv[0]); store_error_message(errbuffer); errors++; } break; case ScriptMode : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "HEYUHELPER") ) configp->script_mode = HEYU_HELPER; else if ( !strncmp(tokv[0], "SCRIPT", 6) ) configp->script_mode = HEYU_SCRIPT; else { store_error_message("SCRIPT_MODE must be HEYUHELPER or SCRIPT"); errors++; } break; case LogDir : strncpy2(token, tokv[0], sizeof(token) - 1); strupper(token); if ( strcmp(token, "NONE") == 0 ) { *configp->logfile = '\0'; break; } strncpy2(token, tokv[0], sizeof(token) - 1); strcat(token, "/"); if ( access(token, W_OK) != 0 ) { store_error_message( "LOG_DIR does not exist or is not writable."); errors++; } else { sprintf(configp->logfile, "%s/%s", tokv[0], LOGFILE); } if (tokc == 2) { configp->log_umask = (int)strtol(tokv[1], &sp, 8); if ( !strchr(" \t\n", *sp) ) { store_error_message("Invalid logfile umask value."); errors++; } } break; case IsDarkOffset : configp->isdark_offset = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->isdark_offset < -360 || configp->isdark_offset > 360 ) { store_error_message("ISDARK_OFFSET outside range +/-360 minutes"); errors++; } break; case EnvAliasPrefix : strupper(tokv[0]); if ( strcmp(tokv[0], "UC") == 0 ) strncpy2(configp->env_alias_prefix, "X10", sizeof(config.env_alias_prefix) - 1); else if ( strcmp(tokv[0], "LC") == 0 ) strncpy2(configp->env_alias_prefix, "x10", sizeof(config.env_alias_prefix) - 1); else { store_error_message("ENV_ALIAS_PREFIX must be UC or LC"); errors++; } break; case SunMode : strupper(tokv[0]); if ( strncmp(tokv[0], "RISESET", 1) == 0 ) configp->sunmode = RiseSet; else if ( strncmp(tokv[0], "CIVILTWI", 1) == 0 ) configp->sunmode = CivilTwi; else if ( strncmp(tokv[0], "NAUTTWI", 1) == 0 ) configp->sunmode = NautTwi; else if ( strncmp(tokv[0], "ASTROTWI", 1) == 0 ) configp->sunmode = AstroTwi; else { store_error_message("DAWNDUSK_DEF must be R, C, N, or A"); errors++; } break; case Fix5A : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->fix_5a = YES; else if ( !strcmp(tokv[0], "NO") ) configp->fix_5a = NO; else { store_error_message("FIX_5A must be YES or NO"); errors++; } break; case CM11PostDelay : configp->cm11_post_delay = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->cm11_post_delay < 0 || configp->cm11_post_delay > 1000 ) { store_error_message("CM11_POST_DELAY must be 0-1000"); errors++; } break; case AutoFetch : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->autofetch = YES; else if ( !strcmp(tokv[0], "NO") ) configp->autofetch = NO; else { store_error_message("AUTOFETCH must be YES or NO"); errors++; } break; case PfailUpdate : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->pfail_update = YES; else if ( !strcmp(tokv[0], "NO") ) configp->pfail_update = NO; else { store_error_message("POWERFAIL_UPDATE must be YES or NO"); errors++; } break; case BitDelay : configp->cm17a_bit_delay = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->cm17a_bit_delay < 100 || configp->cm17a_bit_delay > 10000 ) { store_error_message("CM17A_BIT_DELAY must be 100-10000"); errors++; } break; case BurstSpacing : configp->rf_burst_spacing = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->rf_burst_spacing < 80 || configp->rf_burst_spacing > 160 ) { store_error_message("RF_BURST_SPACING must be 80-160"); errors++; } break; case TimerTweak : configp->rf_timer_tweak = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->rf_timer_tweak < 0 || configp->rf_timer_tweak > 50 ) { store_error_message("RF_TIMER_TWEAK must be 0-50"); errors++; } break; case RFPostDelay : configp->rf_post_delay = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->rf_post_delay < 0 || configp->rf_post_delay > 10000 ) { store_error_message("RF_POST_DELAY must be 0-10000"); errors++; } break; case RFFarbDelay : configp->rf_farb_delay = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->rf_farb_delay < 0 || configp->rf_farb_delay > 10000 ) { store_error_message("RF_FARB_DELAY must be 0-10000"); errors++; } break; case RFFarwDelay : configp->rf_farw_delay = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->rf_farw_delay < 0 || configp->rf_farw_delay > 10000 ) { store_error_message("RF_FARW_DELAY must be 0-10000"); errors++; } break; case DispRFX : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->disp_rf_xmit = YES; else if ( !strcmp(tokv[0], "NO") ) configp->disp_rf_xmit = NO; else if ( !strcmp(tokv[0], "VERBOSE") ) configp->disp_rf_xmit = VERBOSE; else { store_error_message("DISPLAY_RF_XMIT must be NO or YES or VERBOSE"); errors++; } break; case DefRFBursts : configp->def_rf_bursts = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->def_rf_bursts < 5 || configp->def_rf_bursts > 6 ) { store_error_message("DEF_RF_BURSTS must be 5 or 6"); errors++; } break; case RFBursts : bursts = strtol(tokv[1], &sp, 10); if ( !strchr(" \t\n", *sp) || bursts < 1 ) { store_error_message("Invalid RF_BURSTS"); errors++; break; } (void) strlower(tokv[0]); for ( j = 0; j < nrflabels; j++ ) { if ( !strcmp(tokv[0], rf_label[j].label) ) { configp->rf_bursts[rf_label[j].subcode] = bursts; break; } } if ( j >= nrflabels ) { sprintf(errbuffer, "Unknown CM17A function '%s'", tokv[0]); store_error_message(errbuffer); errors++; } break; case LoopCount : configp->timer_loopcount = (unsigned long)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) ) { store_error_message("Invalid TIMER_LOOPCOUNT"); errors++; break; } break; case RestrictDims : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->restrict_dims = YES; else if ( !strcmp(tokv[0], "NO") ) configp->restrict_dims = NO; else { store_error_message("RESTRICT_DIMS must be YES or NO"); errors++; } break; case StateFmt : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "NEW") ) configp->state_format = NEW; else if ( !strcmp(tokv[0], "OLD") ) configp->state_format = OLD; else { store_error_message("STATE_FORMAT must be NEW or OLD"); errors++; } break; case PfailScript : if ( !(configp->pfail_script = strdup(bufp)) ) { store_error_message("Memory allocation error - out of memory"); errors++; } break; case StartEngine : (void) strupper(tokv[0]); if ( !strncmp(tokv[0], "MANUAL", 3) ) configp->start_engine = MANUAL; else if ( !strncmp(tokv[0], "AUTOMATIC", 4) ) configp->start_engine = AUTOMATIC; else { store_error_message("START_ENGINE must be MANUAL or AUTO"); errors++; } break; case IgnoreSilent : break; case NoSwitch : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->rf_noswitch = YES; else if ( !strcmp(tokv[0], "NO") ) configp->rf_noswitch = NO; else { store_error_message("RF_NOSWITCH must be YES or NO"); errors++; } break; case RespoolPerms : #ifndef RESPOOL store_error_message( "The RESPOOL_PERMISSIONS directive is invalid for this operating system"); errors++; break; #endif perms = (int)strtol(tokv[0], &sp, 8); if ( !strchr(" \t\n", *sp) || perms < 0 || perms > 07777) { store_error_message("RESPOOL_PERMISSIONS - invalid octal number"); errors++; } configp->respool_perms = (unsigned int)perms; break; case SpoolMax : configp->spool_max = strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->spool_max < SPOOLFILE_ABSMIN || configp->spool_max > SPOOLFILE_ABSMAX ) { sprintf(errbuffer, "SPOOLFILE_MAX must be between %ul and %ul", SPOOLFILE_ABSMIN, SPOOLFILE_ABSMAX); store_error_message(errbuffer); errors++; } break; case CheckRILine : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->check_RI_line = YES; else if ( !strcmp(tokv[0], "NO") ) configp->check_RI_line = NO; else { store_error_message("CHECK_RI_LINE must be YES or NO"); errors++; } break; case RIdisable : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) store_error_message("Obsolete - use RING_CTRL DISABLE"); else if ( !strcmp(tokv[0], "NO") ) store_error_message("Obsolete - use RING_CTRL ENABLE"); else { store_error_message("Obsolete - Use RING_CTRL ENABLE or DISABLE"); } errors++; break; case RingCtrl : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "ENABLE") ) configp->ring_ctrl = ENABLE; else if ( !strcmp(tokv[0], "DISABLE") ) configp->ring_ctrl = DISABLE; else { store_error_message("RING_CTRL must be ENABLE or DISABLE"); errors++; } break; case SendRetries : configp->send_retries = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n", *sp) || configp->send_retries < 0 ) { store_error_message("SEND_RETRIES must be > 0"); errors++; } break; case ScriptCtrl : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "ENABLE") ) configp->script_ctrl = ENABLE; else if ( !strcmp(tokv[0], "DISABLE") ) configp->script_ctrl = DISABLE; else { store_error_message("SCRIPT_CTRL must be ENABLE or DISABLE"); errors++; } break; case StateCtrl : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "SINGLE") ) configp->state_ctrl = SC_SINGLE; else if ( !strcmp(tokv[0], "BITMAP") ) configp->state_ctrl = SC_BITMAP; else { store_error_message("STATE_CTRL must be SINGLE or BITMAP"); errors++; } break; case RFFuncMask : store_error_message("RF_FUNCMASK not yet implemented"); errors++; break; case HideUnchg : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->hide_unchanged = YES; else if ( !strcmp(tokv[0], "NO") ) configp->hide_unchanged = NO; else { store_error_message("HIDE_UNCHANGED must be YES or NO"); errors++; } break; case AuxRepcounts : for ( j = 0; j < 3; j++ ) { value = (int)strtol(tokv[j], &sp, 10); if ( strchr(" \t\n", *sp) == NULL || value < 0 ) { store_error_message("Invalid AUX_REPCOUNTS"); errors++; break; } configp->aux_repcounts[j] = value; } if ( configp->aux_repcounts[0] < 1 ) { store_error_message("AUX_REPCOUNTS value must be greater than zero"); errors++; break; } break; case DispRFNoise : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "YES") ) configp->disp_rf_noise = YES; else if ( !strcmp(tokv[0], "NO") ) configp->disp_rf_noise = NO; else { store_error_message("DISPLAY_RF_NOISE must be YES or NO"); errors++; } break; case ArmMaxDelay : if ( (longvalue = parse_hhmmss(tokv[0], 3)) >= 0 && longvalue < 43200 ) { configp->arm_max_delay = (int)longvalue; } else { store_error_message("Invalid ARM_MAX_DELAY"); errors++; } break; case ArmLogic : strupper(tokv[0]); configp->arm_logic = (strcmp(tokv[0], "STRICT") == 0) ? ArmLogicStrict : (strcmp(tokv[0], "MEDIUM") == 0) ? ArmLogicMedium : (strcmp(tokv[0], "LOOSE") == 0) ? ArmLogicLoose : 0; if ( configp->arm_logic == 0 ) { store_error_message("ARM_LOGIC must be STRICT, MEDIUM or LOOSE"); errors++; } break; case InactiveTimeout : if ( (longvalue = parse_hhmmss(tokv[0], 3)) >= 0 && longvalue < 86400 ) { configp->inactive_timeout = longvalue; } else { store_error_message("Invalid INACTIVE_TIMEOUT"); errors++; } break; case DispRawRF : (void) strupper(tokv[0]); if ( !strcmp(tokv[0], "NONE") ) configp->disp_raw_rf = DISPMODE_RF_NONE; else if ( !strcmp(tokv[0], "NOISE") ) configp->disp_raw_rf = DISPMODE_RF_NOISE; else if ( !strcmp(tokv[0], "ALL") ) configp->disp_raw_rf = (DISPMODE_RF_NORMAL | DISPMODE_RF_NOISE); else { store_error_message("DISPLAY_RAW_RF must be NONE or NOISE or ALL"); errors++; } break; case HeyuUmask : if ( strcmp(strupper(tokv[0]), "OFF") == 0 ) { configp->heyu_umask = -1; } else { configp->heyu_umask = (int)strtol(tokv[0], &sp, 8); if ( !strchr(" \t\r\n", *sp) || configp->heyu_umask < 0 ) { store_error_message("Invalid HEYU_UMASK value."); errors++; } } break; case AutoWait : configp->auto_wait = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n\r", *sp) || configp->auto_wait < 0 || configp->auto_wait > 300 ) { store_error_message("AUTO_WAIT timeout must be 0-300 seconds."); errors++; } break; case FullBright : value = (int)strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n\r", *sp) || value < 16 || value > 31 ) { store_error_message("FULL_BRIGHT level must be 16 though 31"); errors++; } configp->full_bright = (unsigned char)value; break; case EnginePoll : configp->engine_poll = strtol(tokv[0], &sp, 10); if ( !strchr(" \t\n\r", *sp) || configp->engine_poll < 100L || configp->engine_poll > 100000L ) { store_error_message("ENGINE_POLL must be 100 though 100000"); errors++; } break; default : store_error_message("Unsupported config directive"); errors++; break; } free( tokv ); return errors; } /*---------------------------------------------------------------------+ | Get certain configuration items from environment. | +---------------------------------------------------------------------*/ int environment_config ( void ) { int j, retcode, errors = 0; char *sp; char buffer[32]; static char *envars[] = { "LATITUDE", "LONGITUDE", "ASIF_DATE", "ASIF_TIME", }; reset_isparsed_flags(); for ( j = 0; j < (int)(sizeof(envars)/sizeof(char *)); j++ ) { if ( (sp = getenv(envars[j])) != NULL ) { sprintf(buffer, "%s %s", envars[j], sp); retcode = parse_config_tail(buffer, SRC_ENVIRON); errors += retcode; if ( retcode != 0 || *error_message() != '\0' ) { fprintf(stderr, "Environment variable %s.\n", error_message()); clear_error_message(); } } } return errors; } /*---------------------------------------------------------------------+ | Display configuration file directives that have been overridden, | | either by an environment variable or by a 'config' command in the | | schedule file, as indicated by the isparsed flag. | +---------------------------------------------------------------------*/ void display_config_overrides ( FILE *fd ) { int j, count = 0; char delim = ' '; fprintf(fd, "Configuration overrides:"); for ( j = 0; j < ncommands; j++ ) { if ( command[j].isparsed & (SRC_ENVIRON | SRC_SCHED)) { count++; fprintf(fd, "%c %s", delim, command[j].name); delim = ','; } } if ( !count ) fprintf(fd, " -- None --"); fprintf(fd, "\n\n"); return; } /*---------------------------------------------------------------------+ | Open the user's X10 configuration file and read only the minimal | | directives for specific commands, like 'heyu stop'. | +---------------------------------------------------------------------*/ void read_minimal_config ( unsigned char mode, unsigned int source ) { FILE *fd ; int error_count; char confp[PATH_LEN + 1]; extern char heyu_config[PATH_LEN + 1]; extern int verbose; char msg[80]; find_heyu_path(); strncpy2(confp, pathspec(heyu_config), sizeof(confp) - 1); if ( verbose ) (void) fprintf(stdout, "Opening Heyu configuration file '%s'\n", confp); if ( !(fd = fopen(confp, "r")) ) { snprintf(msg, sizeof(msg) - 1, "Unable to find (or open) Heyu configuration file '%s'\n", confp); if ( !i_am_relay ) fprintf(stderr, msg); else syslog(LOG_ERR, msg); return; } error_count = parse_minimal_config( fd, mode, source ); if ( error_count != 0 ) { snprintf(msg, sizeof(msg) - 1, "Quitting due to errors in configuration file '%s'\n", confp); if ( !i_am_relay ) fprintf(stderr, msg); else syslog(LOG_ERR, msg); exit(1); } (void) fclose( fd ); return; } /*---------------------------------------------------------------------+ | Return the index in the array of SCRIPT structures for the SCRIPT | | having the argument 'label', or -1 if not found. | +---------------------------------------------------------------------*/ int lookup_script ( SCRIPT *scriptp, char *label ) { int j = 0; while ( scriptp && scriptp[j].line_no > 0 ) { if ( strcmp(label, scriptp[j].label) == 0 ) return j; j++; } return -1; } /*---------------------------------------------------------------------+ | Return the index in the array of LAUNCHER structures for the first | | LAUNCHER having the argument 'label', or -1 if not found. | +---------------------------------------------------------------------*/ int lookup_launcher ( LAUNCHER *launcherp, char *label ) { int j = 0; while ( launcherp && launcherp[j].line_no > 0 ) { if ( strcmp(label, launcherp[j].label) == 0 ) return j; j++; } return -1; } /*---------------------------------------------------------------------+ | Resolve interrelated configuration items. | +---------------------------------------------------------------------*/ int finalize_config ( unsigned char mode ) { int finalize_launchers(void); int create_file_paths(void); int initialize_security_sensor_timeout(CONFIG *); mode_t heyu_umask; char errmsg[80]; char *sp; int j; if ( configp->heyu_umask >= 0 ) umask((mode_t)configp->heyu_umask); heyu_umask = umask(0); umask(heyu_umask); if ( configp->log_umask < 0 ) configp->log_umask = (int)heyu_umask; if ( configp->mode == COMPATIBLE ) { configp->program_days = 366; } else { configp->program_days = configp->program_days_in; } if ( configp->min_dusk != OFF && configp->max_dusk != OFF && configp->min_dusk >= configp->max_dusk ) { store_error_message("MIN_DUSK must be less than MAX_DUSK"); return 1; } if ( configp->min_dawn != OFF && configp->max_dawn != OFF && configp->min_dawn >= configp->max_dawn ) { store_error_message("MIN_DAWN must be less than MAX_DAWN"); return 1; } if ( strcmp(configp->tty, "dummy") == 0 ) { configp->device_type |= DEV_DUMMY; } /* Create suffix for lock files, e.g., ".ttyS0" */ if ( (sp = strrchr(configp->tty, '/')) != NULL ) { strncpy2(configp->suffix, sp, sizeof(config.suffix) - 1); *(configp->suffix) = '.'; } else { strncpy2(configp->suffix, ".", sizeof(config.suffix) - 1); strncat(configp->suffix, configp->tty, sizeof(config.suffix) - 1 - strlen(configp->suffix)); } if ( configp->auxdev ) { if ( (sp = strrchr(configp->ttyaux, '/')) != NULL ) { strncpy2(configp->suffixaux, sp, sizeof(config.suffixaux) - 1); *(configp->suffixaux) = '.'; } else { strncpy2(configp->suffixaux, ".", sizeof(config.suffixaux) - 1); strncat(configp->suffixaux, configp->ttyaux, sizeof(config.suffixaux) - 1 - strlen(configp->suffixaux)); } } if ( configp->aux_repcounts[0] == 0 ) { configp->aux_repcounts[0] = (configp->auxdev == DEV_MR26A) ? DEF_AUX_MINCOUNT_MR26 : DEF_AUX_MINCOUNT_W800; } if ( *configp->logfile == '\0' ) { strncpy2(configp->logfile, DEF_LOGFILE, sizeof(config.logfile) - 1); } else { strncat(configp->logfile, configp->suffix, sizeof(config.logfile) - 1 - strlen(configp->logfile)); } create_file_paths(); if ( access(configp->logfile, F_OK) == 0 && access(configp->logfile, W_OK) != 0 ) { sprintf(errmsg, "Log file '%s' is not writable - check permissions.", configp->logfile); store_error_message(errmsg); return 1; } if ( access(statefile, F_OK) == 0 && access(statefile, W_OK) != 0 ) { sprintf(errmsg, "State file '%s' is not writable - check permissions.", statefile); store_error_message(errmsg); return 1; } for ( j = 0; j < nrflabels; j++ ) { if ( configp->rf_bursts[j] > configp->def_rf_bursts ) { sprintf(errmsg, "RF_BURSTS exceeds %d\n", configp->def_rf_bursts); store_error_message(errmsg); return 1; } } if ( configp->transceive & configp->rfforward ) { store_error_message("Housecode conflict in TRANSCEIVE and RFFORWARD"); return 1; } initialize_security_sensor_timeout( configp ); if ( finalize_launchers() > 0 ) return 1; return 0; } /*---------------------------------------------------------------------+ | Add an alias to the array of ALIAS structures and return the index | | of the new member. | +---------------------------------------------------------------------*/ int add_alias ( ALIAS **aliaspp, char *label, int line_no, char housecode, char *units, int modtype ) { static int size, max_size; static int strucsize = sizeof(ALIAS); int j, k, maxlevel; int blksize = 10; char hc; unsigned int bmap; unsigned long vflags, flags, xflags; char errmsg[128]; int (*module_xlate_func(int index))(); clear_error_message(); /* Allocate initial block of memory */ if ( *aliaspp == NULL ) { *aliaspp = (ALIAS *) calloc(blksize, strucsize ); if ( *aliaspp == NULL ) { (void) fprintf(stderr, "Unable to allocate memory for Alias.\n"); exit(1); } max_size = blksize; size = 0; /* Initialize it where necessary */ for ( j = 0; j < max_size; j++ ) { (*aliaspp)[j].label[0] = '\0'; (*aliaspp)[j].modtype = -1; (*aliaspp)[j].line_no = 0; (*aliaspp)[j].vflags = 0; (*aliaspp)[j].flags = 0; (*aliaspp)[j].xflags = 0; (*aliaspp)[j].optflags = 0; (*aliaspp)[j].vtype = 0; (*aliaspp)[j].nident = 0; for ( k = 0; k < NIDENT; k++ ) (*aliaspp)[j].ident[k] = 0; (*aliaspp)[j].xlate_func = NULL; (*aliaspp)[j].timeout = 0; } } /* Check for a valid label length */ if ( (int)strlen(label) > NAME_LEN ) { sprintf(errmsg, "Alias label '%s' too long - maximum %d characters", label, NAME_LEN); store_error_message(errmsg); return -1; } /* See if the alias label is already in the list. */ /* If so, it's an error. */ if ( (j = get_alias(*aliaspp, label, &hc, &bmap)) >= 0 ) { (void) sprintf(errmsg, "Duplicate alias label '%s'", label); store_error_message(errmsg); return -1; } /* Verify that the label is not 'macro' */ if ( strcmp(label, "macro") == 0 ) { store_error_message("An alias may not have the label 'macro'\n"); return -1; } /* Check the housecode */ if ( housecode == '_' ) { (void) sprintf(errmsg, "Alias '%s': Default housecode symbol ('_') is invalid in an alias", label); store_error_message(errmsg); return -1; } if ( (hc = toupper((int)housecode)) < 'A' || hc > 'P' ) { (void) sprintf(errmsg, "Alias '%s': Housecode '%c' outside range A-P", label, hc); store_error_message(errmsg); return -1; } /* Check the units list */ if ( parse_units( units, &bmap ) != 0 ) { (void) sprintf(errmsg, "Alias '%s': ", label); add_error_prefix(errmsg); return -1; } /* Check to see that the module type (if any) specified for */ /* this housecode|unit address doesn't conflict with that */ /* in a previously defined alias. */ if ( modtype >= 0 ) { j = 0; while ( (*aliaspp)[j].line_no > 0 ) { if ( (*aliaspp)[j].housecode == hc && (*aliaspp)[j].unitbmap & bmap && (*aliaspp)[j].modtype >= 0 && (*aliaspp)[j].modtype != modtype ) { sprintf(errmsg, "Module type conflicts with that defined for %c%s on Line %d", hc, bmap2units((*aliaspp)[j].unitbmap & bmap), (*aliaspp)[j].line_no); store_error_message(errmsg); return -1; } j++; } } /* Check to see if there's an available location */ /* If not, increase the size of the memory allocation. */ /* (Always leave room for a final termination indicator.) */ if ( size == (max_size - 1)) { max_size += blksize ; *aliaspp = (ALIAS *) realloc(*aliaspp, max_size * strucsize ); if ( *aliaspp == NULL ) { (void) fprintf(stderr, "Unable to increase size of Alias list.\n"); exit(1); } /* Initialize the new memory allocation */ for ( j = size; j < max_size; j++ ) { (*aliaspp)[j].label[0] = '\0'; (*aliaspp)[j].modtype = -1; (*aliaspp)[j].line_no = 0; (*aliaspp)[j].vflags = 0; (*aliaspp)[j].flags = 0; (*aliaspp)[j].xflags = 0; (*aliaspp)[j].optflags = 0; (*aliaspp)[j].vtype = 0; (*aliaspp)[j].nident = 0; for ( k = 0; k < NIDENT; k++ ) (*aliaspp)[j].ident[k] = 0; (*aliaspp)[j].xlate_func = NULL; (*aliaspp)[j].timeout = 0; } } j = size; size += 1; (void) strncpy2((*aliaspp)[j].label, label, NAME_LEN); (*aliaspp)[j].line_no = line_no; (*aliaspp)[j].housecode = toupper((int)housecode); (*aliaspp)[j].unitbmap = bmap; (*aliaspp)[j].modtype = modtype; module_attributes(modtype, &vflags, &flags, &xflags, &maxlevel); (*aliaspp)[j].vflags = vflags; (*aliaspp)[j].flags = flags; (*aliaspp)[j].xflags = xflags; (*aliaspp)[j].maxlevel = maxlevel; (*aliaspp)[j].onlevel = maxlevel; (*aliaspp)[j].xlate_func = module_xlate_func(modtype); return j; } /*---------------------------------------------------------------------+ | Add a scene or usersyn to the array of SCENE structures and return | | the index of the new member. | | Argument 'type' may be 1 for a scene or 2 for a usersyn | +---------------------------------------------------------------------*/ int add_scene ( SCENE **scenepp, char *label, int line_no, char *body, unsigned int type ) { static int size, max_size ; static int strucsize = sizeof(SCENE); int j; char *sp; int cmdc, nparms; char **cmdv; int blksize = 10; char errmsg[128]; extern char *typename[]; /* Allocate initial block of memory */ if ( *scenepp == NULL ) { *scenepp = calloc(blksize, strucsize ); if ( *scenepp == NULL ) { fprintf(stderr, "Unable to allocate memory for Scene/Usersyn.\n"); exit(1); } max_size = blksize; size = 0; /* Initialize it */ for ( j = 0; j < max_size; j++ ) { (*scenepp)[j].label[0] = '\0'; (*scenepp)[j].line_no = -1; (*scenepp)[j].nparms = 0; (*scenepp)[j].type = 0; (*scenepp)[j].body = NULL; } } /* Check for a valid scene label */ if ( (int)strlen(label) > SCENE_LEN ) { sprintf(errmsg, "%s label too long - maximum %d characters", typename[type], SCENE_LEN); store_error_message(errmsg); return -1; } if ( strchr("+_-", *label) != NULL ) { sprintf(errmsg, "%s label may not may not begin with '+', '-' or '_'", typename[type]); store_error_message(errmsg); return -1; } if ( strchr(label, '$') != NULL ) { sprintf(errmsg, "%s label may not contain the '$' character", typename[type]); store_error_message(errmsg); return -1; } if ( strchr(label, ';') != NULL ) { sprintf(errmsg, "%s label may not contain the ';' character", typename[type]); store_error_message(errmsg); return -1; } /* See if the scene label is already in the list. */ /* If so, it's an error. */ if ( (j = lookup_scene(*scenepp, label)) >= 0 ) { sprintf(errmsg, "%s label '%s' previously defined as a %s on line %d", typename[type], label, typename[(*scenepp)[j].type], (*scenepp)[j].line_no); store_error_message(errmsg); return -1; } if ( is_admin_cmd(label) || is_direct_cmd(label) ) { sprintf(errmsg, "%s label '%s' conflicts with heyu command", typename[type], label); store_error_message(errmsg); return -1; } /* Check to see if there's an available location */ /* If not, increase the size of the memory allocation. */ /* (Always leave room for a final termination indicator.) */ if ( size == (max_size - 1)) { max_size += blksize ; *scenepp = realloc(*scenepp, max_size * strucsize ); if ( *scenepp == NULL ) { fprintf(stderr, "Unable to increase size of Scene/Usersyn list.\n"); exit(1); } /* Initialize the new memory allocation */ for ( j = size; j < max_size; j++ ) { (*scenepp)[j].label[0] = '\0'; (*scenepp)[j].line_no = -1; (*scenepp)[j].nparms = 0; (*scenepp)[j].type = 0; (*scenepp)[j].body = NULL; } } j = size; size += 1; /* Determine the number of replaceable parameters */ sp = strdup(body); tokenize(sp, " \t;", &cmdc, &cmdv); nparms = max_parms(cmdc, cmdv); free(sp); free(cmdv); if ( *error_message() != '\0' ) { sprintf(errmsg, "%s '%s': ", typename[type], label); add_error_prefix(errmsg); } if ( nparms < 0 ) return -1; sp = strdup(body); if ( sp == NULL ) { fprintf(stderr, "Unable to allocate memory for body of Scene/Usersyn '%s'.\n", label); exit(1); } strtrim(sp); (void) strncpy2((*scenepp)[j].label, label, SCENE_LEN); (*scenepp)[j].line_no = line_no; (*scenepp)[j].nparms = nparms; (*scenepp)[j].type = type; (*scenepp)[j].body = sp; return j; } /*---------------------------------------------------------------------+ | Parse Latitude string [NS+-]ddd:mm and store in struct config. | | line_no <= 0 indicates string from environment, otherwise line in | | configuration file. | +---------------------------------------------------------------------*/ int parse_latitude ( char *string ) { char tmpbuff[128]; char *sp1, *sp2; int sign; if ( string == NULL ) return 0; (void)strncpy2(tmpbuff, string, sizeof(tmpbuff) - 1); sp1 = tmpbuff; /* For compatibility with Heyu 1 */ if ( isdigit((int)(*sp1)) ) { (void)strcpy(tmpbuff, "N"); (void)strncpy2(tmpbuff + 1, string, sizeof(tmpbuff) - 2); } else if ( *sp1 == '+' ) { *sp1 = 'N'; } else if ( *sp1 == '-' ) { *sp1 = 'S'; } switch ( toupper((int)(*sp1)) ) { case 'N' : sign = 1; break; case 'S' : sign = -1; break; default : sign = -2; break; } sp1++; if ( sign < -1 || !(sp2 = strchr(sp1, ':')) ) { store_error_message("LATITUDE invalid - must be [NS+-]dd:mm"); configp->loc_flag &= ~(LATITUDE) ; return 1; } configp->lat_m = (int)strtol(sp2 + 1, NULL, 10); *sp2 = '\0'; configp->lat_d = (int)strtol(sp1, NULL, 10); if ( configp->lat_d == 0 ) configp->lat_m *= sign ; else configp->lat_d *= sign ; configp->latitude = ( configp->lat_d < 0 ) ? (double)configp->lat_d - (double)configp->lat_m/60. : (double)configp->lat_d + (double)configp->lat_m/60.; if ( configp->latitude < -89. || configp->latitude > 89. ) { store_error_message("LATITUDE outside range -89 to +89 degrees"); configp->loc_flag &= ~(LATITUDE); return 1; } configp->loc_flag |= LATITUDE ; return 0; } /*---------------------------------------------------------------------+ | Parse Longitude string [EW+-]ddd:mm and store in struct config. | | line_no <= 0 indicates string from environment, otherwise line in | | configuration file. | +---------------------------------------------------------------------*/ int parse_longitude ( char *string ) { char tmpbuff[128]; char *sp1, *sp2; int sign; if ( string == NULL ) return 0; (void)strncpy2(tmpbuff, string, sizeof(tmpbuff) - 1); sp1 = tmpbuff; /* For compatibility with Heyu 1, where positive longitude assumed West */ if ( isdigit((int)(*sp1)) ) { (void)strcpy(tmpbuff, "W"); (void)strncpy2(tmpbuff + 1, string, sizeof(tmpbuff) - 2); } else if ( *sp1 == '+' ) { *sp1 = 'W'; } else if ( *sp1 == '-' ) { *sp1 = 'E'; } switch ( toupper((int)(*sp1)) ) { case 'E' : sign = 1; break; case 'W' : sign = -1; break; default : sign = -2; break; } sp1++; if ( sign < -1 || !(sp2 = strchr(sp1, ':')) ) { store_error_message("LONGITUDE invalid - must be [EW+-]dd:mm"); configp->loc_flag &= ~(LONGITUDE) ; return 1; } configp->lon_m = (int)strtol(sp2 + 1, NULL, 10); *sp2 = '\0'; configp->lon_d = (int)strtol(sp1, NULL, 10); if ( configp->lon_d == 0 ) configp->lon_m *= sign ; else configp->lon_d *= sign ; configp->longitude = ( configp->lon_d < 0 ) ? (double)configp->lon_d - (double)configp->lon_m/60. : (double)configp->lon_d + (double)configp->lon_m/60.; if ( configp->longitude < -180. || configp->longitude > 180. ) { store_error_message("LONGITUDE outside range -180 to +180 degrees"); configp->loc_flag &= ~(LONGITUDE); return 1; } configp->loc_flag |= LONGITUDE ; return 0; } /*---------------------------------------------------------------------+ | Check executability of a file on user's PATH | +---------------------------------------------------------------------*/ int is_executable ( char *pathname ) { char pathbuffer[1024]; char buffer[1024]; char *sp; int j, tokc; char **tokv; if ( access(pathname, X_OK) == 0 ) return 1; if ( (sp = getenv("PATH")) == NULL ) return 0; strncpy2(pathbuffer, sp, sizeof(pathbuffer) - 1); tokenize(buffer, ":", &tokc, &tokv); for ( j = 0; j < tokc; j++ ) { strncpy2(buffer, tokv[j], sizeof(buffer) - 1); strncat(buffer, "/", sizeof(buffer) - 1 - strlen(buffer)); strncat(buffer, pathname, sizeof(buffer) - 1 - strlen(buffer)); if ( access(buffer, X_OK) == 0 ) { free(tokv); return 1; } } free(tokv); return 0; } /*---------------------------------------------------------------------+ | Free memory allocated for SCRIPT structure and contents. | +---------------------------------------------------------------------*/ void free_scripts ( SCRIPT **scriptpp ) { int j = 0; if ( *scriptpp == NULL ) return; while ( (*scriptpp)[j].line_no > 0 ) { if ( (*scriptpp)[j].cmdline ) { free((*scriptpp)[j].cmdline); } j++; } free(*scriptpp); *scriptpp = NULL; return; } /*---------------------------------------------------------------------+ | Free the array of SCENEs and the scene bodies therein. | +---------------------------------------------------------------------*/ void free_scenes ( SCENE **scenepp ) { int j = 0; if ( *scenepp == NULL ) return; while ( (*scenepp)[j].line_no > 0 ) { if ( (*scenepp)[j].body != NULL ) { free((*scenepp)[j].body); } j++; } free((*scenepp)); *scenepp = NULL; return; } /*---------------------------------------------------------------------+ | Free the array of ALIASES. | +---------------------------------------------------------------------*/ void free_aliases ( ALIAS **aliaspp ) { if ( *aliaspp == NULL ) return; free((*aliaspp)); *aliaspp = NULL; return; } /*---------------------------------------------------------------------+ | Free the array of LAUNCHERS. | +---------------------------------------------------------------------*/ void free_launchers ( LAUNCHER **launcherpp ) { if ( *launcherpp == NULL ) return; free((*launcherpp)); *launcherpp = NULL; return; } /*---------------------------------------------------------------------+ | Free the relay powerfail script. | +---------------------------------------------------------------------*/ void free_relay_powerfail_script ( char **pfail_script ) { if ( *pfail_script == NULL ) return; free(*pfail_script); *pfail_script = NULL; return; } /*---------------------------------------------------------------------+ | Free arrays of ALIASes, SCENEs, SCRIPTs, and LAUNCHERs | +---------------------------------------------------------------------*/ void free_all_arrays ( CONFIG *configp ) { free_aliases(&configp->aliasp); free_scenes(&configp->scenep); free_scripts(&configp->scriptp); free_launchers(&configp->launcherp); free_relay_powerfail_script(&configp->pfail_script); return; } /*---------------------------------------------------------------------+ | Create the file pathspecs for many files. | +---------------------------------------------------------------------*/ int create_file_paths ( void ) { sprintf(statefile, "%s", pathspec(STATE_FILE)); sprintf(enginelockfile, "%s/LCK..%s%s", LOCKDIR, STATE_LOCKFILE, configp->suffix); #if 0 if ( configp->ttyaux[0] ) { sprintf(auxfilename,"%s%s", AUXFILE, configp->suffixaux); sprintf(auxfile, "%s/%s%s", LOCKDIR, AUXFILE, configp->suffixaux); } else { auxfilename[0] = '\0'; } #endif #if 0 /* future */ sprintf(spoolfile, "%s/%s%s", SPOOLDIR, SPOOLFILE, configp->suffix); sprintf(relaylockfile, "%s/LCK..%s%s", LOCKDIR, RELAYFILE, configp->suffix); sprintf(writelockfile, "%s/LCK..%s%s", LOCKDIR, WRITEFILE, configp->suffix); sprintf(ttylockfile, "%s/LCK.%s", LOCKDIR, configp->suffix); #endif return 0; } /*---------------------------------------------------------------------+ | Open the user's X10 configuration file and call parse_config() to | | parse it and fill in global structure config. exit(1) is called | | if the file cannot be found or read, or if it contains errors. | +---------------------------------------------------------------------*/ int get_configuration ( unsigned char mode ) { FILE *fd ; int error_count; char confp[PATH_LEN + 1]; CONFIG configtmp; char msg[PATH_LEN + 1]; /* Return if the configuration file has already been read into memory */ if ( config.read_flag != 0 ) { return 0; } configp = &configtmp; find_heyu_path(); strncpy2(confp, pathspec(heyu_config), sizeof(confp) - 1); if ( verbose ) (void) fprintf(stdout, "Reading Heyu configuration file '%s'\n", confp); if ( !(fd = fopen(confp, "r")) ) { snprintf(msg, sizeof(msg) - 1, "Unable to find (or open) Heyu configuration file '%s'\n", confp); if ( !i_am_relay ) fprintf(stderr, msg); else syslog(LOG_ERR, msg); exit(1); } error_count = parse_config( fd, mode ); (void) fclose( fd ); if ( error_count != 0 ) { if ( mode == CONFIG_RESTART && (i_am_aux || i_am_relay) ) { return error_count; } else { (void)fprintf(stderr, "Quitting due to errors in configuration file '%s'\n", confp); exit(1); } } return 0; } /*---------------------------------------------------------------------+ | Parse a time string X:Y:Z and return the total time represented by | | 3600*X + 60*Y + Z, or -1 if error. With X:Y:Z = hh:mm:ss the value | | returned will typically represent seconds. With just Y:Z the value | | returned can be interpreted as either hh:mm or mm:ss depending on | | the context. A null field or empty field, i.e., "::" or ": :" is | | flagged as an error, as is a trailing ':'. | +---------------------------------------------------------------------*/ long parse_hhmmss ( char *hhmmss, int maxfields ) { int j, tokc; long value, seconds = 0; char **tokv; char *sp; char buf[32]; if ( strstr(hhmmss, "::") != NULL || *(hhmmss + strlen(hhmmss) - 1) == ':' || strlen(hhmmss) > (sizeof(buf) - 1) ) { return -1; } strcpy(buf, hhmmss); tokenize(buf, ":", &tokc, &tokv ); if ( tokc > maxfields ) { free(tokv); return -1; } for ( j = 0; j < tokc; j++ ) { if ( *strtrim(tokv[j]) == '\0' ) { free(tokv); return -1; } else { value = strtol(tokv[j], &sp, 10); if ( strchr(" \t\n", *sp) == NULL ) { free(tokv); return -1; } } seconds = 60 * seconds + value; } free(tokv); return seconds; }