From 639ec8beb77307ced22522411ed35d7c394639df Mon Sep 17 00:00:00 2001 From: XANTRONIX Development Date: Tue, 1 Sep 2020 16:38:02 -0500 Subject: [PATCH] Implement bin/pattyd.c Changes: * Implement src/conf.c, patty_conf_read(), to read a configuration file to support a OpenBSD-style configuration file format * Implement bin/pattyd.c to use patty_conf_read() to read a configuration file and apply its settings to a patty_daemon object as it is read; also implement a --standalone|-s flag to allow the user to start a patty server without having to write a configuration file * Refactor patty_daemon_if_add() to accept a patty_ax25_if object; this is necessary as bin/pattyd.c needs to be able to validate the addresses given in a configuration file 'if' statement * Refactor patty_ax25_server_new() to no longer accept a client socket path; instead, patty_ax25_server_start() now accepts the client socket path * Remove the client socket 'path' member from patty_ax25_server in src/server.c * Refactor patty_kiss_tnc_new() to accept only one argument, a patty_kiss_tnc_info object containing flags and settings needed to open a device, use an existing file descriptor, or change termios settings as appropriate * Remove patty_kiss_tnc_new_fd(), as its functionality now exists in patty_kiss_tnc_new() itself * Add a 'flags' field to patty_kiss_tnc_info; use this bit field to determine whether a path or file descriptor is provided by the caller * Make patty_ax25_if_new() accept an interface name argument, as names are explicitly required when declaring new interfaces in configuration files * Make patty_kiss_tnc_new() able to accept /dev/ptmx as a device name, regardless of whether this character device exists on a given platform; when provided, a pseudo TTY pair is allocated with openpty() * Refactor examples/ax25dump.c to use the new patty_kiss_tnc_new() calling convention * Refactor examples/decode.c to use the new patty_kiss_tnc_new() calling convention * Remove examples/daemon.c in favor of bin/pattyd.c * Rename examples/patty.conf to examples/pattyd.conf; modify to provide values which would actually function --- Makefile | 2 + bin/Makefile | 23 ++ bin/pattyd.c | 567 ++++++++++++++++++++++++++++++++++++ examples/Makefile | 2 +- examples/ax25dump.c | 16 +- examples/daemon.c | 157 ---------- examples/decode.c | 14 +- examples/patty.conf | 6 - examples/pattyd.conf | 5 + include/patty/ax25/if.h | 17 +- include/patty/ax25/server.h | 4 +- include/patty/conf.h | 22 ++ include/patty/daemon.h | 6 +- include/patty/kiss.h | 14 +- src/Makefile | 4 +- src/conf.c | 358 +++++++++++++++++++++++ src/daemon.c | 52 ++-- src/if.c | 19 +- src/kiss.c | 157 +++++----- src/server.c | 10 +- src/sock.c | 11 +- 21 files changed, 1127 insertions(+), 339 deletions(-) create mode 100644 bin/Makefile create mode 100644 bin/pattyd.c delete mode 100644 examples/daemon.c delete mode 100644 examples/patty.conf create mode 100644 examples/pattyd.conf create mode 100644 include/patty/conf.h create mode 100644 src/conf.c diff --git a/Makefile b/Makefile index dcbc342..a600bee 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ all: $(MAKE) -C src all + $(MAKE) -C bin all $(MAKE) -C examples all install: @@ -7,4 +8,5 @@ install: clean: $(MAKE) -C src clean + $(MAKE) -C bin clean $(MAKE) -C examples clean diff --git a/bin/Makefile b/bin/Makefile new file mode 100644 index 0000000..13287e0 --- /dev/null +++ b/bin/Makefile @@ -0,0 +1,23 @@ +include ../mk/build.mk + +CC = $(CROSS)cc + +INCLUDE_PATH = ../include + +CFLAGS += -I$(INCLUDE_PATH) +LDFLAGS = -L../src -lpatty + +PROGRAMS = pattyd + +OBJS = pattyd.o + +all: $(PROGRAMS) + +pattyd: pattyd.o + $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) + +$(OBJS): %.o: %.c + $(CC) $(CFLAGS) -c $< + +clean: + $(RM) $(PROGRAMS) diff --git a/bin/pattyd.c b/bin/pattyd.c new file mode 100644 index 0000000..e566743 --- /dev/null +++ b/bin/pattyd.c @@ -0,0 +1,567 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DEFAULT_CONFIG_FILE "/etc/patty/pattyd.conf" +#define DEFAULT_IFNAME "kiss0" + +static int usage(int argc, char **argv, const char *message, ...) { + if (message != NULL) { + va_list args; + + va_start(args, message); + vfprintf(stderr, message, args); + fprintf(stderr, "\n"); + va_end(args); + } + + fprintf(stderr, "usage: %s [-f] [-c pattyd.conf]\n" + " %s [-f] -s patty.sock MYCALL /dev/ttyXYZ [tioflags ...]\n", + argv[0], argv[0]); + + return EX_USAGE; +} + +static int handle_sock(patty_daemon *daemon, + int lineno, + int argc, + char **argv) { + if (argc != 2) { + fprintf(stderr, "Invalid arguments for 'sock' on line %d\n", lineno); + + goto error_invalid_args; + } + + if (patty_daemon_set_sock_path(daemon, argv[1]) < 0) { + fprintf(stderr, "Line %d: unable to set socket path to %s: %s", + lineno, argv[1], strerror(errno)); + + goto error_set_sock_path; + } + + return 0; + +error_set_sock_path: +error_invalid_args: + return -1; +} + +static int handle_pid(patty_daemon *daemon, + int lineno, + int argc, + char **argv) { + if (argc != 2) { + fprintf(stderr, "Invalid arguments for 'sock' on line %d\n", lineno); + + goto error_invalid_args; + } + + if (patty_daemon_set_pidfile(daemon, argv[1]) < 0) { + fprintf(stderr, "Line %d: Unable to set pidfile to %s: %s", + lineno, argv[1], strerror(errno)); + + goto error_set_pidfile; + } + + return 0; + +error_set_pidfile: +error_invalid_args: + return -1; +} + +struct if_context { + patty_daemon *daemon; + patty_ax25_addr addr; + patty_kiss_tnc_info info; +}; + +static int handle_if_ax25(struct if_context *ctx, + int lineno, + char *opt, + char *value) { + if (patty_ax25_pton(value, &ctx->addr) < 0) { + fprintf(stderr, "Line %d: Invalid interface address '%s'\n", + lineno, value); + + goto error_pton; + } + + return 0; + +error_pton: + return -1; +} + +static int handle_if_kiss(struct if_context *ctx, + int lineno, + char *opt, + char *value) { + ctx->info.flags |= PATTY_KISS_TNC_DEVICE; + ctx->info.device = value; + + return 0; +} + +static int handle_if_baud(struct if_context *ctx, + int lineno, + char *opt, + char *value) { + ctx->info.flags |= PATTY_KISS_TNC_BAUD; + ctx->info.baud = atoi(value); + + return 0; +} + +static int handle_if_flow(struct if_context *ctx, + int lineno, + char *opt, + char *value) { + if (strcmp(value, "xonxoff") == 0) { + ctx->info.flow = PATTY_KISS_TNC_FLOW_XONXOFF; + } else if (strcmp(value, "crtscts") == 0) { + ctx->info.flow = PATTY_KISS_TNC_FLOW_CRTSCTS; + } else { + fprintf(stderr, "Line %d: Invalid flow control '%s'\n", + lineno, value); + + goto error_invalid_flow; + } + + ctx->info.flags |= PATTY_KISS_TNC_FLOW; + + return 0; + +error_invalid_flow: + return -1; +} + +struct if_opt_handler { + char *name; + int (*func)(struct if_context *, int, char *, char *); +}; + +static struct if_opt_handler if_opt_handlers[] = { + { "ax25", handle_if_ax25 }, + { "kiss", handle_if_kiss }, + { "baud", handle_if_baud }, + { "flow", handle_if_flow }, + { NULL, NULL } +}; + +static int handle_if_opt(struct if_context *ctx, + int lineno, + char *opt, + char *value) { + int i; + + for (i=0; if_opt_handlers[i].name; i++) { + if (strcmp(opt, if_opt_handlers[i].name) == 0) { + return if_opt_handlers[i].func(ctx, lineno, opt, value); + } + } + + fprintf(stderr, "Line %d: Invalid interface option '%s'\n", + lineno, opt); + + return -1; +} + +static int handle_if(patty_daemon *daemon, + int lineno, + int argc, + char **argv) { + char *name; + struct if_context ctx; + patty_ax25_if *iface; + + int i; + + if (argc < 2) { + fprintf(stderr, "Line %d: No interface name provided\n", lineno); + + goto error_no_ifname; + } + + if (argc < 3) { + fprintf(stderr, "Line %d: No interface options provided\n", lineno); + + goto error_no_options; + } + + if (argc % 2 != 0) { + fprintf(stderr, "Line %d: Invalid number of values provided\n", lineno); + + goto error_invalid_values; + } + + memset(&ctx, '\0', sizeof(ctx)); + ctx.daemon = daemon; + + name = argv[1]; + + for (i=2; itnc); + char *pty; + + if (isatty(fd) && (pty = ptsname(fd)) != NULL) { + printf("if %s pty %s\n", name, pty); + } + } + + if (patty_daemon_if_add(daemon, iface) < 0) { + fprintf(stderr, "Line %d: Unable to create interface %s: %s\n", + lineno, name, strerror(errno)); + + goto error_daemon_if_add; + } + + return 0; + +error_daemon_if_add: + patty_ax25_if_destroy(iface); + +error_if_new: +error_handle_if_opt: +error_invalid_values: +error_no_options: +error_no_ifname: + return -1; +} + +static int handle_route(patty_daemon *daemon, + int lineno, + int argc, + char **argv) { + if (argc < 2) { + fprintf(stderr, "Line %d: Invalid route declaration\n", lineno); + + goto error_invalid_route; + } + + if (strcmp(argv[1], "default") == 0) { + if (argc != 4 || strcmp(argv[2], "if") != 0) { + fprintf(stderr, "Line %d: Invalid default route declaration\n", + lineno); + + goto error_invalid_route; + } + + if (patty_daemon_route_add_default(daemon, argv[3]) < 0) { + fprintf(stderr, "Line %d: Unable to add default route for interface %s: %s\n", + lineno, argv[3], strerror(errno)); + + goto error_daemon_route_add; + } + } else if (strcmp(argv[1], "station") == 0) { + if (argc < 7 || strcmp(argv[3], "if") != 0 || strcmp(argv[5], "path") != 0) { + fprintf(stderr, "Line %d: Invalid station route declaration\n", + lineno); + + goto error_invalid_route; + } + + if (patty_daemon_route_add(daemon, + argv[4], + argv[2], + (const char **)&argv[6], + argc - 6) < 0) { + fprintf(stderr, "Line %d: Unable to add route for interface %s: %s\n", + lineno, argv[4], strerror(errno)); + + goto error_daemon_route_add; + } + } else { + fprintf(stderr, "Line %d: Invalid route type '%s'\n", + lineno, argv[1]); + + goto error_invalid_route; + } + + return 0; + +error_daemon_route_add: +error_invalid_route: + return -1; +} + +struct config_handler { + const char *name; + int (*func)(patty_daemon *, int, int, char **); +}; + +struct config_handler handlers[] = { + { "sock", handle_sock }, + { "pid", handle_pid }, + { "if", handle_if }, + { "route", handle_route }, + { NULL, NULL } +}; + +static int handle_config_line(patty_conf_file *file, + patty_list *line, + void *ctx) { + patty_daemon *daemon = ctx; + patty_list_item *item = line->first; + + int argc = (int)line->length, + i = 0, + ret = 0, + lineno = 0; + + char **argv; + + if (!item) { + return 0; + } + + if ((argv = malloc((argc + 1) * sizeof(char *))) == NULL) { + goto error_malloc_argv; + } + + while (item) { + patty_conf_token *token = item->value; + + if (lineno == 0) { + lineno = token->lineno; + } + + argv[i++] = token->text; + + item = item->next; + } + + argv[argc] = NULL; + + for (i=0; handlers[i].name; i++) { + if (strcmp(argv[0], handlers[i].name) == 0) { + ret = handlers[i].func(daemon, lineno, argc, argv); + + goto done; + } + } + + fprintf(stderr, "Unknown configuration value '%s'\n", argv[0]); + +done: + free(argv); + + return ret; + +error_malloc_argv: + return -1; +} + +static int handle_simple_config(patty_daemon *daemon, + int argc, + char *argv0, + char **argv) { + patty_ax25_if *iface; + patty_ax25_addr addr; + + patty_kiss_tnc_info info = { + .flags = PATTY_KISS_TNC_DEVICE, + .device = argv[2] + }; + + int i; + + for (i=3; i= '0' && argv[i][0] <= '9') { + int baud = atoi(argv[i]); + + info.flags |= PATTY_KISS_TNC_BAUD; + info.baud = baud; + } else { + fprintf(stderr, "%s: Invalid device setting '%s'\n", + argv0, argv[i]); + + goto error_invalid_device_setting; + } + } + + if (patty_daemon_set_sock_path(daemon, argv[0]) < 0) { + fprintf(stderr, "%s: Unable to set socket path to %s: %s\n", + argv0, argv[0], strerror(errno)); + + goto error_set_sock_path; + } + + if (patty_ax25_pton(argv[1], &addr) < 0) { + fprintf(stderr, "%s: Invalid callsign '%s'\n", + argv0, argv[1]); + + goto error_invalid_callsign; + } + + if ((iface = patty_ax25_if_new(PATTY_AX25_IF_KISS_TNC, + DEFAULT_IFNAME, + &info, + &addr)) == NULL) { + fprintf(stderr, "%s: Unable to create network interface %s: %s\n", + argv0, DEFAULT_IFNAME, strerror(errno)); + + goto error_if_new; + } else { + int fd = patty_kiss_tnc_fd(iface->tnc); + char *pty; + + if (isatty(fd) && (pty = ptsname(fd)) != NULL) { + printf("if %s pty %s\n", DEFAULT_IFNAME, pty); + } + } + + if (patty_daemon_if_add(daemon, iface) < 0) { + fprintf(stderr, "%s: Unable to add interface %s: %s\n", + argv0, DEFAULT_IFNAME, strerror(errno)); + + goto error_daemon_if_add; + } + + if (patty_daemon_route_add_default(daemon, DEFAULT_IFNAME) < 0) { + fprintf(stderr, "%s: Unable to add default route to %s: %s\n", + argv0, DEFAULT_IFNAME, strerror(errno)); + + goto error_daemon_route_add_default; + } + + return 0; + +error_daemon_route_add_default: +error_daemon_if_add: +error_if_new: +error_invalid_callsign: +error_invalid_device_setting: +error_set_sock_path: + return -1; +} + +enum flags { + FLAG_FG, + FLAG_STANDALONE, + FLAG_COUNT +}; + +int main(int argc, char **argv) { + int ret = 0, + index, + ch, + flags[FLAG_COUNT]; + + struct option opts[] = { + { "fg", no_argument, &flags[FLAG_FG], 1 }, + { "config", required_argument, NULL, 'c' }, + { "standalone", no_argument, &flags[FLAG_STANDALONE], 1 }, + { NULL, no_argument, NULL, 0 } + }; + + char *config_file = DEFAULT_CONFIG_FILE; + + patty_daemon *daemon; + + memset(flags, '\0', sizeof(flags)); + + if ((daemon = patty_daemon_new()) == NULL) { + fprintf(stderr, "%s: %s: %s\n", + argv[0], "patty_daemon_new()", strerror(errno)); + + goto error_daemon_new; + } + + while ((ch = getopt_long(argc, argv, "fc:s", opts, &index)) >= 0) { + switch (ch) { + case '?': + ret = usage(argc, argv, NULL); + goto error_invalid_args; + + case 'c': + config_file = optarg; + break; + + case 's': + flags[FLAG_STANDALONE] = 1; + break; + + default: + break; + } + } + + if (flags[FLAG_STANDALONE]) { + if (argc - optind < 1) { + ret = usage(argc, argv, "No socket path provided"); + + goto error_config; + } else if (argc - optind < 2) { + ret = usage(argc, argv, "No device path provided"); + + goto error_config; + } else if (argc - optind < 3) { + ret = usage(argc, argv, "No callsign provided"); + + goto error_config; + } + + if (handle_simple_config(daemon, + argc - optind, + argv[0], + argv + optind) < 0) { + goto error_config; + } + } else if (patty_conf_read(config_file, handle_config_line, daemon) < 0) { + if (errno) { + fprintf(stderr, "%s: %s: %s: %s\n", + argv[0], "patty_conf_read()", config_file, strerror(errno)); + } + + goto error_config; + } + + if (patty_daemon_run(daemon) < 0) { + fprintf(stderr, "%s: %s: %s\n", + argv[0], "patty_daemon_run()", strerror(errno)); + + goto error_daemon_run; + } + + patty_daemon_destroy(daemon); + + return 0; + +error_daemon_run: +error_config: +error_invalid_args: + patty_daemon_destroy(daemon); + +error_daemon_new: + return ret; +} diff --git a/examples/Makefile b/examples/Makefile index 078ded0..6923937 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -7,7 +7,7 @@ INCLUDE_PATH = ../include CFLAGS += -I$(INCLUDE_PATH) LDFLAGS = -L../src -lpatty -lutil -EXAMPLES = daemon connect listen login ax25dump decode +EXAMPLES = connect listen login ax25dump decode all: $(EXAMPLES) diff --git a/examples/ax25dump.c b/examples/ax25dump.c index 18a2a46..9515d27 100644 --- a/examples/ax25dump.c +++ b/examples/ax25dump.c @@ -37,7 +37,9 @@ int main(int argc, char **argv) { patty_kiss_tnc *raw; - int fd; + patty_kiss_tnc_info info = { + .flags = PATTY_KISS_TNC_FD + }; if (argc < 2) { usage(argc, argv, "No patty socket provided"); @@ -52,7 +54,7 @@ int main(int argc, char **argv) { goto error_client_new; } - if ((fd = patty_client_socket(client, PATTY_AX25_PROTO_NONE, PATTY_AX25_SOCK_RAW)) < 0) { + if ((info.fd = patty_client_socket(client, PATTY_AX25_PROTO_NONE, PATTY_AX25_SOCK_RAW)) < 0) { fprintf(stderr, "%s: %s: %s\n", argv[0], "patty_client_socket()", strerror(errno)); @@ -63,16 +65,16 @@ int main(int argc, char **argv) { ifreq.state = PATTY_AX25_SOCK_PROMISC; - if (patty_client_setsockopt(client, fd, PATTY_AX25_SOCK_IF, &ifreq, sizeof(ifreq)) < 0) { + if (patty_client_setsockopt(client, info.fd, PATTY_AX25_SOCK_IF, &ifreq, sizeof(ifreq)) < 0) { fprintf(stderr, "%s: %s: %s\n", argv[0], "patty_client_setsockopt()", strerror(errno)); goto error_client_setsockopt; } - if ((raw = patty_kiss_tnc_new_fd(fd, NULL)) == NULL) { + if ((raw = patty_kiss_tnc_new(&info)) == NULL) { fprintf(stderr, "%s: fd %d: %s: %s\n", - argv[0], fd, "patty_kiss_tnc_new_fd()", strerror(errno)); + argv[0], info.fd, "patty_kiss_tnc_new_fd()", strerror(errno)); goto error_kiss_tnc_new_fd; } @@ -137,7 +139,7 @@ error_ax25_frame_decode_address: patty_kiss_tnc_destroy(raw); - patty_client_close(client, fd); + patty_client_close(client, info.fd); patty_client_destroy(client); @@ -149,7 +151,7 @@ error_io: error_kiss_tnc_new_fd: error_client_setsockopt: error_client_socket: - (void)patty_client_close(client, fd); + (void)patty_client_close(client, info.fd); error_client_new: return 1; diff --git a/examples/daemon.c b/examples/daemon.c deleted file mode 100644 index 1d29cbc..0000000 --- a/examples/daemon.c +++ /dev/null @@ -1,157 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "../src/config.h" - -static void usage(int argc, char **argv, const char *message, ...) { - if (message != NULL) { - va_list args; - - va_start(args, message); - vfprintf(stderr, message, args); - fprintf(stderr, "\n"); - va_end(args); - } - - fprintf(stderr, "usage: %s /dev/ttyXX|kiss.cap [path.sock] callsign\n", argv[0]); - - exit(1); -} - -int main(int argc, char **argv) { - patty_daemon *daemon; - - patty_ax25_if_kiss_tnc_info info = { - .type = PATTY_AX25_IF_KISS_TNC_INFO_FD, - - .flags = PATTY_KISS_TNC_BAUD - | PATTY_KISS_TNC_FLOW, - - .baud = B9600, - .flow = PATTY_KISS_TNC_FLOW_CRTSCTS - }; - - if (argc < 2) { - usage(argc, argv, "No TNC device or KISS dump file provided"); - } else if (argc < 3) { - usage(argc, argv, "No socket path provided"); - } else if (argc < 4) { - usage(argc, argv, "No station callsign provided"); - } else if (argc > 4) { - usage(argc, argv, "Too many arguments provided"); - } - - if (strcmp(argv[1], "/dev/ptmx") == 0) { - int ptysub; - char ptyname[256]; - - if (openpty(&info.fd, &ptysub, ptyname, NULL, NULL) < 0) { - fprintf(stderr, "%s: %s: %s\n", - argv[0], "openpty()", strerror(errno)); - - goto error_open; - } - - if (grantpt(info.fd) < 0) { - fprintf(stderr, "%s: %s: %s: %s\n", - argv[0], ptyname, "grantpt()", strerror(errno)); - - goto error_grantpt; - } - - if (unlockpt(info.fd) < 0) { - fprintf(stderr, "%s: %s: %s: %s\n", - argv[0], ptyname, "unlockpt()", strerror(errno)); - - goto error_unlockpt; - } - - fprintf(stderr, "pts %s\n", ptyname); - } else { - if ((info.fd = open(argv[1], O_RDWR | O_NOCTTY)) < 0) { - fprintf(stderr, "%s: %s: %s: %s\n", - argv[0], "open()", argv[1], strerror(errno)); - - goto error_open; - } - } - - errno = 0; - - if ((daemon = patty_daemon_new()) == NULL) { - fprintf(stderr, "%s: %s: %s\n", - argv[0], "patty_daemon_new()", strerror(errno)); - - goto error_daemon_new; - } - - if (argc >= 3) { - if (patty_daemon_set_sock_path(daemon, argv[2]) < 0) { - fprintf(stderr, "%s: %s: %s\n", - argv[0], "patty_daemon_set_sock_path()", strerror(errno)); - - goto error_daemon_set_sock_path; - } - } - - if (patty_daemon_init(daemon) < 0) { - fprintf(stderr, "%s: %s: %s\n", - argv[0], "patty_daemon_init()", strerror(errno)); - - goto error_daemon_init; - } - - if (patty_daemon_if_add(daemon, - PATTY_AX25_IF_KISS_TNC, - (patty_ax25_if_info *)&info, - argv[3]) < 0) { - fprintf(stderr, "%s: %s: %s\n", - argv[0], "patty_daemon_if_add()", strerror(errno)); - - goto error_daemon_if_add; - } - - if (patty_daemon_route_add_default(daemon, "kiss0") < 0) { - fprintf(stderr, "%s: %s: %s\n", - argv[0], "patty_daemon_route_add_default()", strerror(errno)); - - goto error_daemon_route_add_default; - } - - if (patty_daemon_run(daemon) < 0) { - fprintf(stderr, "%s: %s: %s: %s\n", - argv[0], argv[1], "patty_daemon_run()", strerror(errno)); - - goto error_daemon_run; - } - - patty_daemon_destroy(daemon); - - return 0; - -error_daemon_run: -error_daemon_route_add_default: -error_daemon_if_add: -error_daemon_set_sock_path: -error_daemon_init: - patty_daemon_destroy(daemon); - -error_daemon_new: -error_grantpt: -error_unlockpt: - close(info.fd); - -error_open: - return 1; -} diff --git a/examples/decode.c b/examples/decode.c index 4b59dd1..dda5299 100644 --- a/examples/decode.c +++ b/examples/decode.c @@ -28,15 +28,21 @@ int main(int argc, char **argv) { void *buf; int port; + patty_kiss_tnc_info info; + if (argc > 2) { usage(argc, argv, NULL); } - tnc = (argc == 2)? - patty_kiss_tnc_new(argv[1], NULL): - patty_kiss_tnc_new_fd(0, NULL); + if (argc == 2) { + info.flags |= PATTY_KISS_TNC_DEVICE; + info.device = argv[1]; + } else { + info.flags |= PATTY_KISS_TNC_FD; + info.fd = 0; + } - if (tnc == NULL) { + if ((tnc = patty_kiss_tnc_new(&info)) == NULL) { perror("Unable to open TNC"); goto error_kiss_tnc_open; diff --git a/examples/patty.conf b/examples/patty.conf deleted file mode 100644 index eacae20..0000000 --- a/examples/patty.conf +++ /dev/null @@ -1,6 +0,0 @@ -sock /var/run/patty.sock -pid /var/run/patty.pid -if tnc0 ax25 KZ3ROX kiss /dev/ttyUSB0 baud 9600 -route default if tnc0 -route station GB9BLM path KX5UXQ WX3RKR -listen tnc0 ssid 0 exec /usr/bin/login -l -h @peer diff --git a/examples/pattyd.conf b/examples/pattyd.conf new file mode 100644 index 0000000..5cb691f --- /dev/null +++ b/examples/pattyd.conf @@ -0,0 +1,5 @@ +sock /var/run/patty.sock +pid /var/run/patty.pid +if kiss0 ax25 N7MMX kiss /dev/ttyUSB0 baud 9600 +route default if kiss0 +route station GB9BLM if kiss0 path KX4EHF WX9KIP diff --git a/include/patty/ax25/if.h b/include/patty/ax25/if.h index b9c7e93..cfbdfe9 100644 --- a/include/patty/ax25/if.h +++ b/include/patty/ax25/if.h @@ -52,25 +52,10 @@ typedef struct _patty_ax25_if { patty_dict *promisc_fds; } patty_ax25_if; -enum patty_ax25_if_kiss_tnc_info_type { - PATTY_AX25_IF_KISS_TNC_INFO_FD, - PATTY_AX25_IF_KISS_TNC_INFO_PATH -}; - -typedef struct _patty_ax25_if_kiss_tnc_info { - int flags; - speed_t baud; - enum patty_kiss_tnc_flow flow; - - enum patty_ax25_if_kiss_tnc_info_type type; - - int fd; - char path[256]; -} patty_ax25_if_kiss_tnc_info; - typedef void patty_ax25_if_info; patty_ax25_if *patty_ax25_if_new(enum patty_ax25_if_type type, + const char *name, patty_ax25_if_info *info, patty_ax25_addr *addr); diff --git a/include/patty/ax25/server.h b/include/patty/ax25/server.h index 36016bb..4d28342 100644 --- a/include/patty/ax25/server.h +++ b/include/patty/ax25/server.h @@ -3,7 +3,7 @@ typedef struct _patty_ax25_server patty_ax25_server; -patty_ax25_server *patty_ax25_server_new(const char *path); +patty_ax25_server *patty_ax25_server_new(); void patty_ax25_server_destroy(patty_ax25_server *server); @@ -35,7 +35,7 @@ int patty_ax25_server_route_each(patty_ax25_server *server, int (*callback)(patty_ax25_route *, void *), void *ctx); -int patty_ax25_server_start(patty_ax25_server *server); +int patty_ax25_server_start(patty_ax25_server *server, const char *path); int patty_ax25_server_stop(patty_ax25_server *server); diff --git a/include/patty/conf.h b/include/patty/conf.h new file mode 100644 index 0000000..9cdbb65 --- /dev/null +++ b/include/patty/conf.h @@ -0,0 +1,22 @@ +#ifndef _PATTY_CONF_H +#define _PATTY_CONF_H + +#include + +typedef struct _patty_conf_file patty_conf_file; + +typedef struct _patty_conf_token { + char *text; + + size_t lineno, + column, + len; +} patty_conf_token; + +typedef int (*patty_conf_handler)(patty_conf_file *, patty_list *, void *); + +int patty_conf_read(const char *file, + patty_conf_handler handler, + void *ctx); + +#endif /* _PATTY_CONF_H */ diff --git a/include/patty/daemon.h b/include/patty/daemon.h index c42f226..9ed2be2 100644 --- a/include/patty/daemon.h +++ b/include/patty/daemon.h @@ -10,8 +10,6 @@ patty_daemon *patty_daemon_new(); void patty_daemon_destroy(patty_daemon *daemon); -int patty_daemon_init(patty_daemon *daemon); - int patty_daemon_run(patty_daemon *daemon); int patty_daemon_set_sock_path(patty_daemon *daemon, const char *path); @@ -19,9 +17,7 @@ int patty_daemon_set_sock_path(patty_daemon *daemon, const char *path); int patty_daemon_set_pidfile(patty_daemon *daemon, const char *path); int patty_daemon_if_add(patty_daemon *daemon, - enum patty_ax25_if_type type, - patty_ax25_if_info *info, - const char *callsign); + patty_ax25_if *iface); int patty_daemon_route_add(patty_daemon *daemon, const char *ifname, diff --git a/include/patty/kiss.h b/include/patty/kiss.h index 5566553..b663dae 100644 --- a/include/patty/kiss.h +++ b/include/patty/kiss.h @@ -35,8 +35,10 @@ ssize_t patty_kiss_frame_send(int fd, typedef struct _patty_kiss_tnc patty_kiss_tnc; -#define PATTY_KISS_TNC_BAUD (1 << 0) -#define PATTY_KISS_TNC_FLOW (1 << 1) +#define PATTY_KISS_TNC_DEVICE (1 << 0) +#define PATTY_KISS_TNC_FD (1 << 1) +#define PATTY_KISS_TNC_BAUD (1 << 2) +#define PATTY_KISS_TNC_FLOW (1 << 3) enum patty_kiss_tnc_flow { PATTY_KISS_TNC_FLOW_NONE, @@ -47,15 +49,13 @@ enum patty_kiss_tnc_flow { typedef struct _patty_kiss_tnc_info { int flags; + const char *device; + int fd; speed_t baud; enum patty_kiss_tnc_flow flow; } patty_kiss_tnc_info; -patty_kiss_tnc *patty_kiss_tnc_new_fd(int fd, - patty_kiss_tnc_info *info); - -patty_kiss_tnc *patty_kiss_tnc_new(const char *device, - patty_kiss_tnc_info *info); +patty_kiss_tnc *patty_kiss_tnc_new(patty_kiss_tnc_info *info); int patty_kiss_tnc_fd(patty_kiss_tnc *tnc); diff --git a/src/Makefile b/src/Makefile index 8a7853d..f1c3842 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,11 +9,11 @@ LDFLAGS = -lutil HEADERS = kiss.h ax25.h client.h ax25/if.h ax25/frame.h \ ax25/sock.h ax25/route.h ax25/server.h daemon.h list.h \ - hash.h dict.h timer.h print.h + hash.h dict.h timer.h print.h conf.h OBJS = kiss.o ax25.o client.o if.o frame.o \ sock.o route.o server.o daemon.o list.o \ - hash.o dict.o timer.o print.o + hash.o dict.o timer.o print.o conf.o VERSION_MAJOR = 0 VERSION_MINOR = 0.1 diff --git a/src/conf.c b/src/conf.c new file mode 100644 index 0000000..2f7850c --- /dev/null +++ b/src/conf.c @@ -0,0 +1,358 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +enum state { + STRING, + STRING_SINGLE_QUOTE, + STRING_DOUBLE_QUOTE, + DELIM, + COMMENT +}; + +enum escape { + ESCAPE_OFF, + ESCAPE_ON +}; + +struct _patty_conf_file { + const char *path; + size_t size; + void *buf; +}; + +static int file_read(patty_conf_file *file, const char *path) { + int fd; + struct stat st; + + size_t offset, + left; + + if ((fd = open(path, O_RDONLY)) < 0) { + goto error_open; + } + + if (fstat(fd, &st) < 0) { + goto error_fstat; + } + + if ((file->buf = malloc(st.st_size)) == NULL) { + goto error_malloc_buf; + } + + offset = 0; + left = st.st_size; + + while (1) { + ssize_t len; + + if ((len = read(fd, + (uint8_t *)file->buf + offset, + left > st.st_blksize? st.st_blksize: left)) < 0) { + goto error_read; + } else if (len < st.st_blksize) { + break; + } else { + offset += len; + left -= len; + } + } + + (void)close(fd); + + file->size = st.st_size; + file->path = path; + + return 0; + +error_read: + free(file->buf); + +error_malloc_buf: +error_fstat: + (void)close(fd); + +error_open: + return -1; +} + +static void file_free(patty_conf_file *file) { + free(file->buf); +} + +static void token_start(patty_conf_token *token, + char *text, + size_t lineno, + size_t column) { + token->text = text; + token->lineno = lineno; + token->column = column; + token->len = 0; +} + +static int token_save(patty_list *list, patty_conf_token *token) { + patty_conf_token *copy; + + if ((copy = malloc(sizeof(*token))) == NULL) { + goto error_malloc; + } + + memcpy(copy, token, sizeof(*copy)); + + memset(token, '\0', sizeof(*token)); + + if (patty_list_append(list, copy) == NULL) { + goto error_list_append; + } + + return 0; + +error_list_append: + free(copy); + +error_malloc: + return -1; +} + +static void line_destroy(patty_list *list) { + patty_list_item *item = list->first; + + while (item) { + patty_list_item *next = item->next; + + free(item->value); + free(item); + + item = next; + } + + free(list); +} + +static inline int start_of_string(enum state old, enum state state) { + switch (state) { + case STRING: + case STRING_SINGLE_QUOTE: + case STRING_DOUBLE_QUOTE: + if (old == DELIM) { + return 1; + } + + default: + break; + } + + return 0; +} + +static inline int end_of_string(enum state old, enum state state) { + switch (old) { + case STRING: + case STRING_SINGLE_QUOTE: + case STRING_DOUBLE_QUOTE: + if (state == DELIM || state == COMMENT) { + return 1; + } + + default: + break; + } + + return 0; +} + +static inline int within_string(enum state old, enum state state) { + switch (state) { + case STRING: + if (old != STRING_SINGLE_QUOTE && old != STRING_DOUBLE_QUOTE) { + return 1; + } + + break; + + case STRING_SINGLE_QUOTE: + case STRING_DOUBLE_QUOTE: + if (old == state) { + return 1; + } + + default: + break; + } + + return 0; +} + +static inline int char_delim(uint8_t c) { + return (c == ' ' || c == '\r' || c == '\n' || c == '\t')? 1: 0; +} + +int patty_conf_read(const char *path, + patty_conf_handler handler, + void *ctx) { + patty_conf_file file; + + patty_list *line; + patty_conf_token token; + + enum state state = STRING; + enum escape escape = ESCAPE_OFF; + + size_t i, + o, + lineno = 1, + column = 0; + + if (file_read(&file, path) < 0) { + goto error_file_read; + } + + if ((line = patty_list_new()) == NULL) { + goto error_list_new; + } + + token_start(&token, file.buf, lineno, column); + + for (i=0, o=0; ilength > 0) { + if (handler(&file, line, ctx) < 0) { + goto error_handler; + } + + line_destroy(line); + + if ((line = patty_list_new()) == NULL) { + goto error_list_new; + } + } + } + + if (escape == ESCAPE_ON) { + escape = ESCAPE_OFF; + } + } + + if (token.len) { + ((char *)file.buf)[o++] = '\0'; + + if (token_save(line, &token) < 0) { + goto error_token_save; + } + } + + if (line->length > 0) { + if (handler(&file, line, ctx) < 0) { + goto error_handler; + } + } + + line_destroy(line); + + file_free(&file); + + return 0; + +error_handler: +error_token_save: + patty_list_destroy(line); + +error_list_new: + file_free(&file); + +error_file_read: + return -1; +} diff --git a/src/daemon.c b/src/daemon.c index 8ca4f1a..2e5a793 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -28,8 +29,15 @@ patty_daemon *patty_daemon_new() { goto error_set_pidfile; } + if ((daemon->server = patty_ax25_server_new()) == NULL) { + goto error_server_new; + } + return daemon; +error_server_new: + free(daemon->pidfile); + error_set_pidfile: free(daemon->sock_path); @@ -41,24 +49,23 @@ error_malloc: } void patty_daemon_destroy(patty_daemon *daemon) { - patty_ax25_server_destroy(daemon->server); + if (daemon->server) { + patty_ax25_server_destroy(daemon->server); + } + + if (daemon->sock_path) { + free(daemon->sock_path); + } + + if (daemon->pidfile) { + free(daemon->pidfile); + } free(daemon); } -int patty_daemon_init(patty_daemon *daemon) { - if ((daemon->server = patty_ax25_server_new(daemon->sock_path)) == NULL) { - goto error_server_new; - } - - return 0; - -error_server_new: - return -1; -} - int patty_daemon_run(patty_daemon *daemon) { - if (patty_ax25_server_start(daemon->server) < 0) { + if (patty_ax25_server_start(daemon->server, daemon->sock_path) < 0) { goto error_server_start; } @@ -110,25 +117,8 @@ error_strdup: } int patty_daemon_if_add(patty_daemon *daemon, - enum patty_ax25_if_type type, - patty_ax25_if_info *info, - const char *callsign) { - patty_ax25_if *iface; - patty_ax25_addr addr; - - if (patty_ax25_pton(callsign, &addr) < 0) { - goto error_pton; - } - - if ((iface = patty_ax25_if_new(type, info, &addr)) == NULL) { - goto error_if_new; - } - + patty_ax25_if *iface) { return patty_ax25_server_if_add(daemon->server, iface); - -error_if_new: -error_pton: - return -1; } int patty_daemon_route_add(patty_daemon *daemon, diff --git a/src/if.c b/src/if.c index 9a40546..988fa05 100644 --- a/src/if.c +++ b/src/if.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -7,22 +6,13 @@ #include -static int init_tnc(patty_ax25_if *iface, patty_ax25_if_kiss_tnc_info *info) { - static char *prefix = "kiss"; - static int number = 0; - - iface->tnc = (info->type == PATTY_AX25_IF_KISS_TNC_INFO_FD)? - patty_kiss_tnc_new_fd(info->fd, (patty_kiss_tnc_info *)info): - patty_kiss_tnc_new(info->path, (patty_kiss_tnc_info *)info); - - if (iface->tnc == NULL) { +static int init_tnc(patty_ax25_if *iface, patty_kiss_tnc_info *info) { + if ((iface->tnc = patty_kiss_tnc_new(info)) == NULL) { goto error_kiss_tnc_new; } iface->type = PATTY_AX25_IF_KISS_TNC; - snprintf(iface->name, sizeof(iface->name), "%s%d", prefix, number++); - return 0; error_kiss_tnc_new: @@ -34,6 +24,7 @@ static void destroy_tnc(patty_ax25_if *iface) { } patty_ax25_if *patty_ax25_if_new(enum patty_ax25_if_type type, + const char *name, patty_ax25_if_info *info, patty_ax25_addr *addr) { patty_ax25_if *iface; @@ -44,6 +35,8 @@ patty_ax25_if *patty_ax25_if_new(enum patty_ax25_if_type type, memset(iface, '\0', sizeof(*iface)); + strncpy(iface->name, name, sizeof(iface->name)); + if (addr->callsign[0] == '\0') { errno = EINVAL; @@ -78,7 +71,7 @@ patty_ax25_if *patty_ax25_if_new(enum patty_ax25_if_type type, switch (type) { case PATTY_AX25_IF_KISS_TNC: - if (init_tnc(iface, (patty_ax25_if_kiss_tnc_info *)info) < 0) { + if (init_tnc(iface, info) < 0) { goto error_init; } diff --git a/src/kiss.c b/src/kiss.c index 1a47cc6..1b4bf4e 100644 --- a/src/kiss.c +++ b/src/kiss.c @@ -13,6 +13,8 @@ #include +#include "config.h" + enum kiss_flags { KISS_NONE = 0, KISS_FRAME = 1 << 0, @@ -120,8 +122,7 @@ error_io: return -1; } -patty_kiss_tnc *patty_kiss_tnc_new_fd(int fd, - patty_kiss_tnc_info *info) { +patty_kiss_tnc *patty_kiss_tnc_new(patty_kiss_tnc_info *info) { patty_kiss_tnc *tnc; if ((tnc = malloc(sizeof(*tnc))) == NULL) { @@ -132,8 +133,54 @@ patty_kiss_tnc *patty_kiss_tnc_new_fd(int fd, goto error_malloc_buf; } - if (isatty(fd) && ptsname(fd) == NULL) { - if (tcgetattr(fd, &tnc->attrs) < 0) { + if (info->flags & PATTY_KISS_TNC_DEVICE) { + struct stat st; + + if (strcmp(info->device, "/dev/ptmx") == 0) { + int ptysub; + + if (openpty(&tnc->fd, &ptysub, NULL, NULL, NULL) < 0) { + goto error_open; + } + } else if (stat(info->device, &st) < 0) { + goto error_stat; + } else if ((st.st_mode & S_IFMT) == S_IFSOCK) { + struct sockaddr_un addr; + + if (strlen(info->device) > sizeof(addr.sun_path)) { + errno = EOVERFLOW; + + goto error_overflow; + } + + if ((tnc->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { + goto error_socket; + } + + memset(&addr, '\0', sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, info->device, sizeof(addr.sun_path)-1); + + if (connect(tnc->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + goto error_connect; + } + } else { + if ((tnc->fd = open(info->device, O_RDWR | O_NOCTTY)) < 0) { + goto error_open; + } + } + + tnc->opts |= TNC_CLOSE_ON_DESTROY; + } else if (info->flags & PATTY_KISS_TNC_FD) { + tnc->fd = info->fd; + } else { + errno = EINVAL; + + goto error_no_fd; + } + + if (isatty(tnc->fd) && ptsname(tnc->fd) == NULL) { + if (tcgetattr(tnc->fd, &tnc->attrs) < 0) { goto error_tcgetattr; } @@ -141,41 +188,38 @@ patty_kiss_tnc *patty_kiss_tnc_new_fd(int fd, cfmakeraw(&tnc->attrs); - if (info) { - if (info->flags & PATTY_KISS_TNC_BAUD) { - cfsetspeed(&tnc->attrs, info->baud); - } + if (info->flags & PATTY_KISS_TNC_BAUD) { + cfsetspeed(&tnc->attrs, info->baud); + } - if (info->flags & PATTY_KISS_TNC_FLOW) { - switch (info->flow) { - case PATTY_KISS_TNC_FLOW_NONE: - break; + if (info->flags & PATTY_KISS_TNC_FLOW) { + switch (info->flow) { + case PATTY_KISS_TNC_FLOW_NONE: + break; - case PATTY_KISS_TNC_FLOW_CRTSCTS: - tnc->attrs.c_cflag |= CRTSCTS; + case PATTY_KISS_TNC_FLOW_CRTSCTS: + tnc->attrs.c_cflag |= CRTSCTS; - break; + break; - case PATTY_KISS_TNC_FLOW_XONXOFF: - tnc->attrs.c_iflag |= IXON | IXOFF; + case PATTY_KISS_TNC_FLOW_XONXOFF: + tnc->attrs.c_iflag |= IXON | IXOFF; - break; - } + break; } } - if (tcflush(fd, TCIOFLUSH) < 0) { + if (tcflush(tnc->fd, TCIOFLUSH) < 0) { goto error_tcflush; } - if (tcsetattr(fd, TCSANOW, &tnc->attrs) < 0) { + if (tcsetattr(tnc->fd, TCSANOW, &tnc->attrs) < 0) { goto error_tcsetattr; } } else { errno = 0; } - tnc->fd = fd; tnc->opts = TNC_NONE; tnc->bufsz = PATTY_KISS_BUFSZ; tnc->offset = 0; @@ -187,6 +231,18 @@ patty_kiss_tnc *patty_kiss_tnc_new_fd(int fd, error_tcflush: error_tcsetattr: error_tcgetattr: +error_connect: + if (info->flags & PATTY_KISS_TNC_DEVICE) { + (void)close(tnc->fd); + } + +error_open: +error_no_fd: +error_socket: +error_overflow: +error_stat: + free(tnc->buf); + error_malloc_buf: free(tnc); @@ -194,61 +250,6 @@ error_malloc_tnc: return NULL; } -patty_kiss_tnc *patty_kiss_tnc_new(const char *device, - patty_kiss_tnc_info *info) { - patty_kiss_tnc *tnc; - int fd; - struct stat st; - - if (stat(device, &st) < 0) { - goto error_stat; - } - - if ((st.st_mode & S_IFMT) == S_IFSOCK) { - struct sockaddr_un addr; - - if (strlen(device) > sizeof(addr.sun_path)) { - errno = EOVERFLOW; - - goto error_overflow; - } - - if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { - goto error_socket; - } - - memset(&addr, '\0', sizeof(addr)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, device, sizeof(addr.sun_path)-1); - - if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - goto error_connect; - } - } else { - if ((fd = open(device, O_RDWR | O_NOCTTY)) < 0) { - goto error_open; - } - } - - if ((tnc = patty_kiss_tnc_new_fd(fd, info)) == NULL) { - goto error_tnc_new_fd; - } - - tnc->opts |= TNC_CLOSE_ON_DESTROY; - - return tnc; - -error_tnc_new_fd: -error_connect: - close(fd); - -error_socket: -error_overflow: -error_open: -error_stat: - return NULL; -} - int patty_kiss_tnc_fd(patty_kiss_tnc *tnc) { return tnc->fd; } @@ -259,7 +260,7 @@ void patty_kiss_tnc_destroy(patty_kiss_tnc *tnc) { } if (tnc->opts & TNC_CLOSE_ON_DESTROY) { - close(tnc->fd); + (void)close(tnc->fd); } free(tnc->buf); diff --git a/src/server.c b/src/server.c index 52a5d17..f832e9e 100644 --- a/src/server.c +++ b/src/server.c @@ -16,8 +16,6 @@ typedef int (*patty_ax25_server_call)(patty_ax25_server *, int); struct _patty_ax25_server { - char path[PATTY_AX25_SOCK_PATH_SIZE]; - int fd, /* fd of UNIX domain socket */ fd_max; @@ -38,7 +36,7 @@ struct _patty_ax25_server { *clients_by_sock; }; -patty_ax25_server *patty_ax25_server_new(const char *path) { +patty_ax25_server *patty_ax25_server_new() { patty_ax25_server *server; if ((server = malloc(sizeof(*server))) == NULL) { @@ -47,8 +45,6 @@ patty_ax25_server *patty_ax25_server_new(const char *path) { memset(server, '\0', sizeof(*server)); - strncpy(server->path, path, sizeof(server->path)-1); - if ((server->ifaces = patty_list_new()) == NULL) { goto error_list_new_ifaces; } @@ -2296,8 +2292,8 @@ static int handle_socks(patty_ax25_server *server) { return patty_dict_each(server->socks_by_fd, handle_sock, server); } -int patty_ax25_server_start(patty_ax25_server *server) { - return listen_unix(server, server->path); +int patty_ax25_server_start(patty_ax25_server *server, const char *path) { + return listen_unix(server, path); } int patty_ax25_server_stop(patty_ax25_server *server) { diff --git a/src/sock.c b/src/sock.c index 219b6ab..d9f6236 100644 --- a/src/sock.c +++ b/src/sock.c @@ -130,8 +130,13 @@ static patty_ax25_sock *init_dgram(patty_ax25_sock *sock, } static patty_ax25_sock *init_raw(patty_ax25_sock *sock) { - if ((sock->raw = patty_kiss_tnc_new_fd(sock->fd, NULL)) == NULL) { - goto error_kiss_tnc_new_fd; + patty_kiss_tnc_info info = { + .flags = PATTY_KISS_TNC_FD, + .fd = sock->fd + }; + + if ((sock->raw = patty_kiss_tnc_new(&info)) == NULL) { + goto error_kiss_tnc_new; } sock->proto = PATTY_AX25_PROTO_NONE; @@ -140,7 +145,7 @@ static patty_ax25_sock *init_raw(patty_ax25_sock *sock) { return sock; -error_kiss_tnc_new_fd: +error_kiss_tnc_new: (void)close(sock->fd); return NULL;