/* * ser2net - A program for allowing telnet connection to serial ports * Copyright (C) 2001 Corey Minyard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* This file holds the code that reads the configuration file and calls the code in dataxfer to actually create all the ports in the configuration file. */ #include #include #include #include #include #include #include #include #include "dataxfer.h" #include "readconfig.h" #include "utils.h" #include "telnet.h" #include "led.h" #ifdef HAVE_OPENIPMI #include #include #endif #define PORT_BUFSIZE 64 /* Default data transfer buffer size */ extern char *config_port; static int config_num = 0; static int lineno = 0; struct longstr_s { char *name; char *str; unsigned int length; enum str_type type; struct longstr_s *next; }; /* All the strings in the system. */ struct longstr_s *longstrs = NULL; static int isoctdigit(char c) { return ((c >= '0') && (c <= '7')); } /* Assumes the value is pre-checked */ static int hexchar_to_val(char c) { if ((c >= '0') && (c <= '9')) return c - '0'; if (isupper(c)) return c - 'A' + 10; return c - 'a' + 10; } /* * Convert \004 and other ESC sequences in place, inspired from * http://stackoverflow.com/questions/17015970/how-does-c-compiler-convert-escape-sequence-to-actual-bytes */ static void translateescapes(char *string, unsigned int *outlen, char **err, char **errpos) { char *ip = string, *op = string; unsigned int cleft = strlen(string); unsigned int iplen; *err = NULL; while (*ip) { cleft--; if (*ip != '\\') { *op++ = *ip++; continue; } if (cleft == 0) goto out_str_in_bslash; iplen = 2; switch (ip[1]) { case '\\': *op = '\\'; break; case 'r': *op = '\r'; break; case 'n': *op = '\n'; break; case 't': *op = '\t'; break; case 'v': *op = '\v'; break; case 'a': *op = '\a'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': if (cleft < 3) goto out_str_in_bslash; if (!isoctdigit(ip[2]) || !isoctdigit(ip[3])) { *err = "Invalid octal sequence"; *errpos = ip - 1; } iplen = 4; *op = (ip[1] -'0') * 64 + (ip[2] - '0') * 8 + (ip[3] - '0'); break; case 'x': if (cleft < 3) goto out_str_in_bslash; if (!isxdigit(ip[2]) || !isxdigit(ip[3])) { *err = "Invalid octal sequence"; *errpos = ip - 1; } iplen = 4; *op = 16 * hexchar_to_val(ip[2]) + hexchar_to_val(ip[3]); break; default: *err = "Unknown escape sequence"; *errpos = ip - 1; return; } ip += iplen; cleft -= iplen; op++; } *outlen = op - string; *op = '\0'; return; out_str_in_bslash: *err = "end of string right after a \\"; *errpos = ""; } /* Parse the incoming string, it may be on multiple lines. */ static void handle_longstr(const char *name, const char *line, enum str_type type) { struct longstr_s *longstr; /* If the user gave an empty string, we get a NULL. */ if (!line) line = ""; longstr = malloc(sizeof(*longstr)); if (!longstr) { syslog(LOG_ERR, "Out of memory handling string on %d", lineno); return; } memset(longstr, 0, sizeof(*longstr)); longstr->type = type; longstr->name = strdup(name); if (!longstr->name) { syslog(LOG_ERR, "Out of memory handling longstr on %d", lineno); goto out_err; } longstr->str = strdup(line); if (!longstr->str) { syslog(LOG_ERR, "Out of memory handling longstr on %d", lineno); goto out_err; } longstr->length = strlen(line); if (longstr->type == CLOSEON) { char *err = NULL, *errpos = NULL; translateescapes(longstr->str, &longstr->length, &err, &errpos); if (err) { syslog(LOG_ERR, "%s (starting at %s) on line %d", err, errpos, lineno); goto out_err; } } longstr->next = longstrs; longstrs = longstr; return; out_err: if (longstr->name) free(longstr->name); if (longstr->str) free(longstr->str); free(longstr); return; } char * find_str(const char *name, enum str_type *type, unsigned int *len) { struct longstr_s *longstr = longstrs; while (longstr) { if (strcmp(name, longstr->name) == 0) { char *rv; /* Note that longstrs can contain \0, so be careful in handling */ if (type) *type = longstr->type; if (len) *len = longstr->length; rv = malloc(longstr->length + 1); if (!rv) return NULL; memcpy(rv, longstr->str, longstr->length + 1); return rv; } longstr = longstr->next; } return NULL; } void free_longstrs(void) { while (longstrs) { struct longstr_s *longstr = longstrs; longstrs = longstrs->next; free(longstr->name); free(longstr->str); free(longstr); } } struct tracefile_s { char *name; char *str; struct tracefile_s *next; }; #if HAVE_DECL_TIOCSRS485 struct rs485conf_s { char *name; struct serial_rs485 conf; struct rs485conf_s *next; }; #endif /* All the tracefiles in the system. */ struct tracefile_s *tracefiles = NULL; static void handle_tracefile(char *name, char *fname) { struct tracefile_s *new_tracefile; new_tracefile = malloc(sizeof(*new_tracefile)); if (!new_tracefile) { syslog(LOG_ERR, "Out of memory handling tracefile on %d", lineno); return; } new_tracefile->name = strdup(name); if (!new_tracefile->name) { syslog(LOG_ERR, "Out of memory handling tracefile on %d", lineno); free(new_tracefile); return; } new_tracefile->str = strdup(fname); if (!new_tracefile->str) { syslog(LOG_ERR, "Out of memory handling tracefile on %d", lineno); free(new_tracefile->name); free(new_tracefile); return; } new_tracefile->next = tracefiles; tracefiles = new_tracefile; } char * find_tracefile(const char *name) { struct tracefile_s *tracefile = tracefiles; while (tracefile) { if (strcmp(name, tracefile->name) == 0) return strdup(tracefile->str); tracefile = tracefile->next; } syslog(LOG_ERR, "Tracefile %s not found, it will be ignored", name); return NULL; } void free_tracefiles(void) { while (tracefiles) { struct tracefile_s *tracefile = tracefiles; tracefiles = tracefiles->next; free(tracefile->name); free(tracefile->str); free(tracefile); } } #if HAVE_DECL_TIOCSRS485 /* All the RS485 configs in the system. */ struct rs485conf_s *rs485confs = NULL; static void handle_rs485conf(char *name, char *str) { struct rs485conf_s *new_rs485conf; uint8_t rts_on_send, rx_during_tx; new_rs485conf = malloc(sizeof(*new_rs485conf)); if (!new_rs485conf) { syslog(LOG_ERR, "Out of memory handling rs485 config on %d", lineno); return; } memset(new_rs485conf, 0, sizeof(*new_rs485conf)); new_rs485conf->name = strdup(name); if (!new_rs485conf->name) { syslog(LOG_ERR, "Out of memory handling rs485 config on %d", lineno); goto out_err; } if (sscanf(str, "%10u:%10u:%1hhu:%1hhu", &new_rs485conf->conf.delay_rts_before_send, &new_rs485conf->conf.delay_rts_after_send, &rts_on_send, &rx_during_tx) != 4) { syslog(LOG_ERR, "Couldn't parse RS485 config on %d", lineno); goto out_err; } /* check, if flags have values 0 or 1 */ if (rts_on_send > 1) { syslog(LOG_ERR, "RTS_ON_SEND parameter can be 0 or 1 on %d", lineno); goto out_err; } if (rx_during_tx > 1) { syslog(LOG_ERR, "RX_DURING_TX parameter can be 0 or 1 on %d", lineno); goto out_err; } new_rs485conf->conf.flags = SER_RS485_ENABLED; if (rts_on_send) { new_rs485conf->conf.flags |= SER_RS485_RTS_ON_SEND; } else { new_rs485conf->conf.flags |= SER_RS485_RTS_AFTER_SEND; } if (rx_during_tx) { new_rs485conf->conf.flags |= SER_RS485_RX_DURING_TX; } new_rs485conf->next = rs485confs; rs485confs = new_rs485conf; return; out_err: if (new_rs485conf->name) free(new_rs485conf->name); free(new_rs485conf); } struct serial_rs485 * find_rs485conf(const char *name) { struct rs485conf_s *new_rs485conf = rs485confs; while (new_rs485conf) { if (strcmp(name, new_rs485conf->name) == 0) return &new_rs485conf->conf; new_rs485conf = new_rs485conf->next; } syslog(LOG_ERR, "RS485 configuration %s not found, it will be ignored", name); return NULL; } void free_rs485confs(void) { while (rs485confs) { struct rs485conf_s *rs485conf = rs485confs; rs485confs = rs485confs->next; free(rs485conf->name); free(rs485conf); } } #else void free_rs485confs(void) { } #endif static int startswith(char *str, const char *test, char **strtok_data) { int len = strlen(test); if ((strncmp(str, test, len) == 0) && (str[len] == ':')) { strtok_r(str, ":", strtok_data); return 1; } return 0; } static int syslog_eprint(struct absout *e, const char *str, ...) { va_list ap; char buf[1024]; va_start(ap, str); vsnprintf(buf, sizeof(buf), str, ap); va_end(ap); syslog(LOG_ERR, "%s on line %d", buf, *((int *) e->data)); return 0; } static struct absout syslog_eout = { .out = syslog_eprint, .data = &lineno }; struct enum_val { char *str; int val; }; static int lookup_enum(struct enum_val *enums, const char *str, int len) { while (enums->str != NULL) { if (len == -1 && strcmp(enums->str, str) == 0) return enums->val; if (strlen(enums->str) == len && strncmp(enums->str, str, len) == 0) return enums->val; enums++; } return -1; } static struct enum_val speed_enums[] = { { "300", 300 }, { "600", 600 }, { "1200", 1200 }, { "2400", 2400 }, { "4800", 4800 }, { "9600", 9600 }, { "19200", 19200 }, { "38400", 38400 }, { "57600", 57600 }, { "115200", 115200 }, { "230400", 230400 }, { "460800", 460800 }, { "500000", 500000 }, { "576000", 576000 }, { "921600", 921600 }, { "1000000",1000000 }, { "1152000",1152000 }, { "1500000",1500000 }, { "2000000",2000000 }, { "2500000",2500000 }, { "3000000",3000000 }, { "3500000",3500000 }, { "4000000",4000000 }, { NULL }, }; int speedstr_to_speed(const char *speed) { return lookup_enum(speed_enums, speed, -1); } struct enum_val parity_enums[] = { { "NONE", PARITY_NONE }, { "EVEN", PARITY_EVEN }, { "ODD", PARITY_ODD }, { "none", PARITY_NONE }, { "even", PARITY_EVEN }, { "odd", PARITY_ODD }, { "MARK", PARITY_MARK }, { "SPACE", PARITY_SPACE }, { "mark", PARITY_MARK }, { "space", PARITY_SPACE }, { NULL } }; #ifdef HAVE_OPENIPMI struct enum_val shared_serial_alert_enums[] = { { "fail", ipmi_sol_serial_alerts_fail }, { "deferred", ipmi_sol_serial_alerts_deferred }, { "succeed", ipmi_sol_serial_alerts_succeed }, { NULL } }; #endif enum parity_vals lookup_parity(const char *str) { return lookup_enum(parity_enums, str, -1); } enum default_type { DEFAULT_INT, DEFAULT_BOOL, DEFAULT_ENUM, DEFAULT_STR }; struct default_data { const char *name; enum default_type type; int min; int max; struct enum_val *enums; union { int intval; char *strval; } val; union { int intval; const char *strval; } def; const char *altname; }; struct default_data defaults[] = { /* serial device only */ { "stopbits", DEFAULT_INT, .min = 1, .max = 2, .def.intval = 1 }, { "databits", DEFAULT_INT, .min = 5, .max = 8, .def.intval = 8 }, { "parity", DEFAULT_ENUM, .enums = parity_enums, .def.intval = PARITY_NONE }, { "xonxoff", DEFAULT_BOOL, .def.intval = 0 }, { "rtscts", DEFAULT_BOOL, .def.intval = 0 }, { "local", DEFAULT_BOOL, .def.intval = 0 }, { "hangup_when_done", DEFAULT_BOOL, .def.intval = 0 }, /* Serial port and SOL */ { "speed", DEFAULT_ENUM, .enums = speed_enums, .def.intval = 9600 }, { "nobreak", DEFAULT_BOOL, .def.intval = 0 }, /* All port types */ { "remctl", DEFAULT_BOOL, .def.intval = 0 }, { "telnet_brk_on_sync",DEFAULT_BOOL,.def.intval = 0 }, { "kickolduser", DEFAULT_BOOL, .def.intval = 0 }, { "chardelay", DEFAULT_BOOL, .def.intval = 1 }, { "chardelay-scale",DEFAULT_INT, .min = 1, .max = 1000, .def.intval = 20 }, { "chardelay-min", DEFAULT_INT, .min = 1, .max = 100000, .def.intval = 1000 }, { "chardelay-max", DEFAULT_INT, .min = 1, .max = 1000000, .def.intval = 20000 }, { "dev-to-net-bufsize", DEFAULT_INT,.min = 1, .max = 65536, .def.intval = PORT_BUFSIZE, .altname = "dev-to-tcp-bufsize" }, { "net-to-dev-bufsize", DEFAULT_INT,.min = 1, .max = 65536, .def.intval = PORT_BUFSIZE, .altname = "tcp-to-dev-bufsize" }, { "max-connections", DEFAULT_INT, .min=1, .max=65536, .def.intval = 1 }, #ifdef HAVE_OPENIPMI /* SOL only */ { "authenticated", DEFAULT_BOOL, .def.intval = 1 }, { "encrypted", DEFAULT_BOOL, .def.intval = 1 }, { "ack-timeout", DEFAULT_INT, .min = 1, .max = INT_MAX, .def.intval = 1000000 }, { "ack-retries", DEFAULT_INT, .min = 1, .max = INT_MAX, .def.intval = 10 }, { "shared-serial-alert", DEFAULT_ENUM, .enums = shared_serial_alert_enums, .def.intval = ipmi_sol_serial_alerts_fail }, { "deassert_CTS_DCD_DSR_on_connect", DEFAULT_BOOL, .def.intval = 0 }, #endif { "remaddr", DEFAULT_STR, .def.strval = "" }, { NULL } }; static void setup_defaults(void) { int i; for (i = 0; defaults[i].name; i++) { if (defaults[i].type == DEFAULT_STR) { if (defaults[i].val.strval) { free(defaults[i].val.strval); defaults[i].val.strval = NULL; } } else { defaults[i].val.intval = defaults[i].def.intval; } } } static int cmp_default_name(struct default_data *def, const char *name) { return (strcmp(def->name, name) == 0 || (def->altname && strcmp(def->altname, name) == 0)); } int find_default_int(const char *name) { int i; for (i = 0; defaults[i].name; i++) { if (cmp_default_name(&defaults[i], name) && defaults[i].type != DEFAULT_STR) return defaults[i].val.intval; } abort(); } char * find_default_str(const char *name) { int i; for (i = 0; defaults[i].name; i++) { if (cmp_default_name(&defaults[i], name) && defaults[i].type == DEFAULT_STR) { const char *s = defaults[i].val.strval; if (!s) s = defaults[i].def.strval; return strdup(s); } } abort(); } static void handle_new_default(const char *name, const char *str) { int i, val, len; char *end, *sval; const char *s; while (isspace(*str)) str++; s = str; while (!isspace(*s) && *s != '\0') s++; if (s == str) { syslog(LOG_ERR, "No default value on %d", lineno); return; } len = s - str; for (i = 0; defaults[i].name; i++) { if (!cmp_default_name(&defaults[i], name)) continue; switch (defaults[i].type) { case DEFAULT_INT: val = strtoul(str, &end, 10); if (end != s) { syslog(LOG_ERR, "Invalid integer value on %d", lineno); return; } if (val < defaults[i].min || val > defaults[i].max) { syslog(LOG_ERR, "Integer value out of range on %d, " "min is %d, max is %d", lineno, defaults[i].min, defaults[i].max); return; } defaults[i].val.intval = val; break; case DEFAULT_BOOL: val = strtoul(str, &end, 10); if (end == s) defaults[i].val.intval = !!val; else if (len == 4 && ((strncmp(str, "true", 4) == 0) || (strncmp(str, "TRUE", 4) == 0))) defaults[i].val.intval = 1; else if (len == 5 && ((strncmp(str, "false", 5) == 0) || (strncmp(str, "FALSE", 5) == 0))) defaults[i].val.intval = 0; else syslog(LOG_ERR, "Invalid integer value on %d", lineno); break; case DEFAULT_ENUM: val = lookup_enum(defaults[i].enums, str, len); if (val == -1) { syslog(LOG_ERR, "Invalid enumeration value on %d", lineno); return; } defaults[i].val.intval = val; break; case DEFAULT_STR: sval = strdup(str); if (!sval) { syslog(LOG_ERR, "Out of memory processing default string on" " line %d", lineno); return; } if (defaults[i].val.strval) free(defaults[i].val.strval); defaults[i].val.strval = sval; break; } return; } syslog(LOG_ERR, "unknown default name '%s' on %d", name, lineno); } /* * This rather complicated variable is used to scan the string for * ":off", ":telnet", ":raw:", or ":rawlp:". It's a basic state * machine where if a character in the first string "c" matches the * current character, you go to the state machine index in the * corresponding location giving by the character in string "next". * So ":raw:" would see the ":" in state zero and go to state 1. Then * the "r" in state 1 would go to state 7. Then "a" in state 7 would * go to state 8, "w" to state name. The ":" has a zero, that means a * match was found. * * This is nasty, but it allows there to be ":" characters in the * portnum so that IPV6 addresses can be specified. */ static struct { char *c; char *next; } scanstate[] = { { ":", "\x01" }, /* 0x00 */ { "tro", "\x02\x07\x0b" }, /* 0x01 */ { "e", "\x03" }, /* 0x02 */ { "l", "\x04" }, /* 0x03 */ { "n", "\x05" }, /* 0x04 */ { "e", "\x06" }, /* 0x05 */ { "t", "\x0d" }, /* 0x06 */ { "a", "\x08" }, /* 0x07 */ { "w", "\x09" }, /* 0x09 */ { ":l", "\x00\x0a" }, /* 0x09 */ { "p", "\x0d" }, /* 0x0a */ { "f", "\x0c" }, /* 0x0b */ { "f", "\x0d" }, /* 0x0c */ { ":", "\x00" } /* 0x0d */ }; static char * scan_for_state(char *str) { int s = 0; char *b = str; for (; *str; str++) { int i; for (i = 0; scanstate[s].c[i]; i++) { if (scanstate[s].c[i] == *str) break; } if (scanstate[s].c[i]) { s = scanstate[s].next[i]; if (s == 0) return b; } else { s = 0; b = str + 1; } } return NULL; } int handle_config_line(char *inbuf, int len) { char *portnum, *state, *timeout, *devname, *devcfg; char *strtok_data = NULL; if (len == 0) /* Ignore empty lines */ goto out; if (inbuf[0] == '#') /* Ignore comments. */ goto out; if (inbuf[len - 1] == '\\') return len - 1; /* Continued line. */ if (startswith(inbuf, "BANNER", &strtok_data)) { char *name = strtok_r(NULL, ":", &strtok_data); char *str = strtok_r(NULL, "\n", &strtok_data); if (name == NULL) { syslog(LOG_ERR, "No banner name given on line %d", lineno); goto out; } handle_longstr(name, str, BANNER); goto out; } if (startswith(inbuf, "SIGNATURE", &strtok_data)) { char *name = strtok_r(NULL, ":", &strtok_data); char *str = strtok_r(NULL, "\n", &strtok_data); if (name == NULL) { syslog(LOG_ERR, "No signature given on line %d", lineno); goto out; } handle_longstr(name, str, SIGNATURE); goto out; } if (startswith(inbuf, "OPENSTR", &strtok_data)) { char *name = strtok_r(NULL, ":", &strtok_data); char *str = strtok_r(NULL, "\n", &strtok_data); if (name == NULL) { syslog(LOG_ERR, "No open string name given on line %d", lineno); goto out; } handle_longstr(name, str, OPENSTR); goto out; } if (startswith(inbuf, "CLOSESTR", &strtok_data)) { char *name = strtok_r(NULL, ":", &strtok_data); char *str = strtok_r(NULL, "\n", &strtok_data); if (name == NULL) { syslog(LOG_ERR, "No close string name given on line %d", lineno); goto out; } handle_longstr(name, str, CLOSESTR); goto out; } if (startswith(inbuf, "CLOSEON", &strtok_data)) { char *name = strtok_r(NULL, ":", &strtok_data); char *str = strtok_r(NULL, "\n", &strtok_data); if (name == NULL) { syslog(LOG_ERR, "No close on string name given on line %d", lineno); goto out; } handle_longstr(name, str, CLOSEON); goto out; } if (startswith(inbuf, "DEVICE", &strtok_data)) { char *name = strtok_r(NULL, ":", &strtok_data); char *str = strtok_r(NULL, "\n", &strtok_data); if (name == NULL) { syslog(LOG_ERR, "No device name given on line %d", lineno); goto out; } handle_longstr(name, str, DEVNAME); goto out; } if (startswith(inbuf, "TRACEFILE", &strtok_data)) { char *name = strtok_r(NULL, ":", &strtok_data); char *str = strtok_r(NULL, "\n", &strtok_data); if (name == NULL) { syslog(LOG_ERR, "No tracefile name given on line %d", lineno); goto out; } if ((str == NULL) || (strlen(str) == 0)) { syslog(LOG_ERR, "No tracefile given on line %d", lineno); goto out; } handle_tracefile(name, str); goto out; } if (startswith(inbuf, "CONTROLPORT", &strtok_data)) { if (config_port) /* * The control port has already been configured either on the * command line or on a previous statement. Only take the first. */ goto out; config_port = strdup(strtok_r(NULL, "\n", &strtok_data)); if (!config_port) { syslog(LOG_ERR, "Could not allocate memory for CONTROLPORT"); goto out; } goto out; } #if HAVE_DECL_TIOCSRS485 if (startswith(inbuf, "RS485CONF", &strtok_data)) { char *name = strtok_r(NULL, ":", &strtok_data); char *str = strtok_r(NULL, "\n", &strtok_data); if (name == NULL) { syslog(LOG_ERR, "No signature given on line %d", lineno); goto out; } if ((str == NULL) || (strlen(str) == 0)) { syslog(LOG_ERR, "No RS485 configuration given on line %d", lineno); goto out; } handle_rs485conf(name, str); goto out; } #endif if (startswith(inbuf, "DEFAULT", &strtok_data)) { char *name = strtok_r(NULL, ":", &strtok_data); char *str = strtok_r(NULL, "\n", &strtok_data); if (name == NULL) { syslog(LOG_ERR, "No default name given on line %d", lineno); goto out; } handle_new_default(name, str); goto out; } if (startswith(inbuf, "ROTATOR", &strtok_data)) { char *name = strtok_r(NULL, ":", &strtok_data); char *str = strtok_r(NULL, "\n", &strtok_data); if (name == NULL) { syslog(LOG_ERR, "No rotator name given on line %d", lineno); goto out; } add_rotator(name, str, lineno); goto out; } if (startswith(inbuf, "LED", &strtok_data)) { char *name = strtok_r(NULL, ":", &strtok_data); char *str = strtok_r(NULL, "\n", &strtok_data); if (name == NULL) { syslog(LOG_ERR, "No LED name given on line %d", lineno); goto out; } if ((str == NULL) || (strlen(str) == 0)) { syslog(LOG_ERR, "No LED given on line %d", lineno); goto out; } handle_led(name, str, lineno); goto out; } /* Scan for the state. */ state = scan_for_state(inbuf); if (!state) { syslog(LOG_ERR, "No state given on line %d", lineno); goto out; } /* Everything before the state is the port number. */ portnum = inbuf; *state = '\0'; state++; /* Terminate the state. */ inbuf = strchr(state, ':'); /* ":" must be there if scan_for_state works */ *inbuf = '\0'; inbuf++; timeout = strtok_r(inbuf, ":", &strtok_data); if (timeout == NULL) { syslog(LOG_ERR, "No timeout given on line %d", lineno); goto out; } devname = strtok_r(NULL, ":", &strtok_data); if (devname == NULL) { syslog(LOG_ERR, "No device name given on line %d", lineno); goto out; } devcfg = strtok_r(NULL, "", &strtok_data); if (devcfg == NULL) { /* An empty device config is ok. */ devcfg = ""; } portconfig(&syslog_eout, portnum, state, timeout, devname, devcfg, config_num); out: return 0; } void readconfig_init(void) { setup_defaults(); free_longstrs(); free_tracefiles(); #if HAVE_DECL_TIOCSRS485 free_rs485confs(); #endif free_leds(); config_num++; free_rotators(); } /* Read the specified configuration file and call the routine to create the ports. */ int readconfig(char *filename) { FILE *instream = NULL; int linesize = 256; char *inbuf = malloc(linesize); int rv = 0, pos = 0; if (!inbuf) { syslog(LOG_ERR, "Unable to allocate input buffer"); return -1; } lineno = 0; instream = fopen(filename, "r"); if (!instream) { syslog(LOG_ERR, "Unable to open config file '%s': %m", filename); rv = -1; goto out_err; } while (fgets(inbuf + pos, linesize - pos, instream) != NULL) { int len = strlen(inbuf); lineno++; if (len >= (linesize - 1) && inbuf[len - 1] != '\n') { char *new_inbuf; /* We filled up the buffer. Expand the line. */ pos = len; linesize += 256; new_inbuf = realloc(inbuf, linesize); if (!new_inbuf) { syslog(LOG_ERR, "Unable to reallocate input buffer"); rv = -1; goto out_err; } inbuf = new_inbuf; continue; } /* Remove the '\n' */ if (len > 0 && inbuf[len - 1] == '\n') { inbuf[len - 1] = '\0'; len--; } pos = handle_config_line(inbuf, len); } if (pos > 0) handle_config_line(inbuf, strlen(inbuf)); /* Delete anything that wasn't in the new config file. */ clear_old_port_config(config_num); out_err: if (instream) fclose(instream); free(inbuf); return rv; }