//TODO webserver_mode, webserver_index_function, // webserver_whitelist_address, webserver_whitelist_netmask, // sqlslaveof int DXDB_configSetCommand(cli *c, robj *o) { if (!strcasecmp(c->argv[2]->ptr,"luacronfunc")) { if (server.alc.LuaCronFunc) zfree(server.alc.LuaCronFunc); server.alc.LuaCronFunc = zstrdup(o->ptr); return 0; } else if (!strcasecmp(c->argv[2]->ptr, "lua_output_start")) { if (server.alc.OutputLuaFunc_Start) { zfree(server.alc.OutputLuaFunc_Start); } server.alc.OutputLuaFunc_Start = zstrdup(o->ptr); return 0; } else if (!strcasecmp(c->argv[2]->ptr, "lua_output_cnames")) { if (server.alc.OutputLuaFunc_Cnames) { zfree(server.alc.OutputLuaFunc_Cnames); } server.alc.OutputLuaFunc_Cnames = zstrdup(o->ptr); return 0; } else if (!strcasecmp(c->argv[2]->ptr, "lua_output_row")) { if (server.alc.OutputLuaFunc_Row) zfree(server.alc.OutputLuaFunc_Row); server.alc.OutputLuaFunc_Row = zstrdup(o->ptr); return 0; } else if (!strcasecmp(c->argv[2]->ptr, "rest_api_mode")) { int yn = yesnotoi(o->ptr); if (yn == -1) goto badfmt; server.alc.RestAPIMode = yn ? 1 : -1; return 0; } else if (!strcasecmp(c->argv[2]->ptr, "outputmode")) { if (!strcasecmp(o->ptr, "embedded")) { server.alc.OutputMode = OUTPUT_EMBEDDED; } else if (!strcasecmp(o->ptr, "pure_redis")) { server.alc.OutputMode = OUTPUT_PURE_REDIS; } else if (!strcasecmp(o->ptr, "normal")) { server.alc.OutputMode = OUTPUT_NORMAL; } else if (!strcasecmp(o->ptr, "lua")) { if (!server.alc.OutputLuaFunc_Start || !server.alc.OutputLuaFunc_Cnames || !server.alc.OutputLuaFunc_Row) { sds err = sdsnew("-ERR: OUTPUTMODE lua requires " \ "[lua_output_start, lua_output_cnames, " \ "lua_output_row]\r\n"); addReplySds(c, err); decrRefCount(o); return -1; } server.alc.OutputMode = OUTPUT_LUA; } else { addReplySds(c,sdscatprintf(sdsempty(), "-ERR OUTPUTMODE: [EMBEDDED|PURE_REDIS|LUA|NORMAL] not: %s\r\n", (char *)o->ptr)); decrRefCount(o); return -1; } return 0; } return 1; badfmt: /* Bad format errors */ addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'", (char*)o->ptr, (char*)c->argv[2]->ptr); return -1; }
void load_config_from_string(sds config) { char *err = NULL; int linenum = 0, totlines, i; sds *lines = sdssplitlen(config, strlen(config), "\n", 1, &totlines); for (i = 0; i < totlines; i++) { sds *argv; int argc; linenum = i + 1; lines[i] = sdstrim(lines[i], " \t\r\n"); if (lines[i][0] == '#' || lines[i][0] == '\0') continue; argv = sdssplitargs(lines[i], &argc); if (argv == NULL) { err = "Unbalanced quotes in configuration line"; goto loaderr; } if (argc == 0) { sdsfreesplitres(argv, argc); continue; } sdstolower(argv[0]); if (strcasecmp(argv[0], "working-directory") == 0 && argc == 2) { destor.working_directory = sdscpy(destor.working_directory, argv[1]); } else if (strcasecmp(argv[0], "simulation-level") == 0 && argc == 2) { if (strcasecmp(argv[1], "all") == 0) { destor.simulation_level = SIMULATION_ALL; } else if (strcasecmp(argv[1], "append") == 0) { destor.simulation_level = SIMULATION_APPEND; } else if (strcasecmp(argv[1], "restore") == 0) { destor.simulation_level = SIMULATION_RESTORE; } else if (strcasecmp(argv[1], "no") == 0) { destor.simulation_level = SIMULATION_NO; } else { err = "Invalid simulation level"; goto loaderr; } } else if (strcasecmp(argv[0], "log-level") == 0 && argc == 2) { if (strcasecmp(argv[1], "debug") == 0) { destor.verbosity = DESTOR_DEBUG; } else if (strcasecmp(argv[1], "verbose") == 0) { destor.verbosity = DESTOR_VERBOSE; } else if (strcasecmp(argv[1], "notice") == 0) { destor.verbosity = DESTOR_NOTICE; } else if (strcasecmp(argv[1], "warning") == 0) { destor.verbosity = DESTOR_WARNING; } else { err = "Invalid log level"; goto loaderr; } } else if (strcasecmp(argv[0], "chunk-algorithm") == 0 && argc == 2) { if (strcasecmp(argv[1], "fixed") == 0) { destor.chunk_algorithm = CHUNK_FIXED; } else if (strcasecmp(argv[1], "rabin") == 0) { destor.chunk_algorithm = CHUNK_RABIN; } else if (strcasecmp(argv[1], "normalized rabin") == 0) { destor.chunk_algorithm = CHUNK_NORMALIZED_RABIN; } else if (strcasecmp(argv[1], "file") == 0) { destor.chunk_algorithm = CHUNK_FILE; } else { err = "Invalid chunk algorithm"; goto loaderr; } } else if (strcasecmp(argv[0], "chunk-avg-size") == 0 && argc == 2) { destor.chunk_avg_size = atoi(argv[1]); } else if (strcasecmp(argv[0], "chunk-max-size") == 0 && argc == 2) { destor.chunk_max_size = atoi(argv[1]); } else if (strcasecmp(argv[0], "chunk-min-size") == 0 && argc == 2) { destor.chunk_min_size = atoi(argv[1]); } else if (strcasecmp(argv[0], "fingerprint-index") == 0 && argc >= 3) { if (strcasecmp(argv[1], "exact") == 0) { destor.index_category[0] = INDEX_CATEGORY_EXACT; } else if (strcasecmp(argv[1], "near-exact") == 0) { destor.index_category[0] = INDEX_CATEGORY_NEAR_EXACT; } else { err = "Invalid index category"; goto loaderr; } if (strcasecmp(argv[2], "physical") == 0) { destor.index_category[1] = INDEX_CATEGORY_PHYSICAL_LOCALITY; } else if (strcasecmp(argv[2], "logical") == 0) { destor.index_category[1] = INDEX_CATEGORY_LOGICAL_LOCALITY; } else { err = "Invalid index category"; goto loaderr; } if (argc > 3) { if (strcasecmp(argv[3], "ddfs") == 0) { assert(destor.index_category[0] == INDEX_CATEGORY_EXACT && destor.index_category[1] == INDEX_CATEGORY_PHYSICAL_LOCALITY); destor.index_specific = INDEX_SPECIFIC_DDFS; } else if (strcasecmp(argv[3], "sampled index") == 0) { assert(destor.index_category[0] == INDEX_CATEGORY_NEAR_EXACT && destor.index_category[1] == INDEX_CATEGORY_PHYSICAL_LOCALITY); destor.index_specific = INDEX_SPECIFIC_SAMPLED; } else if (strcasecmp(argv[3], "block locality caching") == 0) { assert(destor.index_category[0] == INDEX_CATEGORY_EXACT && destor.index_category[1] == INDEX_CATEGORY_LOGICAL_LOCALITY); destor.index_specific = INDEX_SPECIFIC_BLOCK_LOCALITY_CACHING; } else if (strcasecmp(argv[3], "extreme binning") == 0) { assert(destor.index_category[0] == INDEX_CATEGORY_NEAR_EXACT && destor.index_category[1] == INDEX_CATEGORY_LOGICAL_LOCALITY); destor.index_specific = INDEX_SPECIFIC_EXTREME_BINNING; } else if (strcasecmp(argv[3], "sparse index") == 0) { assert(destor.index_category[0] == INDEX_CATEGORY_NEAR_EXACT && destor.index_category[1] == INDEX_CATEGORY_LOGICAL_LOCALITY); destor.index_specific = INDEX_SPECIFIC_SPARSE; } else if (strcasecmp(argv[3], "silo") == 0) { assert(destor.index_category[0] == INDEX_CATEGORY_NEAR_EXACT && destor.index_category[1] == INDEX_CATEGORY_LOGICAL_LOCALITY); destor.index_specific = INDEX_SPECIFIC_SILO; } else { err = "Invalid index specific"; goto loaderr; } } } else if (strcasecmp(argv[0], "fingerprint-index-cache-size") == 0 && argc == 2) { destor.index_cache_size = atoi(argv[1]); } else if (strcasecmp(argv[0], "fingerprint-index-key-value") == 0 && argc == 2) { if (strcasecmp(argv[1], "htable") == 0) { destor.index_key_value_store = INDEX_KEY_VALUE_HTABLE; } else { err = "Invalid key-value store"; goto loaderr; } } else if (strcasecmp(argv[0], "fingerprint-index-key-size") == 0 && argc == 2) { destor.index_key_size = atoi(argv[1]); } else if (strcasecmp(argv[0], "fingerprint-index-value-length") == 0 && argc == 2) { destor.index_value_length = atoi(argv[1]); } else if (strcasecmp(argv[0], "fingerprint-index-bloom-filter") == 0 && argc == 2) { destor.index_bloom_filter_size = atoi(argv[1]); } else if (strcasecmp(argv[0], "fingerprint-index-sampling-method") == 0 && argc >= 2) { if (strcasecmp(argv[1], "optmin") == 0) destor.index_sampling_method[0] = INDEX_SAMPLING_OPTIMIZED_MIN; else if (strcasecmp(argv[1], "random") == 0) destor.index_sampling_method[0] = INDEX_SAMPLING_RANDOM; else if (strcasecmp(argv[1], "min") == 0) destor.index_sampling_method[0] = INDEX_SAMPLING_MIN; else if (strcasecmp(argv[1], "uniform") == 0) destor.index_sampling_method[0] = INDEX_SAMPLING_UNIFORM; else { err = "Invalid feature method!"; goto loaderr; } if (argc > 2) { destor.index_sampling_method[1] = atoi(argv[2]); } else { destor.index_sampling_method[1] = 0; } } else if (strcasecmp(argv[0], "fingerprint-index-segment-algorithm") == 0 && argc >= 2) { if (strcasecmp(argv[1], "fixed") == 0) destor.index_segment_algorithm[0] = INDEX_SEGMENT_FIXED; else if (strcasecmp(argv[1], "file-defined") == 0) destor.index_segment_algorithm[0] = INDEX_SEGMENT_FILE_DEFINED; else if (strcasecmp(argv[1], "content-defined") == 0) destor.index_segment_algorithm[0] = INDEX_SEGMENT_CONTENT_DEFINED; else { err = "Invalid segment algorithm"; goto loaderr; } if (argc > 2) { assert(destor.index_segment_algorithm[0] != INDEX_SEGMENT_FILE_DEFINED); destor.index_segment_algorithm[1] = atoi(argv[2]); } } else if (strcasecmp(argv[0], "fingerprint-index-segment-boundary") == 0 && argc == 3) { destor.index_segment_min = atoi(argv[1]); destor.index_segment_max = atoi(argv[2]); } else if (strcasecmp(argv[0], "fingerprint-index-segment-selection") == 0 && argc >= 2) { destor.index_segment_selection_method[1] = 1; if (strcasecmp(argv[1], "base") == 0) destor.index_segment_selection_method[0] = INDEX_SEGMENT_SELECT_BASE; else if (strcasecmp(argv[1], "top") == 0) { destor.index_segment_selection_method[0] = INDEX_SEGMENT_SELECT_TOP; if (argc > 2) destor.index_segment_selection_method[1] = atoi(argv[2]); } else if (strcasecmp(argv[1], "mix") == 0) destor.index_segment_selection_method[0] = INDEX_SEGMENT_SELECT_MIX; else { err = "Invalid selection method!"; goto loaderr; } } else if (strcasecmp(argv[0], "fingerprint-index-segment-prefetching") == 0 && argc == 2) { destor.index_segment_prefech = atoi(argv[1]); } else if (strcasecmp(argv[0], "rewrite-algorithm") == 0 && argc >= 2) { if (strcasecmp(argv[1], "no") == 0) destor.rewrite_algorithm[0] = REWRITE_NO; else if (strcasecmp(argv[1], "cfl-based selective deduplication") == 0 || strcasecmp(argv[1], "cfl") == 0) destor.rewrite_algorithm[0] = REWRITE_CFL_SELECTIVE_DEDUPLICATION; else if (strcasecmp(argv[1], "context-based rewriting") == 0 || strcasecmp(argv[1], "cbr") == 0) destor.rewrite_algorithm[0] = REWRITE_CONTEXT_BASED; else if (strcasecmp(argv[1], "capping") == 0 || strcasecmp(argv[1], "cap") == 0) destor.rewrite_algorithm[0] = REWRITE_CAPPING; else { err = "Invalid rewriting algorithm"; goto loaderr; } if (argc > 2) { assert(destor.rewrite_algorithm != REWRITE_NO); destor.rewrite_algorithm[1] = atoi(argv[2]); } else { destor.rewrite_algorithm[1] = 1024; } } else if (strcasecmp(argv[0], "rewrite-enable-cfl-switch") == 0 && argc == 2) { destor.rewrite_enable_cfl_switch = yesnotoi(argv[1]); } else if (strcasecmp(argv[0], "rewrite-cfl-require") == 0 && argc == 2) { destor.rewrite_cfl_require = atof(argv[1]); } else if (strcasecmp(argv[0], "rewrite-cfl-usage-threshold") == 0 && argc == 2) { destor.rewrite_cfl_usage_threshold = atof(argv[1]); } else if (strcasecmp(argv[0], "rewrite-cbr-limit") == 0 && argc == 2) { destor.rewrite_cbr_limit = atof(argv[1]); } else if (strcasecmp(argv[0], "rewrite-cbr-minimal-utility") == 0 && argc == 2) { destor.rewrite_cbr_minimal_utility = atof(argv[1]); } else if (strcasecmp(argv[0], "rewrite-capping-level") == 0 && argc == 2) { destor.rewrite_capping_level = atoi(argv[1]); } else if (strcasecmp(argv[0], "rewrite-enable-har") == 0 && argc == 2) { destor.rewrite_enable_har = yesnotoi(argv[1]); } else if (strcasecmp(argv[0], "rewrite-har-utilization-threshold") == 0 && argc == 2) { destor.rewrite_har_utilization_threshold = atof(argv[1]); } else if (strcasecmp(argv[0], "rewrite-har-rewrite-limit") == 0 && argc == 2) { destor.rewrite_har_rewrite_limit = atof(argv[1]); } else if (strcasecmp(argv[0], "rewrite-enable-cache-aware") == 0 && argc == 2) { destor.rewrite_enable_cache_aware = yesnotoi(argv[1]); } else if (strcasecmp(argv[0], "restore-cache") == 0 && argc == 3) { if (strcasecmp(argv[1], "lru") == 0) destor.restore_cache[0] = RESTORE_CACHE_LRU; else if (strcasecmp(argv[1], "optimal cache") == 0 || strcasecmp(argv[1], "opt") == 0) destor.restore_cache[0] = RESTORE_CACHE_OPT; else if (strcasecmp(argv[1], "forward assembly") == 0 || strcasecmp(argv[1], "asm") == 0) destor.restore_cache[0] = RESTORE_CACHE_ASM; else { err = "Invalid restore cache"; goto loaderr; } destor.restore_cache[1] = atoi(argv[2]); } else if (strcasecmp(argv[0], "restore-opt-window-size") == 0 && argc == 2) { destor.restore_opt_window_size = atoi(argv[1]); } else if (strcasecmp(argv[0], "backup-retention-time") == 0 && argc == 2) { destor.backup_retention_time = atoi(argv[1]); } else { err = "Bad directive or wrong number of arguments"; goto loaderr; } sdsfreesplitres(argv, argc); } sdsfreesplitres(lines, totlines); return; loaderr: fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR in destor ***\n"); fprintf(stderr, "Reading the configuration file, at line %d\n", linenum); fprintf(stderr, ">>> '%s'\n", lines[i]); fprintf(stderr, "%s\n", err); exit(1); }
int DXDB_loadServerConfig(int argc, sds *argv) { //printf("DXDB_loadServerConfig: 0: %s\n", argv[0]); if (!strcasecmp(argv[0], "luacronfunc") && argc == 2) { if (server.alc.LuaCronFunc) zfree(server.alc.LuaCronFunc); server.alc.LuaCronFunc = zstrdup(argv[1]); return 0; } else if (!strcasecmp(argv[0], "basedir") && argc == 2) { if (server.alc.Basedir) zfree(server.alc.Basedir); server.alc.Basedir = zstrdup(argv[1]); return 0; } else if (!strcasecmp(argv[0], "lua_output_start") && argc == 2) { if (server.alc.OutputLuaFunc_Start) { zfree(server.alc.OutputLuaFunc_Start); } server.alc.OutputLuaFunc_Start = zstrdup(argv[1]); return 0; } else if (!strcasecmp(argv[0], "lua_output_cnames") && argc == 2) { if (server.alc.OutputLuaFunc_Cnames) { zfree(server.alc.OutputLuaFunc_Cnames); } server.alc.OutputLuaFunc_Cnames = zstrdup(argv[1]); return 0; } else if (!strcasecmp(argv[0], "lua_output_row") && argc == 2) { if (server.alc.OutputLuaFunc_Row) zfree(server.alc.OutputLuaFunc_Row); server.alc.OutputLuaFunc_Row = zstrdup(argv[1]); return 0; } else if (!strcasecmp(argv[0], "outputmode") && argc >= 2) { if (!strcasecmp(argv[1], "embedded")) { server.alc.OutputMode = OUTPUT_EMBEDDED; } else if (!strcasecmp(argv[1], "pure_redis")) { server.alc.OutputMode = OUTPUT_PURE_REDIS; } else if (!strcasecmp(argv[1], "normal")) { server.alc.OutputMode = OUTPUT_NORMAL; } else if (!strcasecmp(argv[1], "lua")) { if (!server.alc.OutputLuaFunc_Start || !server.alc.OutputLuaFunc_Cnames || !server.alc.OutputLuaFunc_Row) { char *err = "OUTPUTMODE lua requires [lua_output_start, "\ "lua_output_cnames, lua_output_row]"; fprintf(stderr, "%s\n", err); return -1; } server.alc.OutputMode = OUTPUT_LUA; } else { char *err = "argument must be 'embedded', 'pure_redis' or 'normal'"; fprintf(stderr, "%s\n", err); return -1; } return 0; } else if (!strcasecmp(argv[0], "webserver_mode") && argc == 2) { if ((server.alc.WebServerMode = yesnotoi(argv[1])) == -1) { char *err = "argument must be 'yes' or 'no'"; fprintf(stderr, "%s\n", err); return -1; } #if 0 //TODO webserver_mode & rest_api_mode can run in tandem??? right??? if (server.alc.RestAPIMode != -1) { char *err = "CHOOSE: webserver_mode OR rest_api_mode "; fprintf(stderr, "%s\n", err); return -1; } #endif return 0; } else if (!strcasecmp(argv[0], "webserver_index_function") && argc == 2) { if (server.alc.WebServerIndexFunc) zfree(server.alc.WebServerIndexFunc); server.alc.WebServerIndexFunc = zstrdup(argv[1]); return 0; } else if (!strcasecmp(argv[0], "webserver_whitelist_address") && argc == 2) { if (!inet_aton(argv[1], &server.alc.WS_WL_Addr)) { fprintf(stderr, "ERR: webserver_whitelist_address: %s\n", argv[1]); return -1; } computeWS_WL_MinMax(); return 0; } else if (!strcasecmp(argv[0], "webserver_whitelist_netmask") && argc == 2) { if (!inet_aton(argv[1], &server.alc.WS_WL_Mask)) { fprintf(stderr, "ERR: webserver_whitelist_netmask: %s\n", argv[1]); return -1; } computeWS_WL_MinMax(); return 0; } else if (!strcasecmp(argv[0], "rest_api_mode") && argc == 2) { int yn = yesnotoi(argv[1]); if (yn == -1) { char *err = "argument must be 'yes' or 'no'"; fprintf(stderr, "%s\n", err); return -1; } server.alc.RestAPIMode = yn ? 1 : -1; #if 0 //TODO webserver_mode & rest_api_mode can run in tandem??? right??? if (server.alc.WebServerMode != -1) { char *err = "CHOOSE: webserver_mode OR rest_api_mode "; fprintf(stderr, "%s\n", err); return -1; } #endif return 0; } else if (!strcasecmp(argv[0],"sqlappendonly") && argc == 2) { if (!strcasecmp(argv[1], "no")) { server.appendonly = 0; } else if (!strcasecmp(argv[1], "yes")) { server.appendonly = 1; } else if (!strcasecmp(argv[1], "mysql")) { server.appendonly = 1; server.alc.SQL_AOF_MYSQL = 1; } else { fprintf(stderr, "argument must be 'yes', 'no' or 'mysql;\n"); return -1; } server.alc.SQL_AOF = 1; return 0; } return 1; }
/* I agree, this is a very rudimental way to load a configuration... will improve later if the config gets more complex */ void loadServerConfig(char *filename) { FILE *fp; char buf[REDIS_CONFIGLINE_MAX+1], *err = NULL; int linenum = 0; sds line = NULL; if (filename[0] == '-' && filename[1] == '\0') fp = stdin; else { if ((fp = fopen(filename,"r")) == NULL) { redisLog(REDIS_WARNING, "Fatal error, can't open config file '%s'", filename); exit(1); } } while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) { sds *argv; int argc, j; linenum++; line = sdsnew(buf); line = sdstrim(line," \t\r\n"); /* Skip comments and blank lines*/ if (line[0] == '#' || line[0] == '\0') { sdsfree(line); continue; } /* Split into arguments */ argv = sdssplitargs(line,&argc); sdstolower(argv[0]); /* Execute config directives */ if (!strcasecmp(argv[0],"timeout") && argc == 2) { server.maxidletime = atoi(argv[1]); if (server.maxidletime < 0) { err = "Invalid timeout value"; goto loaderr; } } else if (!strcasecmp(argv[0],"port") && argc == 2) { server.port = atoi(argv[1]); if (server.port < 0 || server.port > 65535) { err = "Invalid port"; goto loaderr; } } else if (!strcasecmp(argv[0],"bind") && argc == 2) { server.bindaddr = zstrdup(argv[1]); } else if (!strcasecmp(argv[0],"formula") && argc == 2) { void *val = loadfm(argv[1]); if(!val) goto loaderr; int retval = dictAdd(server.fms, sdsnew(argv[1]), val); if(retval != DICT_OK) goto loaderr; } else if (!strcasecmp(argv[0],"dir") && argc == 2) { if (chdir(argv[1]) == -1) { redisLog(REDIS_WARNING,"Can't chdir to '%s': %s", argv[1], strerror(errno)); exit(1); } } else if (!strcasecmp(argv[0],"loglevel") && argc == 2) { if (!strcasecmp(argv[1],"debug")) server.verbosity = REDIS_DEBUG; else if (!strcasecmp(argv[1],"verbose")) server.verbosity = REDIS_VERBOSE; else if (!strcasecmp(argv[1],"notice")) server.verbosity = REDIS_NOTICE; else if (!strcasecmp(argv[1],"warning")) server.verbosity = REDIS_WARNING; else { err = "Invalid log level. Must be one of debug, notice, warning"; goto loaderr; } } else if (!strcasecmp(argv[0],"logfile") && argc == 2) { FILE *logfp; server.logfile = zstrdup(argv[1]); if (!strcasecmp(server.logfile,"stdout")) { zfree(server.logfile); server.logfile = NULL; } if (server.logfile) { /* Test if we are able to open the file. The server will not * be able to abort just for this problem later... */ logfp = fopen(server.logfile,"a"); if (logfp == NULL) { err = sdscatprintf(sdsempty(), "Can't open the log file: %s", strerror(errno)); goto loaderr; } fclose(logfp); } } else if (!strcasecmp(argv[0],"databases") && argc == 2) { server.dbnum = atoi(argv[1]); if (server.dbnum < 1) { err = "Invalid number of databases"; goto loaderr; } } else if (!strcasecmp(argv[0],"maxclients") && argc == 2) { server.maxclients = atoi(argv[1]); } else if (!strcasecmp(argv[0],"daemonize") && argc == 2) { if ((server.daemonize = yesnotoi(argv[1])) == -1) { err = "argument must be 'yes' or 'no'"; goto loaderr; } } else if (!strcasecmp(argv[0],"pidfile") && argc == 2) { zfree(server.pidfile); server.pidfile = zstrdup(argv[1]); } else { err = "Bad directive or wrong number of arguments"; goto loaderr; } for (j = 0; j < argc; j++) sdsfree(argv[j]); zfree(argv); sdsfree(line); } if (fp != stdin) fclose(fp); return; loaderr: fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n"); fprintf(stderr, "Reading the configuration file, at line %d\n", linenum); fprintf(stderr, ">>> '%s'\n", line); fprintf(stderr, "%s\n", err); exit(1); }