1078 lines
26 KiB
C
1078 lines
26 KiB
C
/*
|
|
* ser2net - A program for allowing telnet connection to serial ports
|
|
* Copyright (C) 2001 Corey Minyard <minyard@acm.org>
|
|
*
|
|
* 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 <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <syslog.h>
|
|
#include <stdarg.h>
|
|
#include <limits.h>
|
|
|
|
#include "dataxfer.h"
|
|
#include "readconfig.h"
|
|
#include "utils.h"
|
|
#include "telnet.h"
|
|
#include "led.h"
|
|
|
|
#ifdef HAVE_OPENIPMI
|
|
#include <OpenIPMI/ipmi_conn.h>
|
|
#include <OpenIPMI/ipmi_sol.h>
|
|
#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;
|
|
}
|
|
|