static void client_cb(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask) { struct node *client = (struct node *)clientData; /* read */ char buffer[512]; int len = read(fd, buffer, 512); if (len <= 0) { fprintf(stderr, "Client closed: %d.", fd); aeDeleteFileEvent(eventLoop, fd, mask); client_remove(client); return; } /* push to total and split */ int count = 0; client->buffer = sdscatlen(client->buffer, buffer, len); sds *lines = sdssplitlen(client->buffer, sdslen(client->buffer), "\n", 1, &count); /* translate */ int i; for (i = 0; i < count; i++) { translate(lines[i], client); sdsfree(lines[i]); } free(lines); sdsclear(client->buffer); }
/* Tokenize a given sds, setting a term to zero-length sds if it's * a stopword. A total number of tokens and total number of nonstopwords * will be returned */ static sds *sds_tokenize(sds s, int *len, int *nonstopwords) { int i, l, k; sds *terms; struct stemmer *stmer; *nonstopwords = 0; terms = sdssplitlen(s, sdslen(s), " ", 1, len); if (!terms) return NULL; stmer = create_stemmer(); for (i = 0; i < *len; i++) { sds stemmed = NULL, term = terms[i]; term = sdstrim(term, puncs); l = sdslen(term); sdstolower(term); if (l == 0 || rr_stopwords_check(term)) { sdsclear(term); continue; } *nonstopwords += 1; /* note that the third argument is a zero-based index */ k = stem(stmer, term, l-1); if (k < l-1) { stemmed = sdsnewlen(term, k+1); sdsfree(term); terms[i] = stemmed; } } free_stemmer(stmer); return terms; }
void emit_command_help (const char *plist, FILE *fp) { int i, count; sds *p; if (!(p = sdssplitlen (plist, strlen (plist), ":", 1, &count))) return; for (i = 0; i < count; i++) emit_command_help_from_pattern (p[i], fp); sdsfreesplitres (p, count); }
static void sendLuaFuncReply(cli *c, sds file) { //printf("sendLuaFuncReply\n"); int argc; robj **rargv = NULL; if (!sdslen(file) || !strcmp(file, "/")) { argc = 2; //NOTE: rargv[0] is ignored rargv = zmalloc(sizeof(robj *) * argc); rargv[1] = _createStringObject(server.alc.WebServerIndexFunc); } else if (c->http.post && c->http.req_clen) { int urgc; sds *urgv = sdssplitlen(file, sdslen(file), "/", 1, &urgc); sds pb = c->http.post_body; sds *argv = sdssplitlen(pb, sdslen(pb), "&", 1, &argc); rargv = zmalloc(sizeof(robj *) * (argc + urgc + 1)); for (int i = 0; i < urgc; i++) { rargv[i + 1] = createStringObject(urgv[i], sdslen(urgv[i])); } for (int i = 0; i < argc; i++) { char *x = strchr(argv[i], '='); if (!x) continue; x++; rargv[i + urgc + 1] = createStringObject(x, strlen(x)); } for (int i = 0; i < urgc; i++) sdsfree(urgv[i]); zfree(urgv); zfree(argv); argc += (urgc + 1); //NOTE: rargv[0] is ignored } else { sds *argv = sdssplitlen(file, sdslen(file), "/", 1, &argc); rargv = zmalloc(sizeof(robj *) * (argc + 1)); for (int i = 0; i < argc; i++) { rargv[i + 1] = createStringObject(argv[i], sdslen(argv[i])); } for (int i = 0; i < argc; i++) sdsfree(argv[i]); zfree(argv); argc++; //NOTE: rargv[0] is ignored } if (!luafunc_call(c, argc, rargv)) { robj *resp = luaReplyToHTTPReply(server.lua); SEND_REPLY_FROM_STRING(send_http_reponse_header(c, sdslen(resp->ptr))); if (c->http.get || c->http.post) addReply(c, resp); decrRefCount(resp); } for (int i = 1; i < argc; i++) decrRefCount(rargv[i]); zfree(rargv); CLEAR_LUA_STACK }
static void runCmdInFakeClient(sds s) { //RL4 "runCmdInFakeClient: %s", s); int argc; sds *argv = sdssplitlen(s, sdslen(s), " ", 1, &argc); // FREEME 017 if (!argv) return; if (argc < 1) goto run_cmd_end; redisCommand *cmd = lookupCommand(argv[0]); if (!cmd) goto run_cmd_end; if ((cmd->arity > 0 && cmd->arity > argc) || (argc < -cmd->arity)) goto run_cmd_end; int arity; robj **rargv; if (cmd->arity > 0 || cmd->proc == insertCommand || cmd->proc == sqlSelectCommand || cmd->proc == tscanCommand) { arity = abs(cmd->arity); rargv = zmalloc(sizeof(robj *) * arity); /* ZFREE ME 018 */ for (int j = 0; j < arity - 1; j++) { rargv[j] = createStringObject(argv[j], sdslen(argv[j])); // FREE 019 } sds lastarg = sdsempty(); for (int j = arity - 1; j < argc; j++) { if (j != (arity - 1)) lastarg = sdscatlen(lastarg, " ", 1); lastarg = sdscatlen(lastarg, argv[j], sdslen(argv[j])); } rargv[arity - 1] = createObject(REDIS_STRING, lastarg); // FREE 019 } else { arity = argc; rargv = zmalloc(sizeof(robj *) * arity); /* ZFREE ME 018 */ for (int j = 0; j < arity; j++) { rargv[j] = createStringObject(argv[j], sdslen(argv[j])); // FREE 019 } } redisClient *c = CurrClient; redisClient *fc = rsql_createFakeClient(); /* DESTROY ME 020 */ fc->argv = rargv; fc->argc = arity; fakeClientPipe(c, fc, NULL, 0, &NriFlag, nonRelIndRespHandler, emptyNonRelIndRespHandler); rsql_freeFakeClient(fc); /* DESTROYED 020 */ for (int j = 0; j < arity; j++) decrRefCount(rargv[j]); /* FREED 019 */ zfree(rargv); /* ZFREED 018 */ run_cmd_end: for (int j = 0; j < argc; j++) sdsfree(argv[j]); /* FREED 017 */ zfree(argv); /* FREED 017 */ }
int rmt_tcp_context_connect_addr(tcp_context *tc, const char *addr, int len, const struct timeval *timeout, const char *source_addr) { int ret; int port; sds *ip_port = NULL; int ip_port_count = 0; ip_port = sdssplitlen(addr, len, IP_PORT_SEPARATOR, rmt_strlen(IP_PORT_SEPARATOR), &ip_port_count); if(ip_port == NULL || ip_port_count != 2) { log_error("Error: ip port parsed error"); goto error; } port = rmt_atoi(ip_port[1], sdslen(ip_port[1])); if(rmt_valid_port(port) == 0) { log_error("Error: port is invalid"); goto error; } ret = rmt_tcp_context_connect(tc, ip_port[0], port, timeout, source_addr); if(ret != RMT_OK) { log_error("Error: can't connect to redis master"); goto error; } sdsfreesplitres(ip_port, ip_port_count); ip_port = NULL; ip_port_count = 0; return RMT_OK; error: if(ip_port != NULL) { sdsfreesplitres(ip_port, ip_port_count); } return RMT_ERROR; }
void jsondocvalidateCommand(redisClient *c) { /* args 0-1: ["jsondocvalidate", json] */ sds err = sdsempty(); struct jsonObj *root = yajl_decode(c->argv[1]->ptr, &err); if (!root) { int sz; sds *error_lines = sdssplitlen(err, sdslen(err), "\n", 1, &sz); addReplyMultiBulkLen(c, sz + 1); addReply(c, g.err_parse); for (int i = 0; i < sz; i++) { addReplyBulkCBuffer(c, error_lines[i], sdslen(error_lines[i])); } sdsfreesplitres(error_lines, sz); } else { addReply(c, shared.ok); } sdsfree(err); }
int processLineBuffer(ugClient *c) { char *newline = strstr(c->querybuf, "\r\n"); int argc, j; sds *argv; size_t querylen; /* Nothing to do without a \r\n */ if (newline == NULL) { if (sdslen(c->querybuf) > UG_INLINE_MAX_SIZE) { addReplyErrorFormat(c, "Protocol error: too big inline request"); setProtocolError(c,0); } return UGERR; } /* Split the input buffer up to the \r\n */ querylen = newline-(c->querybuf); argv = sdssplitlen(c->querybuf,(int)querylen," ",1,&argc); /* Leave data after the first line of the query in the buffer */ c->querybuf = sdsrange(c->querybuf,(int)(querylen+2),-1); /* Setup argv array on client structure */ if (c->argv) zfree(c->argv); c->argv = zmalloc(sizeof(robj *)*argc); /* Create redis objects for all arguments. */ for (c->argc = 0, j = 0; j < argc; j++) { if (sdslen(argv[j])) { c->argv[c->argc] = createObject(UG_STRING,argv[j]); c->argc++; } else { sdsfree(argv[j]); } } zfree(argv); return UGOK; }
void processInputBuffer(redisClient *c) { again: /* Before to process the input buffer, make sure the client is not * waitig for a blocking operation such as BLPOP. Note that the first * iteration the client is never blocked, otherwise the processInputBuffer * would not be called at all, but after the execution of the first commands * in the input buffer the client may be blocked, and the "goto again" * will try to reiterate. The following line will make it return asap. */ if (c->flags & REDIS_BLOCKED || c->flags & REDIS_IO_WAIT) return; if (c->bulklen == -1) { /* Read the first line of the query */ char *p = strchr(c->querybuf,'\n'); size_t querylen; if (p) { sds query, *argv; int argc, j; query = c->querybuf; c->querybuf = sdsempty(); querylen = 1+(p-(query)); if (sdslen(query) > querylen) { /* leave data after the first line of the query in the buffer */ c->querybuf = sdscatlen(c->querybuf,query+querylen,sdslen(query)-querylen); } *p = '\0'; /* remove "\n" */ if (*(p-1) == '\r') *(p-1) = '\0'; /* and "\r" if any */ sdsupdatelen(query); /* Now we can split the query in arguments */ argv = sdssplitlen(query,sdslen(query)," ",1,&argc); sdsfree(query); if (c->argv) zfree(c->argv); c->argv = zmalloc(sizeof(robj*)*argc); for (j = 0; j < argc; j++) { if (sdslen(argv[j])) { c->argv[c->argc] = createObject(REDIS_STRING,argv[j]); c->argc++; } else { sdsfree(argv[j]); } } zfree(argv); if (c->argc) { /* Execute the command. If the client is still valid * after processCommand() return and there is something * on the query buffer try to process the next command. */ if (processCommand(c) && sdslen(c->querybuf)) goto again; } else { /* Nothing to process, argc == 0. Just process the query * buffer if it's not empty or return to the caller */ if (sdslen(c->querybuf)) goto again; } return; } else if (sdslen(c->querybuf) >= REDIS_REQUEST_MAX_SIZE) { redisLog(REDIS_VERBOSE, "Client protocol error"); freeClient(c); return; } } else { /* Bulk read handling. Note that if we are at this point the client already sent a command terminated with a newline, we are reading the bulk data that is actually the last argument of the command. */ int qbl = sdslen(c->querybuf); if (c->bulklen <= qbl) { /* Copy everything but the final CRLF as final argument */ c->argv[c->argc] = createStringObject(c->querybuf,c->bulklen-2); c->argc++; c->querybuf = sdsrange(c->querybuf,c->bulklen,-1); /* Process the command. If the client is still valid after * the processing and there is more data in the buffer * try to parse it. */ if (processCommand(c) && sdslen(c->querybuf)) goto again; return; } } }
size_t cetcd_parse_response(char *ptr, size_t size, size_t nmemb, void *userdata) { int len, i; char *key, *val; cetcd_response *resp; cetcd_response_parser *parser; yajl_status status; enum resp_parser_st { request_line_start_st, request_line_end_st, request_line_http_status_start_st, request_line_http_status_st, request_line_http_status_end_st, header_key_start_st, header_key_st, header_key_end_st, header_val_start_st, header_val_st, header_val_end_st, blank_line_st, json_start_st, json_end_st, response_discard_st }; /* Headers we are interested in: * X-Etcd-Index: 14695 * X-Raft-Index: 672930 * X-Raft-Term: 12 * */ parser = userdata; resp = parser->resp; len = size * nmemb; for (i = 0; i < len; ++i) { if (parser->st == request_line_start_st) { if (ptr[i] == ' ') { parser->st = request_line_http_status_start_st; } continue; } if (parser->st == request_line_end_st) { if (ptr[i] == '\n') { parser->st = header_key_start_st; } continue; } if (parser->st == request_line_http_status_start_st) { parser->buf = sdscatlen(parser->buf, ptr+i, 1); parser->st = request_line_http_status_st; continue; } if (parser->st == request_line_http_status_st) { if (ptr[i] == ' ') { parser->st = request_line_http_status_end_st; } else { parser->buf = sdscatlen(parser->buf, ptr+i, 1); continue; } } if (parser->st == request_line_http_status_end_st) { val = parser->buf; parser->http_status = atoi(val); sdsclear(parser->buf); parser->st = request_line_end_st; continue; } if (parser->st == header_key_start_st) { if (ptr[i] == '\r') { ++i; } if (ptr[i] == '\n') { parser->st = blank_line_st; if (parser->http_status >= 300 && parser->http_status < 400) { /*this is a redirection, restart the state machine*/ parser->st = request_line_start_st; break; } continue; } parser->st = header_key_st; } if (parser->st == header_key_st) { parser->buf = sdscatlen(parser->buf, ptr+i, 1); if (ptr[i] == ':') { parser->st = header_key_end_st; } else { continue; } } if (parser->st == header_key_end_st) { parser->st = header_val_start_st; continue; } if (parser->st == header_val_start_st) { if (ptr[i] == ' ') { continue; } parser->st = header_val_st; } if (parser->st == header_val_st) { if (ptr[i] == '\r') { ++i; } if (ptr[i] == '\n') { parser->st = header_val_end_st; } else { parser->buf = sdscatlen(parser->buf, ptr+i, 1); continue; } } if (parser->st == header_val_end_st) { parser->st = header_key_start_st; int count = 0; sds *kvs = sdssplitlen(parser->buf, sdslen(parser->buf), ":", 1, &count); sdsclear(parser->buf); if (count < 2) { sdsfreesplitres(kvs, count); continue; } key = kvs[0]; val = kvs[1]; if (strncmp(key, "X-Etcd-Index", sizeof("X-Etcd-Index")-1) == 0) { resp->etcd_index = atoi(val); } else if (strncmp(key, "X-Raft-Index", sizeof("X-Raft-Index")-1) == 0) { resp->raft_index = atoi(val); } else if (strncmp(key, "X-Raft-Term", sizeof("X-Raft-Term")-1) == 0) { resp->raft_term = atoi(val); } sdsfreesplitres(kvs, count); continue; } if (parser->st == blank_line_st) { if (ptr[i] != '{') { /*not a json response, discard*/ parser->st = response_discard_st; if (resp->err == NULL) { resp->err = calloc(1, sizeof(cetcd_error)); resp->err->ecode = error_response_parsed_failed; resp->err->message = sdsnew("not a json response"); resp->err->cause = sdsnewlen(ptr, len); } continue; } parser->st = json_start_st; cetcd_array_init(&parser->ctx.keystack, 10); cetcd_array_init(&parser->ctx.nodestack, 10); if (parser->http_status != 200 && parser->http_status != 201) { resp->err = calloc(1, sizeof(cetcd_error)); parser->ctx.userdata = resp->err; parser->json = yajl_alloc(&error_callbacks, 0, &parser->ctx); } else { parser->ctx.userdata = resp; parser->json = yajl_alloc(&callbacks, 0, &parser->ctx); } } if (parser->st == json_start_st) { if (yajl_status_ok != yajl_parse(parser->json, (const unsigned char *)ptr + i, len - i)) { parser->st = json_end_st; } else { parser->st = response_discard_st; yajl_free(parser->json); cetcd_array_destory(&parser->ctx.keystack); cetcd_array_destory(&parser->ctx.nodestack); } } if (parser->st == json_end_st) { status = yajl_complete_parse(parser->json); yajl_free(parser->json); cetcd_array_destory(&parser->ctx.keystack); cetcd_array_destory(&parser->ctx.nodestack); /*parse failed, TODO set error message*/ if (status != yajl_status_ok) { if (resp->err == NULL) { resp->err = calloc(1, sizeof(cetcd_error)); resp->err->ecode = error_response_parsed_failed; resp->err->message = sdsnew("http response is invalid json format"); resp->err->cause = sdsnewlen(ptr, len); } return 0; } break; } if (parser->st == response_discard_st) { return len; } } return len; }
void load_conf_from_string(char *config) { char *err = NULL; int linenum = 0, tlines, i; sds *lines; lines = sdssplitlen(config, strlen(config), "\n", 1, &tlines); for(i = 0; i < tlines; i++) { sds *argv; int argc; linenum = i+1; log_debug(">> %0.3d %s", linenum, lines[i]); lines[i] = sdstrim(lines[i], " \t\r\n"); // skip comments and blank lines 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], "name") && argc == 2) { server.name = sdsdup(argv[1]); }else if(!strcasecmp(argv[0], "bind") && argc >= 2) { server.bind_addr_count = argc-1; if((argc-1) > ZR_MAX_BIND_ADDR) { err = "Too many bind address specified."; goto loaderr; } int j; for(j = 0; j < server.bind_addr_count; j++) { server.bind_arr[j].addr_str = sdsnew(argv[j+1]); server.bind_arr[j].addr.sin_family = AF_INET; server.bind_arr[j].addr.sin_addr.s_addr = inet_addr(argv[j+1]); if(server.bind_arr[j].addr.sin_addr.s_addr == INADDR_NONE) { err = sdscatprintf(sdsempty(), "Wrong binding ip address: %s", argv[j+1]); 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], "req-log-uds") && argc == 2) { server.req_log_uds = sdsnew(argv[1]); }else if(!strcasecmp(argv[0], "client-max") && argc == 2) { server.client_max = atoi(argv[1]); if(server.client_max < 0) { err = "Invalid server-connection-max value."; goto loaderr; } } else if (!strcasecmp(argv[0],"tcp-backlog") && argc == 2) { server.tcp_backlog = atoi(argv[1]); if (server.tcp_backlog < 0) { err = "Invalid backlog value"; goto loaderr; } /* } else if (!strcasecmp(argv[0],"rr-counter-buffer") && argc == 2) { server.tcp_backlog = atoi(argv[1]); if (server.tcp_backlog < 0) { err = "Invalid rr-counter-buffer value"; goto loaderr; } */ }else if(!strcasecmp(argv[0], "log-level") && argc == 2) { server.log_level = log_get_level_str(argv[1]); }else if(!strcasecmp(argv[0], "log-file") && argc == 2) { server.log_file = sdsdup(argv[1]); if(!strcasecmp(argv[1], "stdout")) { log_set_out(stdout); }else if(!strcasecmp(argv[1], "stderr")) { log_set_out(stderr); }else { FILE *fd; fd = fopen(argv[1], "a"); if(fd == NULL) { err = sdscatprintf(sdsempty(), "Can't open the log file: %s", strerror(errno)); goto loaderr; } log_set_out(fd); } }else if(!strcasecmp(argv[0], "zookeeper-health-uri") && argc == 2) { struct z_conn_info *zcinfo = z_parse_conn_string(argv[1]); if(zcinfo == NULL) { err = "Invalid zookeeper-health-url(Zookeeper URI) value.\n" // "Zookeeper URI must be as below.\n" // " - zoo://[USER[:PASS]@]HOST_STRING/NODE/PATH\n" // " ex) zoo://192.168.1.2:2181/test/node\n" // " ex) zoo://[email protected]:2181,192.168.1.3:2181/test/node\n" // " ex) zoo://foo:[email protected]:2181,192.168.1.3:2181/test/node\n" ""; goto loaderr; } server.z_health_conn = zcinfo; }else if(!strcasecmp(argv[0], "zookeeper-timeout") && argc == 2) { server.zoo_timeout = atoi(argv[1]); if(server.zoo_timeout < 0) { err = "Invalid zookeeper-timeout value"; goto loaderr; } }else if(!strcasecmp(argv[0], "service") && argc == 3) { if(strlen(argv[1]) > ZR_MAX_SVC_NAME) { err = sdscatprintf(sdsempty(), "Service name is too long, first argument(service name) must be less then %d.", ZR_MAX_SVC_NAME); goto loaderr; } struct z_conn_info *cinfo = z_parse_conn_string(argv[2]); if(!cinfo) { err = "Invalid zookeeper-health-url(Zookeeper URI) value.\n" // "Zookeeper URI must be as below.\n" // " - zoo://[USER[:PASS]@]HOST_STRING/NODE/PATH\n" // " ex) zoo://192.168.1.2:2181/test/node\n" // " ex) zoo://[email protected]:2181,192.168.1.3:2181/test/node\n" // " ex) zoo://foo:[email protected]:2181,192.168.1.3:2181/test/node\n" ""; goto loaderr; } int j; for(j = 0; j < server.svc_count; j++) { struct svc *tsvc = server.svc_arr[j]; if(!sdscmp(argv[1], tsvc->name)) { err = sdscatprintf(sdsempty(), "\"%s\" service name already exists.", argv[1]); goto loaderr; } } struct svc *svc = svc_alloc(argv[1], strlen(argv[1]), server.svc_count); svc->z_conn = cinfo; server.svc_arr[server.svc_count] = svc; server.svc_count++; }else { err = "Bad config directive or wrong number of arguments"; goto loaderr; } sdsfreesplitres(argv,argc); } sdsfreesplitres(lines,tlines); return; loaderr: log_eerr("CONFIG FILE ERROR."); log_eerr("Reading configureation file, at line %d", linenum); log_eerr(">> '%s'", lines[i]); log_eerr("%s", err); exit(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); }
/* ----------------- */ static sds get_bitmap_from_argv (int argc, robj ** argv) { sds bitmap; int i; bitmap = sdsgrowzero (sdsempty (), ARC_KS_SIZE); for (i = 0; i < argc; i++) { int ok, n, j; long long ll, from, to; sds *tokens; sds from_to = (sds) argv[i]->ptr; /* There are two kinds of argument type. * 1. <slotNo> ex) 1024 * 2. <slotNo from>-<slotNo to> ex) 0-2047 */ tokens = sdssplitlen (from_to, sdslen (from_to), "-", 1, &n); if (tokens == NULL) { return NULL; } if (n == 1) { /* Type 1 <slotNo> */ ok = string2ll (tokens[0], sdslen (tokens[0]), &ll); if (!ok) { sdsfreesplitres (tokens, n); return NULL; } from = ll; to = ll; } else if (n == 2) { /* Type 2 <slotNo from>-<slotNo to> */ ok = string2ll (tokens[0], sdslen (tokens[0]), &ll); if (!ok) { sdsfreesplitres (tokens, n); return NULL; } from = ll; ok = string2ll (tokens[1], sdslen (tokens[1]), &ll); if (!ok) { sdsfreesplitres (tokens, n); return NULL; } to = ll; } else { /* not belong to Type 1 or Type 2 */ sdsfreesplitres (tokens, n); return NULL; } sdsfreesplitres (tokens, n); /* range check */ if (from < 0 || to >= ARC_KS_SIZE || from > to) { return NULL; } /* set bit */ for (j = from; j <= to; j++) { bitmapSetBit ((unsigned char *) bitmap, j); } } return bitmap; }
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 { err = "Invalid chunk algorithm"; goto loaderr; } } else if (strcasecmp(argv[0], "chunk-avg-size") == 0 && argc == 2) { destor.chunk_avg_size = atoi(argv[1]); int pwr; for (pwr = 0; destor.chunk_avg_size; pwr++) { destor.chunk_avg_size >>= 1; } destor.chunk_avg_size = 1 << (pwr - 1); } else if (strcasecmp(argv[0], "chunk-max-size") == 0 && argc == 2) {
static bool sendRestAPIReply(cli *c, sds file) { //printf("sendRestAPIReply\n"); int argc; bool ret = 0; sds pb = c->http.post_body; //TODO cat [file,pb] is too much copying sds url = pb ? sdscatprintf(sdsempty(), "%s%s", file, pb) : sdsdup(file); //FREE 156 sds *argv = sdssplitlen(url, sdslen(url), "/", 1, &argc); //FREE 157 rcommand *cmd = lookupCommand(argv[0]); if (!cmd) goto send_rest_end; ret = 1; //printf("sendRestAPIReply: found cmd: %s\n", cmd->name); if ((cmd->arity > 0 && cmd->arity != argc) || (argc < -cmd->arity)) { addReplyErrorFormat(c,"wrong number of arguments for '%s' command", cmd->name); goto send_rest_end; } //NOTE: rest is copy of redis.c processCommand() if (server.maxmemory) freeMemoryIfNeeded(); if (server.maxmemory && (cmd->flags & REDIS_CMD_DENYOOM) && zmalloc_used_memory() > server.maxmemory) { addReplyError(c, "command not allowed when used memory > 'maxmemory'"); goto send_rest_end; } if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0) && cmd->proc != subscribeCommand && cmd->proc != unsubscribeCommand && cmd->proc != psubscribeCommand && cmd->proc != punsubscribeCommand) { addReplyError(c, "only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context"); goto send_rest_end; } if (server.masterhost && server.replstate != REDIS_REPL_CONNECTED && server.repl_serve_stale_data == 0 && cmd->proc != infoCommand && cmd->proc != slaveofCommand) { addReplyError(c, "link with MASTER is down and slave-serve-stale-data is set to no"); goto send_rest_end; } if (server.loading && cmd->proc != infoCommand) { addReply(c, shared.loadingerr); goto send_rest_end; } if (c->flags & REDIS_MULTI && cmd->proc != execCommand && cmd->proc != discardCommand && cmd->proc != multiCommand && cmd->proc != watchCommand) { queueMultiCommand(c, cmd); addReply(c, shared.queued); goto send_rest_end; } listNode *ln; listIter *li; cli *restc = server.alc.RestClient; // 1.) call() cmd in RestClient { // REST CLIENT call robj **rargv = zmalloc(sizeof(robj *) * argc); for (int i = 0; i < argc; i++) { rargv[i] = createStringObject(argv[i], sdslen(argv[i])); } restc->argc = argc; restc->argv = rargv; call(restc, cmd); for (int i = 0; i < argc; i++) decrRefCount(rargv[i]); zfree(rargv); } // 2.) calculate Content-Length from RestClient's response ulong brlen = restc->bufpos; ulong trlen = brlen; if (restc->reply->len) { li = listGetIterator(restc->reply, AL_START_HEAD); while((ln = listNext(li))) { robj *r = ln->value; trlen += sdslen(r->ptr); } listReleaseIterator(li); } bool err = brlen && (*restc->buf == '-'); //TODO check for "+OK" and return 201 w/ no body // 3.) create header w/ Content-Length sds s = err ? send_http404_reponse_header(c, trlen) : send_http200_reponse_header(c, trlen); robj *ho = createObject(REDIS_STRING, s); addReply(c, ho); decrRefCount(ho); // 4.) tack on RestClient's response as HTTP Body if (brlen) { addReplyString(c, restc->buf, brlen); } if (restc->reply->len) { li = listGetIterator(restc->reply, AL_START_HEAD); while((ln = listNext(li))) { robj *r = ln->value; addReply(c, r); } listReleaseIterator(li); } // 5.) reset RestClient restc->bufpos = 0; while (restc->reply->len) { listDelNode(restc->reply, listFirst(restc->reply)); } send_rest_end: sdsfree(url); // FREE 156 for (int i = 0; i < argc; i++) sdsfree(argv[i]); zfree(argv); // FREE 157 return ret; }
static int initFromConfigBuffer (sds config, conf_t * conf) { char *err = NULL; int linenum = 0, totlines, i; sds *lines; lines = sdssplitlen (config, sdslen (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"); /* Skip comments and blank lines */ if (lines[i][0] == '#' || lines[i][0] == '\0') continue; /* Split into arguments */ argv = sdssplitargs (lines[i], &argc); if (argv == NULL) { err = "Unbalanced quotes in configuration line"; goto err; } /* Skip this line if the resulting command vector is empty. */ if (argc == 0) { sdsfreesplitres (argv, argc); continue; } sdstolower (argv[0]); if (!strcasecmp (argv[0], "zookeeper") && argc == 2) { conf->zk_addr = strdup (argv[1]); } else if (!strcasecmp (argv[0], "cluster_name") && argc == 2) { conf->cluster_name = strdup (argv[1]); } else if (!strcasecmp (argv[0], "port") && argc == 2) { conf->port = atoi (argv[1]); if (conf->port < 0 || conf->port > 65535) { sdsfreesplitres (argv, argc); err = "Invalid port"; goto err; } } else if (!strcasecmp (argv[0], "daemonize") && argc == 2) { if (!strcasecmp (argv[1], "yes")) { conf->daemonize = 1; } else if (!strcasecmp (argv[1], "no")) { conf->daemonize = 0; } else { sdsfreesplitres (argv, argc); err = "argument must be 'yes' or 'no'"; goto err; } } else if (!strcasecmp (argv[0], "num_conn_per_gw") && argc == 2) { conf->capi_conf.num_conn_per_gw = atoi (argv[1]); if (conf->capi_conf.num_conn_per_gw < 1 || conf->capi_conf.num_conn_per_gw > 32) { sdsfreesplitres (argv, argc); err = "Invalid num_conn_per_gw value"; goto err; } } else if (!strcasecmp (argv[0], "init_timeout_millis") && argc == 2) { conf->capi_conf.init_timeout_millis = atoi (argv[1]); if (conf->capi_conf.init_timeout_millis < 3000 || conf->capi_conf.init_timeout_millis > 600000) { sdsfreesplitres (argv, argc); err = "Invalid init_timeout_millis value"; goto err; } } else if (!strcasecmp (argv[0], "log_level") && argc == 2) { if (!strcasecmp (argv[1], "NOLOG")) { conf->capi_conf.log_level = ARC_LOG_LEVEL_NOLOG; } else if (!strcasecmp (argv[1], "ERROR")) { conf->capi_conf.log_level = ARC_LOG_LEVEL_ERROR; } else if (!strcasecmp (argv[1], "WARN")) { conf->capi_conf.log_level = ARC_LOG_LEVEL_WARN; } else if (!strcasecmp (argv[1], "INFO")) { conf->capi_conf.log_level = ARC_LOG_LEVEL_INFO; } else if (!strcasecmp (argv[1], "DEBUG")) { conf->capi_conf.log_level = ARC_LOG_LEVEL_DEBUG; } else { sdsfreesplitres (argv, argc); err = "Invalid log_level value"; goto err; } } else if (!strcasecmp (argv[0], "log_file_prefix") && argc == 2) { if (argv[1][0] != '\0') { conf->capi_conf.log_file_prefix = strdup (argv[1]); } } else if (!strcasecmp (argv[0], "max_fd") && argc == 2) { conf->capi_conf.max_fd = atoi (argv[1]); if (conf->capi_conf.max_fd < 1024 || conf->capi_conf.max_fd > 16000) { sdsfreesplitres (argv, argc); err = "Invalid max_fd value"; goto err; } } else if (!strcasecmp (argv[0], "conn_reconnect_millis") && argc == 2) { conf->capi_conf.conn_reconnect_millis = atoi (argv[1]); if (conf->capi_conf.conn_reconnect_millis < 100 || conf->capi_conf.conn_reconnect_millis > 600000) { sdsfreesplitres (argv, argc); err = "Invalid conn_reconnect_millis value"; goto err; } } else if (!strcasecmp (argv[0], "zk_reconnect_millis") && argc == 2) { conf->capi_conf.zk_reconnect_millis = atoi (argv[1]); if (conf->capi_conf.zk_reconnect_millis < 100 || conf->capi_conf.zk_reconnect_millis > 600000) { sdsfreesplitres (argv, argc); err = "Invalid zk_reconnect_millis value"; goto err; } } else if (!strcasecmp (argv[0], "zk_session_timeout_millis") && argc == 2) { conf->capi_conf.zk_session_timeout_millis = atoi (argv[1]); if (conf->capi_conf.zk_session_timeout_millis < 1000 || conf->capi_conf.zk_session_timeout_millis > 600000) { sdsfreesplitres (argv, argc); err = "Invalid zk_session_timeout_millis value"; goto err; } } else if (!strcasecmp (argv[0], "local_proxy_query_timeout_millis") && argc == 2) { conf->query_timeout_millis = atoi (argv[1]); if (conf->query_timeout_millis < 1000 || conf->query_timeout_millis > 600000) { sdsfreesplitres (argv, argc); err = "Invalid local_proxy_query_timeout_millis"; goto err; } } else { sdsfreesplitres (argv, argc); err = "Bad directive or wrong number of arguments"; goto err; } sdsfreesplitres (argv, argc); } sdsfreesplitres (lines, totlines); return 0; err: fprintf (stderr, "\n*** FATAL CONFIG FILE ERROR ***\n"); fprintf (stderr, "Reading the configuration file, at line %d\n", linenum); fprintf (stderr, ">>> '%s'\n", lines[i]); fprintf (stderr, "%s\n", err); sdsfreesplitres (lines, totlines); return -1; }