#define _GNU_SOURCE #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; }