view sip-in/readconf.c @ 190:62ecc0aa081f

sip-manual-out: add state machine for capturing full IS messages
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 17 Mar 2023 10:56:43 -0800
parents 7e04d28fae8b
children
line wrap: on
line source

/*
 * In this module we implement the reading of /var/gsm/themwi-sip-in.cfg:
 * the main settings are bind-ip and bind-port, but we also have some
 * optional config settings.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

struct in_addr sip_bind_ip;
unsigned sip_bind_port;
int cfg_use_100rel;
int cfg_force_pcma;
unsigned cfg_retrans_timeout = 500;
unsigned cfg_retrans_count = 10;
unsigned max_forwards = 70;
unsigned sip_linger_acked = 5;
unsigned sip_linger_gotbye = 30;
unsigned sip_linger_error = 180;

static char config_file_pathname[] = "/var/gsm/themwi-sip-in.cfg";

struct parse_state {
	int lineno;
	int set_mask;
};

static void
require_one_arg(st, kw, arg)
	struct parse_state *st;
	char *kw, *arg;
{
	char *cp;

	if (*arg == '\0' || *arg == '#') {
inv_syntax:	fprintf(stderr,
			"%s line %d: %s setting requires one argument\n",
			config_file_pathname, st->lineno, kw);
		exit(1);
	}
	for (cp = arg; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	while (isspace(*cp))
		cp++;
	if (*cp != '\0' && *cp != '#')
		goto inv_syntax;
}

static void
handle_ip(st, kw, arg, var)
	struct parse_state *st;
	char *kw, *arg;
	struct in_addr *var;
{
	require_one_arg(st, kw, arg);
	var->s_addr = inet_addr(arg);
	if (var->s_addr == INADDR_NONE) {
		fprintf(stderr,
			"%s line %d: invalid IP address argument \"%s\"\n",
			config_file_pathname, st->lineno, arg);
		exit(1);
	}
}

static void
handle_num(st, kw, arg, var)
	struct parse_state *st;
	char *kw, *arg;
	unsigned *var;
{
	char *endp;

	require_one_arg(st, kw, arg);
	*var = strtoul(arg, &endp, 10);
	if (*endp) {
		fprintf(stderr, "%s line %d: invalid numeric argument \"%s\"\n",
			config_file_pathname, st->lineno, arg);
		exit(1);
	}
}

static void
handle_bool(st, kw, arg, var)
	struct parse_state *st;
	char *kw, *arg;
	int *var;
{
	require_one_arg(st, kw, arg);
	if (!strcmp(arg, "true") || !strcmp(arg, "on") || !strcmp(arg, "yes")
	    || !strcmp(arg, "1")) {
		*var = 1;
		return;
	}
	if (!strcmp(arg, "false") || !strcmp(arg, "off") || !strcmp(arg, "no")
	    || !strcmp(arg, "0")) {
		*var = 0;
		return;
	}
	fprintf(stderr, "%s line %d: invalid boolean argument \"%s\"\n",
		config_file_pathname, st->lineno, arg);
	exit(1);
}

static void
handle_retrans_conf(st, kw, arg)
	struct parse_state *st;
	char *kw, *arg;
{
	char *cp = arg;

	if (!isdigit(*cp)) {
inv:		fprintf(stderr,
		"%s line %d: %s setting requires two numeric arguments\n",
			config_file_pathname, st->lineno, kw);
		exit(1);
	}
	cfg_retrans_timeout = strtoul(cp, &cp, 10);
	while (isspace(*cp))
		cp++;
	if (!isdigit(*cp))
		goto inv;
	cfg_retrans_count = strtoul(cp, &cp, 10);
	while (isspace(*cp))
		cp++;
	if (*cp != '\0' && *cp != '#')
		goto inv;
}

static void
process_line(st, line)
	struct parse_state *st;
	char *line;
{
	char *cp, *kw;
	void (*handler)(), *var;
	int set_id;

	if (!index(line, '\n')) {
		fprintf(stderr, "%s line %d: too long or missing newline\n",
			config_file_pathname, st->lineno);
		exit(1);
	}
	for (cp = line; isspace(*cp); cp++)
		;
	if (*cp == '\0' || *cp == '#')
		return;
	for (kw = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	if (!strcmp(kw, "bind-ip")) {
		handler = handle_ip;
		var = &sip_bind_ip;
		set_id = 1;
	} else if (!strcmp(kw, "bind-port")) {
		handler = handle_num;
		var = &sip_bind_port;
		set_id = 2;
	} else if (!strcmp(kw, "use-100rel")) {
		handler = handle_bool;
		var = &cfg_use_100rel;
		set_id = 0;
	} else if (!strcmp(kw, "force-pcma-codec")) {
		handler = handle_bool;
		var = &cfg_force_pcma;
		set_id = 0;
	} else if (!strcmp(kw, "sip-udp-retrans")) {
		handler = handle_retrans_conf;
		var = (void *) 0;
		set_id = 0;
	} else if (!strcmp(kw, "sip-linger-acked")) {
		handler = handle_num;
		var = &sip_linger_acked;
		set_id = 0;
	} else if (!strcmp(kw, "sip-linger-got-bye")) {
		handler = handle_num;
		var = &sip_linger_gotbye;
		set_id = 0;
	} else if (!strcmp(kw, "sip-linger-error")) {
		handler = handle_num;
		var = &sip_linger_error;
		set_id = 0;
	} else if (!strcmp(kw, "max-forwards")) {
		handler = &handle_num;
		var = &max_forwards;
		set_id = 0;
	} else {
		fprintf(stderr, "%s line %d: non-understood keyword \"%s\"\n",
			config_file_pathname, st->lineno, kw);
		exit(1);
	}
	if (st->set_mask & set_id) {
		fprintf(stderr, "%s line %d: duplicate %s setting\n",
			config_file_pathname, st->lineno, kw);
		exit(1);
	}
	while (isspace(*cp))
		cp++;
	handler(st, kw, cp, var);
	st->set_mask |= set_id;
}

read_config_file()
{
	FILE *inf;
	struct parse_state pst;
	char linebuf[256];

	inf = fopen(config_file_pathname, "r");
	if (!inf) {
		perror(config_file_pathname);
		exit(1);
	}
	pst.set_mask = 0;
	for (pst.lineno = 1; fgets(linebuf, sizeof linebuf, inf); pst.lineno++)
		process_line(&pst, linebuf);
	fclose(inf);
	if (pst.set_mask != 3) {
		fprintf(stderr, "error: %s did not set all required settings\n",
			config_file_pathname);
		exit(1);
	}
}