Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ser2net for openSUSE:Factory checked in at 2022-01-27 23:16:51 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ser2net (Old) and /work/SRC/openSUSE:Factory/.ser2net.new.1898 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ser2net" Thu Jan 27 23:16:51 2022 rev:20 rq:949347 version:4.3.5 Changes: -------- --- /work/SRC/openSUSE:Factory/ser2net/ser2net.changes 2021-12-12 00:58:04.434603510 +0100 +++ /work/SRC/openSUSE:Factory/.ser2net.new.1898/ser2net.changes 2022-01-27 23:18:24.926377708 +0100 @@ -1,0 +2,9 @@ +Sun Jan 23 15:53:49 UTC 2022 - Martin Hauke <[email protected]> + +- ser2net 4.3.5: + * Yet another quick release. gcc-11 and the new sctp library + threw some curve balls, so those needed to be handled. + No functional changes, just compile and bug fixes related to + the new compiler and library. + +------------------------------------------------------------------- Old: ---- ser2net-4.3.4.tar.gz New: ---- ser2net-4.3.5.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ser2net.spec ++++++ --- /var/tmp/diff_new_pack.TlQpE9/_old 2022-01-27 23:18:25.790371739 +0100 +++ /var/tmp/diff_new_pack.TlQpE9/_new 2022-01-27 23:18:25.798371684 +0100 @@ -1,7 +1,7 @@ # # spec file for package ser2net # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: ser2net -Version: 4.3.4 +Version: 4.3.5 Release: 0 Summary: Serial port to network proxy License: GPL-2.0-or-later ++++++ ser2net-4.3.4.tar.gz -> ser2net-4.3.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/addsysattrs.c new/ser2net-4.3.5/addsysattrs.c --- old/ser2net-4.3.4/addsysattrs.c 2020-10-28 19:50:59.000000000 +0100 +++ new/ser2net-4.3.5/addsysattrs.c 2022-01-17 18:59:00.000000000 +0100 @@ -25,6 +25,8 @@ * release a modified version which carries forward this exception. */ +#include <gensio/gensio.h> + #ifdef linux #include <sys/types.h> @@ -35,7 +37,6 @@ #include <ctype.h> #include <errno.h> #include <limits.h> -#include <gensio/gensio.h> #include <gensio/argvutils.h> #include "ser2net.h" #include "port.h" @@ -50,9 +51,12 @@ s = strstr(devname, ",serialdev"); if (s) { + s++; s = strchr(s, ','); if (s) s++; + while (isspace(*s)) + s++; } else { s = strstr(devname, "/dev/"); } @@ -62,6 +66,8 @@ *len = e - s; else *len = strlen(s); + while (*len > 0 && isspace(s[(*len) - 1])) + (*len)--; } return s; @@ -118,7 +124,6 @@ { int rv; char *p, *s; - unsigned int len; rv = gensio_argv_sappend(so, txt, args, argc, "devicetype=serialusb"); if (rv < 0) @@ -126,27 +131,26 @@ "Device %s: Unable add txt devicetype for %s\n", portname, gensio_err_to_str(rv)); - /* Search backwards three directory levels, the name should match. */ + /* + * Search backwards to the device. Depending on the specific + * device, there should be various levels of directories named + * "tty" or "ttyxxxx". Like "tty/ttyACM0" or + * "ttyUSB0/tty/ttyUSB0". + */ s = p = strrchr(path, '/'); - if (p && p > path) { - p--; - while (p > path && *p != '/') - p--; - } - if (p && p > path) { + while (p && p > path && strncmp(p + 1, "tty", 3) == 0) { s = p; p--; while (p > path && *p != '/') p--; } - len = strlen(devstr); - if (!p || (s - p - 1 != len) || strncmp(p + 1, devstr, len)) { + if (!p) { eout->out(eout, "Device %s: usb path is not valid: %s\n", portname, path); return; } - *p = '\0'; + *s = '\0'; add_attr(eout, portname, path, "bInterfaceNumber", txt, args, argc); add_attr(eout, portname, path, "interface", txt, args, argc); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/configure new/ser2net-4.3.5/configure --- old/ser2net-4.3.4/configure 2021-10-06 03:37:05.000000000 +0200 +++ new/ser2net-4.3.5/configure 2022-01-18 01:24:28.000000000 +0100 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for ser2net 4.3.4. +# Generated by GNU Autoconf 2.69 for ser2net 4.3.5. # # Report bugs to <[email protected]>. # @@ -590,8 +590,8 @@ # Identity of this package. PACKAGE_NAME='ser2net' PACKAGE_TARNAME='ser2net' -PACKAGE_VERSION='4.3.4' -PACKAGE_STRING='ser2net 4.3.4' +PACKAGE_VERSION='4.3.5' +PACKAGE_STRING='ser2net 4.3.5' PACKAGE_BUGREPORT='[email protected]' PACKAGE_URL='' @@ -1339,7 +1339,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures ser2net 4.3.4 to adapt to many kinds of systems. +\`configure' configures ser2net 4.3.5 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1410,7 +1410,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of ser2net 4.3.4:";; + short | recursive ) echo "Configuration of ser2net 4.3.5:";; esac cat <<\_ACEOF @@ -1530,7 +1530,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -ser2net configure 4.3.4 +ser2net configure 4.3.5 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1899,7 +1899,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by ser2net $as_me 4.3.4, which was +It was created by ser2net $as_me 4.3.5, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2762,7 +2762,7 @@ # Define the identity of the package. PACKAGE='ser2net' - VERSION='4.3.4' + VERSION='4.3.5' cat >>confdefs.h <<_ACEOF @@ -13054,6 +13054,29 @@ fi +for ac_header in wordexp.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "wordexp.h" "ac_cv_header_wordexp_h" "$ac_includes_default" +if test "x$ac_cv_header_wordexp_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_WORDEXP_H 1 +_ACEOF + +fi + +done + +for ac_func in wordexp +do : + ac_fn_c_check_func "$LINENO" "wordexp" "ac_cv_func_wordexp" +if test "x$ac_cv_func_wordexp" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_WORDEXP 1 +_ACEOF + +fi +done + @@ -13979,7 +14002,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by ser2net $as_me 4.3.4, which was +This file was extended by ser2net $as_me 4.3.5, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -14036,7 +14059,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -ser2net config.status 4.3.4 +ser2net config.status 4.3.5 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/configure.ac new/ser2net-4.3.5/configure.ac --- old/ser2net-4.3.4/configure.ac 2021-10-06 03:35:39.000000000 +0200 +++ new/ser2net-4.3.5/configure.ac 2022-01-18 01:21:26.000000000 +0100 @@ -1,4 +1,4 @@ -AC_INIT([ser2net], [4.3.4], [[email protected]]) +AC_INIT([ser2net], [4.3.5], [[email protected]]) AM_INIT_AUTOMAKE([-Wall]) AC_PROG_CC AM_PROG_AR @@ -47,6 +47,8 @@ AC_CONFIG_MACRO_DIR([m4]) AC_STDC_HEADERS AC_CHECK_LIB(nsl,main) +AC_CHECK_HEADERS([wordexp.h]) +AC_CHECK_FUNCS(wordexp) PKG_CHECK_MODULES(GENSIO, libgensio, [LIBS=$GENSIO_LIBS], [AC_CHECK_HEADER(gensio/gensio.h, [], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/port.c new/ser2net-4.3.5/port.c --- old/ser2net-4.3.4/port.c 2021-08-23 07:10:38.000000000 +0200 +++ new/ser2net-4.3.5/port.c 2022-01-17 18:59:00.000000000 +0100 @@ -1170,7 +1170,8 @@ handle_port_child_event, new_port, &new_port->accepter); if (err) { - eout->out(eout, "Invalid port name/number: %s", gensio_err_to_str(err)); + eout->out(eout, "Invalid accepter port name/number '%s': %s", + new_port->accstr, gensio_err_to_str(err)); return -1; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/readconfig.h new/ser2net-4.3.5/readconfig.h --- old/ser2net-4.3.4/readconfig.h 2020-10-27 02:49:45.000000000 +0100 +++ new/ser2net-4.3.5/readconfig.h 2022-01-17 18:59:00.000000000 +0100 @@ -37,7 +37,7 @@ /* Read the specified configuration file and call the routine to create the ports. */ int readconfig(FILE *instream); -int yaml_readconfig(FILE *f, char **config_lines, +int yaml_readconfig(FILE *f, char *filename, char **config_lines, unsigned int num_config_lines, struct absout *errout); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/ser2net.c new/ser2net-4.3.5/ser2net.c --- old/ser2net-4.3.4/ser2net.c 2021-02-05 17:25:32.000000000 +0100 +++ new/ser2net-4.3.5/ser2net.c 2022-01-17 18:59:00.000000000 +0100 @@ -167,7 +167,7 @@ } static FILE * -fopen_config_file(bool *is_yaml) +fopen_config_file(bool *is_yaml, char **rfilename) { FILE *instream = fopen(config_file, "r"); @@ -183,8 +183,10 @@ " '%s': %m", config_file, old_config_file); return NULL; } + *rfilename = old_config_file; *is_yaml = false; } else { + *rfilename = config_file; *is_yaml = str_endswith(config_file, ".yaml"); } @@ -210,20 +212,21 @@ if (config_file) { FILE *instream = NULL; + char *filename; bool is_yaml; syslog(LOG_INFO, "Got %s, re-reading configuration", reqtype); readconfig_init(); - instream = fopen_config_file(&is_yaml); + instream = fopen_config_file(&is_yaml, &filename); if (!instream) goto out; if (!admin_port_from_cmdline) controller_shutdown(); if (is_yaml) - rv = yaml_readconfig(instream, config_lines, num_config_lines, - eout); + rv = yaml_readconfig(instream, filename, + config_lines, num_config_lines, eout); else rv = readconfig(instream); fclose(instream); @@ -718,6 +721,7 @@ int print_when_ready = 0; FILE *instream = NULL; enum { CONFIG_NONE, CONFIG_YAML, CONFIG_OLD } config_type = CONFIG_NONE; + char *filename; gensio_set_progname("ser2net"); @@ -940,8 +944,9 @@ if (strcmp(config_file, "-") == 0) { instream = stdin; is_yaml = true; + filename = "<stdin>"; } else { - instream = fopen_config_file(&is_yaml); + instream = fopen_config_file(&is_yaml, &filename); if (!instream) exit(1); } @@ -960,13 +965,16 @@ } config_type = CONFIG_OLD; } + } else { + filename = "<cmdline>"; } if (config_type == CONFIG_YAML || instream) { int rv; if (config_type == CONFIG_YAML) - rv = yaml_readconfig(instream, config_lines, num_config_lines, + rv = yaml_readconfig(instream, filename, + config_lines, num_config_lines, &stderr_absout); else rv = readconfig(instream); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/ser2net.yaml new/ser2net-4.3.5/ser2net.yaml --- old/ser2net-4.3.4/ser2net.yaml 2020-10-27 02:49:45.000000000 +0100 +++ new/ser2net-4.3.5/ser2net.yaml 2022-01-17 18:59:00.000000000 +0100 @@ -11,6 +11,13 @@ # error doesn't happen. define: &confver 1.0 +# # You can include other files from here, only at the main level. +# # These must be full yaml files, but are included in context at +# # this point, with all the defines, connection names, etc. +# # This can let you have individual connections in individual +# # files to ease management. globs are accepted. +# include: /etc/ser2net/ser2net/*.yaml + # # Just a basic serial port The con0 is the name of the connection, # # each connection must be given a unique name. The accepter is the # # information about how to receive connections, in this case it is diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/ser2net.yaml.5 new/ser2net-4.3.5/ser2net.yaml.5 --- old/ser2net-4.3.4/ser2net.yaml.5 2021-06-03 21:54:11.000000000 +0200 +++ new/ser2net-4.3.5/ser2net.yaml.5 2022-01-17 18:59:00.000000000 +0100 @@ -40,11 +40,23 @@ you are not sure, like: .IP define: &aliasname "alias text" +.br banner: "My banner *(aliasname) is here" .PP If you for some reason need "*(" in your text, use "*(*" for that. +.SH INCLUDING OTHER FILES +A YAML file may include another file at the main level, with: +.IP +include: <filename> +.PP +This filename can include globs, and all referenced files are +included. These must be full yaml files, but are included in context +at this point, with all the defines, connection names, etc. This can +let you have individual connections in individual files to ease +management. + .SH USING EXTERNAL FILES You may want to store passwords and such in external files for better security. Putting "*{filename}" in a YAML scalar will put the file's diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/tests/ipmisimdaemon.py new/ser2net-4.3.5/tests/ipmisimdaemon.py --- old/ser2net-4.3.4/tests/ipmisimdaemon.py 2021-01-22 21:45:55.000000000 +0100 +++ new/ser2net-4.3.5/tests/ipmisimdaemon.py 2022-01-17 18:59:00.000000000 +0100 @@ -11,6 +11,8 @@ import signal import time +ipmisol_port = 9002 + default_ipmisim_emu = """ mc_setbmc 0x20\n \n @@ -26,7 +28,7 @@ set_working_mc 0x20\n \n startlan 1\n - addr localhost 9001\n + addr localhost %d\n \n # Maximum privilege limit on the channel.\n priv_limit admin\n @@ -53,7 +55,7 @@ # # valid name passw priv-lim max-sess allowed-auths (ignored)\n user 1 true "" "test" user 10 none md2 md5 straight\n user 2 true "ipmiusr" "test" admin 10 none md2 md5 straight\n -""" +""" % (ipmisol_port) class IPMISimDaemon: """Create an IPMI Sim daemon instance and start it up diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/tests/test_rfc2217.py new/ser2net-4.3.5/tests/test_rfc2217.py --- old/ser2net-4.3.4/tests/test_rfc2217.py 2021-02-02 05:04:45.000000000 +0100 +++ new/ser2net-4.3.5/tests/test_rfc2217.py 2022-01-17 18:59:00.000000000 +0100 @@ -169,8 +169,6 @@ def op(self, io1, io2): sio1 = io1.cast_to_sergensio() sio1.sg_flowcontrol_s(gensio.SERGENSIO_FLOWCONTROL_XON_XOFF) - if utils.gensio_version_ge("2.3.0-rc1"): - return termioschk.dup_base_termios(iflags=termios.IXON) return termioschk.dup_base_termios(iflags=termios.IXON | termios.IXOFF) termioschk.test_ser2net_termios("xon/xoff rfc2217 settings", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/tests/test_tty_base.py new/ser2net-4.3.5/tests/test_tty_base.py --- old/ser2net-4.3.4/tests/test_tty_base.py 2021-02-02 05:01:53.000000000 +0100 +++ new/ser2net-4.3.5/tests/test_tty_base.py 2022-01-17 18:59:00.000000000 +0100 @@ -118,10 +118,7 @@ class xonhandler: def op(self, io1, io2): - if utils.gensio_version_ge("2.3.0-rc1"): - return termioschk.dup_base_termios(iflags=termios.IXON) return termioschk.dup_base_termios(iflags=(termios.IXON | - termios.IXANY | termios.IXOFF)) termioschk.test_ser2net_termios("xon/xoff termios settings", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/tests/test_xfer_basic_ipmisol.py new/ser2net-4.3.5/tests/test_xfer_basic_ipmisol.py --- old/ser2net-4.3.4/tests/test_xfer_basic_ipmisol.py 2021-06-03 21:54:00.000000000 +0200 +++ new/ser2net-4.3.5/tests/test_xfer_basic_ipmisol.py 2022-01-17 18:59:00.000000000 +0100 @@ -11,7 +11,7 @@ test_transfer("basic ipmisol", "This is a test!", ("connection: &con", " accepter: tcp,3023", - " connector: ipmisol,lan -U ipmiusr -P test -p 9001 localhost,9600"), + " connector: ipmisol,lan -U ipmiusr -P test -p %d localhost,9600" % ipmisimdaemon.ipmisol_port), "tcp,localhost,3023", "serialdev,/dev/ttyPipeA0,9600N81") @@ -26,7 +26,7 @@ test_write_drain("basic tcp", "This is a write drain test!", ("connection: &con", " accepter: tcp,3023", - " connector: ipmisol,lan -U ipmiusr -P test -p 9001 localhost,9600"), + " connector: ipmisol,lan -U ipmiusr -P test -p %d localhost,9600" % ipmisimdaemon.ipmisol_port), "tcp,localhost,3023", "serialdev,/dev/ttyPipeA0,9600N81,LOCAL", switch_delay = 0.25) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/tests/test_xfer_large_ipmisol.py new/ser2net-4.3.5/tests/test_xfer_large_ipmisol.py --- old/ser2net-4.3.4/tests/test_xfer_large_ipmisol.py 2020-10-27 02:49:45.000000000 +0100 +++ new/ser2net-4.3.5/tests/test_xfer_large_ipmisol.py 2022-01-17 18:59:00.000000000 +0100 @@ -14,6 +14,6 @@ test_transfer("basic ipmisol", rb, ("connection: &con", " accepter: tcp,3023", - " connector: ipmisol,lan -U ipmiusr -P test -p 9001 localhost,115200"), + " connector: ipmisol,lan -U ipmiusr -P test -p %d localhost,115200" % (ipmisimdaemon.ipmisol_port)), "tcp,localhost,3023", "serialdev,/dev/ttyPipeA0,115200N81", timeout=150000) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/tests/test_xfer_small_ipmisol.py new/ser2net-4.3.5/tests/test_xfer_small_ipmisol.py --- old/ser2net-4.3.4/tests/test_xfer_small_ipmisol.py 2020-12-05 04:21:02.000000000 +0100 +++ new/ser2net-4.3.5/tests/test_xfer_small_ipmisol.py 2022-01-17 18:59:00.000000000 +0100 @@ -14,6 +14,6 @@ test_transfer("basic ipmisol", rb, ("connection: &con", " accepter: tcp,3023", - " connector: ipmisol,lan -U ipmiusr -P test -p 9001 localhost,9600"), + " connector: ipmisol,lan -U ipmiusr -P test -p %d localhost,9600" % (ipmisimdaemon.ipmisol_port)), "tcp,localhost,3023", "serialdev,/dev/ttyPipeA0,9600N81", timeout=5000) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ser2net-4.3.4/yamlconf.c new/ser2net-4.3.5/yamlconf.c --- old/ser2net-4.3.4/yamlconf.c 2020-10-27 02:49:45.000000000 +0100 +++ new/ser2net-4.3.5/yamlconf.c 2022-01-17 18:59:00.000000000 +0100 @@ -34,6 +34,12 @@ #include <unistd.h> #include <fcntl.h> #include <errno.h> +#if defined(HAVE_WORDEXP) && defined(HAVE_WORDEXP_H) +#define DO_WORDEXP +#endif +#ifdef DO_WORDEXP +#include <wordexp.h> +#endif #include <gensio/gensio.h> #include <gensio/argvutils.h> #include "ser2net.h" @@ -56,6 +62,7 @@ MAIN_LEVEL, IN_DEFINE, + IN_INCLUDE, /* * We have read the type of the main mapping (connection, led, etc) @@ -157,6 +164,39 @@ }; }; +/* + * Used for processing include directives, stores the glob output and + * information about the previous file so we can go back. + */ +struct yaml_read_file { +#ifdef DO_WORDEXP + wordexp_t files; + bool files_set; + int curr_file; +#endif + + bool closeme; + yaml_parser_t parser; + yaml_event_t e; + char *filename; + FILE *f; + + struct yaml_read_file *prev_f; +}; + +struct yaml_read_handler_data { + struct yaml_read_file *f; + char **config_lines; + unsigned int num_config_lines; + unsigned int curr; + unsigned int pos; + char in_quote; + bool in_escape; + unsigned int include_depth; + struct yconf *y; + struct absout *errout; +}; + struct yconf { enum ystate state; @@ -188,42 +228,66 @@ struct yfile *files; - yaml_parser_t parser; - yaml_event_t e; - struct absout *errout; + struct yaml_read_handler_data *d; struct absout sub_errout; }; static int -errout(struct yconf *y, const char *str, ...) +yaml_verrout_d_f(struct yaml_read_handler_data *d, + struct yaml_read_file *f, + const char *str, va_list ap) { - va_list ap; char buf[1024]; - va_start(ap, str); vsnprintf(buf, sizeof(buf), str, ap); - va_end(ap); - y->errout->out(y->errout, "%s on line %lu column %lu", buf, - (unsigned long) y->e.start_mark.line, - (unsigned long) y->e.start_mark.column); + d->errout->out(d->errout, "%s:%lu(column %lu): %s", + f->filename, + (unsigned long) f->e.start_mark.line, + (unsigned long) f->e.start_mark.column, + buf); return 0; } static int +yaml_errout(struct yconf *y, const char *str, ...) +{ + va_list ap; + int rv; + + va_start(ap, str); + rv = yaml_verrout_d_f(y->d, y->d->f, str, ap); + va_end(ap); + return rv; +} + +static int sub_errout(struct absout *e, const char *str, ...) { struct yconf *y = e->data; va_list ap; - char buf[1024]; + int rv; va_start(ap, str); - vsnprintf(buf, sizeof(buf), str, ap); + rv = yaml_verrout_d_f(y->d, y->d->f, str, ap); va_end(ap); - y->errout->out(y->errout, "%s on line %lu column %lu", buf, - (unsigned long) y->e.start_mark.line, - (unsigned long) y->e.start_mark.column); - return 0; + return rv; +} + +#ifdef DO_WORDEXP +static int +yaml_errout_d_f(struct yaml_read_handler_data *d, + struct yaml_read_file *f, + const char *str, ...) +{ + va_list ap; + int rv; + + va_start(ap, str); + rv = yaml_verrout_d_f(d, f, str, ap); + va_end(ap); + return rv; } +#endif static void dofree(char **val) @@ -290,13 +354,13 @@ name = strdup(iname); if (!name) { - errout(y, "Out of memory allocating alias name"); + yaml_errout(y, "Out of memory allocating alias name"); return -1; } value = strdup(ivalue); if (!value) { free(name); - errout(y, "Out of memory allocating alias value"); + yaml_errout(y, "Out of memory allocating alias value"); return -1; } @@ -309,7 +373,7 @@ if (!a) { free(name); free(value); - errout(y, "Out of memory allocating alias"); + yaml_errout(y, "Out of memory allocating alias"); return -1; } a->next = y->aliases; @@ -321,6 +385,128 @@ return 0; } +static void +cleanup_yaml_read_file(struct yaml_read_handler_data *d) +{ + struct yaml_read_file *old_f = d->f; + + d->f = d->f->prev_f; + yaml_parser_delete(&old_f->parser); + if (old_f->closeme) { + if (old_f->f) + fclose(old_f->f); +#ifdef DO_WORDEXP + if (old_f->files_set) + wordfree(&old_f->files); +#endif + free(old_f); + d->include_depth--; + } +} + +static bool +another_yaml_file_pending(struct yaml_read_handler_data *d) +{ +#ifdef DO_WORDEXP + struct yaml_read_file *f = d->f; + + return f->curr_file < f->files.we_wordc; +#else + return false; +#endif +} + +static int yaml_read_handler(void *data, unsigned char *buffer, size_t size, + size_t *size_read); + +/* + * Returns 0 if it successfully opened a file, 1 if not. + */ +static int +next_yaml_read_file(struct yaml_read_handler_data *d) +{ +#ifdef DO_WORDEXP + struct yaml_read_file *f = d->f; + + retry: + f->filename = f->files.we_wordv[f->curr_file++]; + f->f = fopen(f->filename, "r"); + if (!f->f) { + yaml_errout_d_f(d, f->prev_f, + "Unable to open file %s, skipping\n", f->filename); + if (f->curr_file < f->files.we_wordc) + goto retry; + return 1; + } + yaml_parser_initialize(&f->parser); + yaml_parser_set_input(&f->parser, yaml_read_handler, d); + d->y->state = BEGIN_DOC; + return 0; +#else + return 1; +#endif +} + +static int +do_include(struct yconf *y, const char *ivalue) +{ +#ifdef DO_WORDEXP + struct yaml_read_file *f; + int rv; + + if (y->d->include_depth > 100) { + yaml_errout(y, "Too many nested includes, you probably have a" + " circular include\n"); + return -1; + } + + f = calloc(1, sizeof(*f)); + if (!f) { + yaml_errout(y, "Out of memory allocating include info"); + return -1; + } + + rv = wordexp(ivalue, &f->files, WRDE_NOCMD); + switch (rv) { + case 0: + break; + + case WRDE_BADCHAR: + yaml_errout(y, "Bad character in include directive"); + return -1; + + case WRDE_CMDSUB: + yaml_errout(y, "Command substitution not allowed in include directive"); + return -1; + + case WRDE_NOSPACE: + yaml_errout(y, "Out of memory processing include directive"); + return -1; + + case WRDE_SYNTAX: + yaml_errout(y, "Syntax error in include directive"); + return -1; + + case WRDE_BADVAL: + default: + yaml_errout(y, "Unknown error in include directive"); + return -1; + } + + f->closeme = true; + f->files_set = true; + f->prev_f = y->d->f; + y->d->f = f; + y->d->include_depth++; + if (next_yaml_read_file(y->d)) + cleanup_yaml_read_file(y->d); + return 0; +#else + yaml_errout(y, "Include is not supported on this system"); + return -1; +#endif +} + static struct yfile * lookup_filename_len(struct yconf *y, const char *filename, unsigned int len) { @@ -336,38 +522,38 @@ name = strndup(filename, len); if (!name) { - errout(y, "Out of memory allocating alias name"); + yaml_errout(y, "Out of memory allocating alias name"); return NULL; } infd = open(name, O_RDONLY); if (infd == -1) { - errout(y, "Error opening %s: %s", name, strerror(errno)); + yaml_errout(y, "Error opening %s: %s", name, strerror(errno)); goto out_err; } rv = fstat(infd, &stat); if (rv == -1) { - errout(y, "Error stat-ing %s: %s", name, strerror(errno)); + yaml_errout(y, "Error stat-ing %s: %s", name, strerror(errno)); goto out_err; } value = malloc(stat.st_size + 1); if (!value) { - errout(y, "Error allocating memory for file %s", name); + yaml_errout(y, "Error allocating memory for file %s", name); goto out_err; } rv = read(infd, value, stat.st_size); if (rv == -1) { - errout(y, "Error reading %s: %s", name, strerror(errno)); + yaml_errout(y, "Error reading %s: %s", name, strerror(errno)); goto out_err; } value[stat.st_size] = '\0'; f = malloc(sizeof(*f)); if (!f) { - errout(y, "Error allocating memory for file struct %s", name); + yaml_errout(y, "Error allocating memory for file struct %s", name); goto out_err; } close(infd); @@ -398,7 +584,8 @@ char **new_options = malloc(sizeof(char *) * new_len); if (!new_options) { - errout(y, "Out of memory allocating option array for %s", place); + yaml_errout(y, "Out of memory allocating option array for %s", + place); return -1; } memcpy(new_options, y->options, sizeof(char *) * y->options_len); @@ -417,8 +604,8 @@ s = strdup(name); } if (!s) { - errout(y, "Out of memory allocating option %s for %s", - option, place); + yaml_errout(y, "Out of memory allocating option %s for %s", + option, place); return -1; } y->options[y->curr_option] = s; @@ -437,8 +624,8 @@ char **new_connections = malloc(sizeof(char *) * new_len); if (!new_connections) { - errout(y, "Out of memory allocating connection array for %s", - place); + yaml_errout(y, "Out of memory allocating connection array for %s", + place); return -1; } memcpy(new_connections, y->connections, @@ -451,8 +638,8 @@ if (connection) { y->connections[y->curr_connection] = strdup(connection); if (!y->connections[y->curr_connection]) { - errout(y, "Out of memory allocating connection %s for %s", - connection, place); + yaml_errout(y, "Out of memory allocating connection %s for %s", + connection, place); return -1; } } else { @@ -591,6 +778,7 @@ static struct scalar_next_state sc_main[] = { { "define", IN_DEFINE }, + { "include", IN_INCLUDE }, { "default", IN_MAIN_NAME, WHICH_INFO_MAP, .map_info = &sc_default_map }, { "delete_default", IN_MAIN_NAME, WHICH_INFO_MAP, .map_info = &sc_deldefault_map }, @@ -607,16 +795,16 @@ struct yconf *y) { if (!ival || strlen(ival) == 0) { - errout(y, "Empty %s %s not permitted", mapname, keyname); + yaml_errout(y, "Empty %s %s not permitted", mapname, keyname); return -1; } if (*oval) { - errout(y, "%s %s already set in connection", mapname, keyname); + yaml_errout(y, "%s %s already set in connection", mapname, keyname); return -1; } *oval = strdup(ival); if (!*oval) { - errout(y, "Unable to allocate %s %s", mapname, keyname); + yaml_errout(y, "Unable to allocate %s %s", mapname, keyname); return -1; } return 0; @@ -695,12 +883,12 @@ len += 2; state = 0; } else if (!*s) { - errout(y, "Missing ')' for alias at '%s'", start - 2); + yaml_errout(y, "Missing ')' for alias at '%s'", start - 2); goto out_err; } else if (*s == ')') { struct alias *a = lookup_alias_len(y, start, s - start); if (!a) { - errout(y, "unknown alias at '%s'", start - 2); + yaml_errout(y, "unknown alias at '%s'", start - 2); goto out_err; } alen = strlen(a->value); @@ -722,7 +910,7 @@ len += 2; state = 0; } else if (!*s) { - errout(y, "Missing '}' for filename at '%s'", start - 2); + yaml_errout(y, "Missing '}' for filename at '%s'", start - 2); goto out_err; } else if (*s == '}') { struct yfile *f = lookup_filename_len(y, start, s - start); @@ -741,7 +929,7 @@ if (!out) { out = malloc(len + 1); if (!out) { - errout(y, "Out of memory processing string '%s'", iscalar); + yaml_errout(y, "Out of memory processing string '%s'", iscalar); return NULL; } rv = out; @@ -772,7 +960,7 @@ case MAIN_LEVEL: scalar_next_state(y, sc_main, scalar); if (y->state == PARSE_ERR) { - errout(y, "Invalid token at the main level: %s\n", scalar); + yaml_errout(y, "Invalid token at the main level: %s\n", scalar); goto out_err; } break; @@ -780,7 +968,7 @@ case IN_DEFINE: anchor_allowed = true; if (!anchor) - errout(y, "No anchor for define, define ignored\n"); + yaml_errout(y, "No anchor for define, define ignored\n"); else { if (add_alias(y, anchor, scalar)) goto out_err; @@ -788,11 +976,17 @@ y->state = MAIN_LEVEL; break; + case IN_INCLUDE: + if (do_include(y, scalar)) + goto out_err; + /* do_include conditionally moves us to BEGIN_DOC. */ + break; + case IN_MAIN_MAP: scalar_next_state(y, y->map_info->states, scalar); if (y->state == PARSE_ERR) { - errout(y, "Invalid token in the %s: %s", - y->map_info->name, scalar); + yaml_errout(y, "Invalid token in the %s: %s", + y->map_info->name, scalar); goto out_err; } break; @@ -807,7 +1001,7 @@ case IN_CONNSPEC_TIMEOUT: y->timeout = strtoul(scalar, &end, 0); if (end == scalar || *end != '\0') { - errout(y, "Invalid number in connection timeout"); + yaml_errout(y, "Invalid number in connection timeout"); goto out_err; } y->state = IN_MAIN_MAP; @@ -819,7 +1013,7 @@ } else if (strcasecmp(scalar, "off") == 0) { y->enable = false; } else { - errout(y, "enable must be 'on' or 'off'"); + yaml_errout(y, "enable must be 'on' or 'off'"); goto out_err; } y->state = IN_MAIN_MAP; @@ -845,12 +1039,12 @@ break; default: - errout(y, "Unexpected scalar value"); + yaml_errout(y, "Unexpected scalar value"); goto out_err; } if (anchor && !anchor_allowed) - errout(y, "Anchor on non-scalar ignored\n"); + yaml_errout(y, "Anchor on non-scalar ignored\n"); free(scalar); return 0; @@ -869,7 +1063,7 @@ break; default: - errout(y, "Unexpected sequence start: %d", y->state); + yaml_errout(y, "Unexpected sequence start: %d", y->state); return -1; } @@ -885,7 +1079,7 @@ break; default: - errout(y, "Unexpected sequence end: %d", y->state); + yaml_errout(y, "Unexpected sequence end: %d", y->state); return -1; } @@ -902,15 +1096,15 @@ case IN_MAIN_NAME: if (y->map_info->needs_anchor) { - char *anchor = (char *) y->e.data.mapping_start.anchor; + char *anchor = (char *) y->d->f->e.data.mapping_start.anchor; if (!anchor) { - errout(y, "Main mapping requires an anchor for the name"); + yaml_errout(y, "Main mapping requires an anchor for the name"); return -1; } y->name = strdup(anchor); if (!y->name) { - errout(y, "Out of memory allocating name"); + yaml_errout(y, "Out of memory allocating name"); return -1; } if (add_alias(y, anchor, anchor)) @@ -924,7 +1118,7 @@ break; default: - errout(y, "Unexpected mapping start: %d", y->state); + yaml_errout(y, "Unexpected mapping start: %d", y->state); return -1; } @@ -946,14 +1140,14 @@ switch (y->map_info->map_type) { case MAIN_MAP_DEFAULT: if (!y->name) { - errout(y, "No name given in default"); + yaml_errout(y, "No name given in default"); return -1; } err = gensio_set_default(so, y->class, y->name, y->value, 0); if (err) { - errout(y, "Unable to set default name %s:%s:%s: %s", - y->class ? y->class : "", - y->name, y->value, gensio_err_to_str(err)); + yaml_errout(y, "Unable to set default name %s:%s:%s: %s", + y->class ? y->class : "", + y->name, y->value, gensio_err_to_str(err)); return -1; } y->state = MAIN_LEVEL; @@ -962,18 +1156,18 @@ case MAIN_MAP_DELDEFAULT: if (!y->name) { - errout(y, "No name given in delete_default"); + yaml_errout(y, "No name given in delete_default"); return -1; } if (!y->class) { - errout(y, "No class given in delete_default"); + yaml_errout(y, "No class given in delete_default"); return -1; } err = gensio_del_default(so, y->class, y->name, false); if (err) { - errout(y, "Unable to set default name %s:%s:%s: %s", - y->class ? y->class : "", - y->name, y->value, gensio_err_to_str(err)); + yaml_errout(y, "Unable to set default name %s:%s:%s: %s", + y->class ? y->class : "", + y->name, y->value, gensio_err_to_str(err)); return -1; } y->state = MAIN_LEVEL; @@ -982,15 +1176,15 @@ case MAIN_MAP_CONNECTION: if (!y->name) { - errout(y, "No name given in connection"); + yaml_errout(y, "No name given in connection"); return -1; } if (!y->accepter) { - errout(y, "No accepter given in connection"); + yaml_errout(y, "No accepter given in connection"); return -1; } if (!y->connector) { - errout(y, "No connector given in connection"); + yaml_errout(y, "No connector given in connection"); return -1; } /* NULL terminate the options. */ @@ -1005,15 +1199,15 @@ case MAIN_MAP_ROTATOR: if (!y->name) { - errout(y, "No name given in rotator"); + yaml_errout(y, "No name given in rotator"); return -1; } if (!y->accepter) { - errout(y, "No accepter given in rotator"); + yaml_errout(y, "No accepter given in rotator"); return -1; } if (y->curr_connection == 0) { - errout(y, "No connections given in rotator"); + yaml_errout(y, "No connections given in rotator"); return -1; } /* NULL terminate the connections. */ @@ -1022,14 +1216,15 @@ err = gensio_argv_copy(so, (const char **) y->connections, &argc, &argv); if (err) { - errout(y, "Unable to allocate rotator connections"); + yaml_errout(y, "Unable to allocate rotator connections"); return -1; } /* NULL terminate the options. */ if (add_option(y, NULL, NULL, "rotator")) return -1; err = add_rotator(&y->sub_errout, y->name, y->accepter, argc, argv, - (const char **) y->options, y->e.start_mark.line); + (const char **) y->options, + y->d->f->e.start_mark.line); if (err) gensio_argv_free(so, argv); y->state = MAIN_LEVEL; @@ -1038,25 +1233,26 @@ case MAIN_MAP_LED: if (!y->name) { - errout(y, "No name given in led"); + yaml_errout(y, "No name given in led"); return -1; } if (!y->driver) { - errout(y, "No driver given in led"); + yaml_errout(y, "No driver given in led"); return -1; } /* NULL terminate the options. */ if (add_option(y, NULL, NULL, "led")) return -1; err = add_led(y->name, y->driver, - (const char **) y->options, y->e.start_mark.line); + (const char **) y->options, + y->d->f->e.start_mark.line); y->state = MAIN_LEVEL; yconf_cleanup_main(y); break; case MAIN_MAP_ADMIN: if (!y->accepter) { - errout(y, "No accepter given in admin"); + yaml_errout(y, "No accepter given in admin"); return -1; } /* NULL terminate the options. */ @@ -1075,23 +1271,13 @@ break; default: - errout(y, "Unexpected mapping end: %d", y->state); + yaml_errout(y, "Unexpected mapping end: %d", y->state); return -1; } return 0; } -struct yaml_read_handler_data { - FILE *f; - char **config_lines; - unsigned int num_config_lines; - unsigned int curr; - unsigned int pos; - char in_quote; - bool in_escape; -}; - /* * Copy characters from input to buffer, up to either buffer_size or * input_size. Upon return buffer_size and input_size are updated to @@ -1158,23 +1344,27 @@ *size_read = 0; - if (d->f) { - *size_read = fread(buffer, 1, size, d->f); + if (d->f->f) { + *size_read = fread(buffer, 1, size, d->f->f); if (*size_read < size) { - if (ferror(d->f)) { - syslog(LOG_ERR, "Error reading input file: %m"); + if (ferror(d->f->f)) { + yaml_errout(d->y, "Error reading input file: %s", + strerror(errno)); return 0; } /* End of file */ - d->f = NULL; size -= *size_read; buffer += *size_read; - } else { - return 1; + + /* + * End of file handling in the main routine will move to + * the next file as necessary. + */ } + return 1; } - while (d->curr < d->num_config_lines && size > 1) { + while (d->curr < d->num_config_lines) { unsigned int len = strlen(d->config_lines[d->curr]); size--; /* leave space for the terminating newline. */ @@ -1185,7 +1375,7 @@ if (!process_buffer(d, buffer, &output_processed, d->config_lines[d->curr] + d->pos, &input_processed)) { - syslog(LOG_ERR, "Invalid yaml config string"); + yaml_errout(d->y, "Invalid yaml config string"); return 0; } size -= output_processed; @@ -1210,13 +1400,15 @@ } int -yaml_readconfig(FILE *f, char **config_lines, unsigned int num_config_lines, +yaml_readconfig(FILE *file, char *filename, + char **config_lines, unsigned int num_config_lines, struct absout *errout) { bool done = false; struct yconf y; int err = 0; struct yaml_read_handler_data d; + struct yaml_read_file f; memset(&y, 0, sizeof(y)); y.enable = true; @@ -1234,29 +1426,31 @@ } y.connections_len = 10; y.state = BEGIN_DOC; - y.errout = errout; y.sub_errout.out = sub_errout; y.sub_errout.data = &y; - - yaml_parser_initialize(&y.parser); + y.d = &d; memset(&d, 0, sizeof(d)); - d.f = f; + d.errout = errout; d.config_lines = config_lines; d.num_config_lines = num_config_lines; - yaml_parser_set_input(&y.parser, yaml_read_handler, &d); + d.f = &f; + d.y = &y; + + memset(&f, 0, sizeof(f)); + f.filename = filename; + f.f = file; + yaml_parser_initialize(&f.parser); + yaml_parser_set_input(&f.parser, yaml_read_handler, &d); while (!done && !err) { - if (!yaml_parser_parse(&y.parser, &y.e)) { - errout->out(errout, "yaml parsing error at line %lu column %lu: %s", - (unsigned long) y.parser.problem_mark.line, - (unsigned long) y.parser.problem_mark.column, - y.parser.problem); + if (!yaml_parser_parse(&y.d->f->parser, &y.d->f->e)) { + yaml_errout(&y, y.d->f->parser.problem); err = EINVAL; break; } - switch (y.e.type) { + switch (y.d->f->e.type) { case YAML_NO_EVENT: case YAML_STREAM_START_EVENT: case YAML_DOCUMENT_START_EVENT: @@ -1265,7 +1459,7 @@ case YAML_STREAM_END_EVENT: if (y.state != END_DOC) { - errout->out(errout, "yaml file ended in invalid state: %d", + yaml_errout(&y, "yaml file ended in invalid state: %d", y.state); err = EINVAL; } @@ -1278,10 +1472,10 @@ printf("YAML_ALIAS_EVENT\n"); printf(" anc: '%s'\n", y.e.data.alias.anchor); #endif - a = lookup_alias(&y, (char *) y.e.data.alias.anchor); + a = lookup_alias(&y, (char *) y.d->f->e.data.alias.anchor); if (!a) { - errout->out(errout, "Unable to find alias '%s'", - y.e.data.alias.anchor); + yaml_errout(&y, "Unable to find alias '%s'", + y.d->f->e.data.alias.anchor); err = EINVAL; } else { if (yhandle_scalar(&y, NULL, a->value)) @@ -1297,8 +1491,8 @@ printf(" tag: '%s'\n", y.e.data.scalar.tag); printf(" val: '%s'\n", y.e.data.scalar.value); #endif - if (yhandle_scalar(&y, (char *) y.e.data.scalar.anchor, - (char *) y.e.data.scalar.value)) + if (yhandle_scalar(&y, (char *) y.d->f->e.data.scalar.anchor, + (char *) y.d->f->e.data.scalar.value)) err = EINVAL; break; @@ -1339,10 +1533,25 @@ break; } - yaml_event_delete(&y.e); + yaml_event_delete(&y.d->f->e); + + if (done) { + continue_clean: + while (d.f && !another_yaml_file_pending(&d)) + /* Done with this include directive. */ + cleanup_yaml_read_file(&d); + if (d.f) { + done = false; + yaml_parser_delete(&d.f->parser); + fclose(d.f->f); + if (next_yaml_read_file(&d)) + goto continue_clean; + } + } } - yaml_parser_delete(&y.parser); + while (d.f) + cleanup_yaml_read_file(&d); yconf_cleanup_main(&y); free(y.options);
