Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package tio for openSUSE:Factory checked in at 2022-06-09 14:11:57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/tio (Old) and /work/SRC/openSUSE:Factory/.tio.new.1548 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "tio" Thu Jun 9 14:11:57 2022 rev:5 rq:981391 version:1.38 Changes: -------- --- /work/SRC/openSUSE:Factory/tio/tio.changes 2022-04-22 21:55:36.390898448 +0200 +++ /work/SRC/openSUSE:Factory/.tio.new.1548/tio.changes 2022-06-09 14:12:08.400555966 +0200 @@ -1,0 +2,25 @@ +Thu Jun 2 09:05:15 UTC 2022 - Martin Hauke <mar...@gmx.de> + +- Update to version 1.38 + * Redirect error messages to stderr + * Improve help and man page + * Mention config file in --help + * Fix running without config file + * Fix config file error messages + * Redirect error messages to stderr + * Add repology packaging status + * Fix parsing of default settings + * Default configuration file settings were not parsed in case a + section was matched. Now we make sure that the default + (unnamed) settings are always parsed. + * Append to existing log file (no truncation) + * Add socket info to show configuration + * Print socket info at startup + * Fix socket option parsing + * Match user input against config section names if pattern + matching was unsuccessful. + * Add support for external control via a Unix domain socket. + * fix for using option 'log' without 'log-filename' in config + file. + +------------------------------------------------------------------- Old: ---- tio-1.37.tar.xz New: ---- tio-1.38.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ tio.spec ++++++ --- /var/tmp/diff_new_pack.glWpnB/_old 2022-06-09 14:12:09.048556834 +0200 +++ /var/tmp/diff_new_pack.glWpnB/_new 2022-06-09 14:12:09.052556840 +0200 @@ -17,7 +17,7 @@ Name: tio -Version: 1.37 +Version: 1.38 Release: 0 Summary: Simple TTY terminal I/O application License: GPL-2.0-or-later ++++++ tio-1.37.tar.xz -> tio-1.38.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/AUTHORS new/tio-1.38/AUTHORS --- old/tio-1.37/AUTHORS 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/AUTHORS 2022-06-02 09:36:36.000000000 +0200 @@ -32,5 +32,6 @@ attila-v <attil...@index.hu> Yin Fengwei <fengwei....@intel.com> Liam Beguin <liambeg...@gmail.com> +Peter Collingbourne <p...@google.com> Thanks to everyone who has contributed to this project. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/ChangeLog new/tio-1.38/ChangeLog --- old/tio-1.37/ChangeLog 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/ChangeLog 2022-06-02 09:36:36.000000000 +0200 @@ -1,4 +1,88 @@ -=== tio v1.37 === +=== tio v1.38 === + + + +Changes since tio v1.37: + + * Redirect error messages to stderr + + * Improve help and man page + + * Mention config file in --help + + * Fix running without config file + + * Fix config file error messages + + * Redirect error messages to stderr + + * Add repology packaging status + + * Fix parsing of default settings + + Default configuration file settings were not parsed in case a section + was matched. Now we make sure that the default (unnamed) settings are + always parsed. + + * Append to existing log file (no truncation) + + * Add socket info to show configuration + + * Print socket info at startup + + * Fix socket option parsing + +Peter Collingbourne: + + * Match user input against config section names if pattern matching was unsuccessful. + + This allows for better config file ergonomics if the user has a diverse + set of serial devices as the name does not need to be specified in + the config file twice. + + * Add support for external control via a Unix domain socket. + + This feature allows an external program to inject output into and + listen to input from a serial port via a Unix domain socket (path + specified via the -S/--socket command line flag, or the socket + config file option) while tio is running. This is useful for ad-hoc + scripting of serial port interactions while still permitting manual + control. Since many serial devices (at least on Linux) get confused + when opened by multiple processes, and most commands do not know + how to correctly open a serial device, this allows a more convenient + usage model than directly writing to the device node from an external + program. + + Any input from clients connected to the socket is sent on the serial + port as if entered at the terminal where tio is running (except that + ctrl-t sequences are not recognized), and any input from the serial + port is multiplexed to the terminal and all connected clients. + + Sockets remain open while the serial port is disconnected, and writes + will block. + + Example usage 1 (issue a command): + + echo command | nc -UN /path/to/socket > /dev/null + + Example usage 2 (use the expect command to script an interaction): + + #!/usr/bin/expect -f + + set timeout -1 + log_user 0 + + spawn nc -UN /path/to/socket + set uart $spawn_id + + send -i $uart "command1\n" + expect -i $uart "prompt> " + send -i $uart "command2\n" + expect -i $uart "prompt> " + +lexaone: + + * fix for using option 'log' without 'log-filename' in config file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/README.md new/tio-1.38/README.md --- old/tio-1.37/README.md 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/README.md 2022-06-02 09:36:36.000000000 +0200 @@ -2,6 +2,7 @@ [![CircleCI](https://circleci.com/gh/tio/tio/tree/master.svg?style=shield)](https://circleci.com/gh/tio/tio/tree/master) [![tio](https://snapcraft.io/tio/badge.svg)](https://snapcraft.io/tio) +[![Packaging status](https://repology.org/badge/tiny-repos/tio.svg)](https://repology.org/project/tio/versions) ## 1. Introduction @@ -24,7 +25,7 @@ The command-line interface is straightforward as reflected in the output from 'tio --help': ``` - Usage: tio [<options>] <tty-device> + Usage: tio [<options>] <tty-device|config> Options: -b, --baudrate <bps> Baud rate (default: 115200) @@ -40,12 +41,15 @@ -l, --log <filename> Log to file -m, --map <flags> Map special characters -c, --color <0..255> Colorize tio text + -S, --socket <socket> Listen on socket -v, --version Display version -h, --help Display help - See the man page for more details. + Options may be set via configuration file. In session, press ctrl-t q to quit. + + See the man page for more details. ``` The only option which requires a bit of elaboration is perhaps the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/man/tio.1.in new/tio-1.38/man/tio.1.in --- old/tio-1.37/man/tio.1.in 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/man/tio.1.in 2022-06-02 09:36:36.000000000 +0200 @@ -6,13 +6,13 @@ .SH "SYNOPSIS" .PP .B tio -.RI "[" <options> "] " "<tty-device>" +.RI "[" <options> "] " "<tty-device|config>" .SH "DESCRIPTION" .PP .B tio is a simple serial terminal tool which features a straightforward command-line -interface to easily connect to serial/TTY devices for basic I/O operations. +interface to easily connect to serial TTY devices for basic I/O operations. .SH "OPTIONS" @@ -115,6 +115,23 @@ If color code is negative a list of available ANSI colors will be printed. .TP +.BR \-S ", " "\-\-socket \fI<socket>\fR\fB + +Listen on socket. Any input from clients connected to the socket is sent on the serial port as if entered at the terminal where tio is running (except that +.B ctrl-t +sequences are not recognized), and any input from the serial port is multiplexed to the terminal and all connected clients. + +Sockets remain open while the serial port is disconnected, and writes will block. + +Two socket types are supported using different prefixes in the socket field: + +unix:<filename> - Unix Domain Socket (file) + +inet:<IP>:<port> - Internet Socket (network) (NOT YET SUPPORTED) + +At present there is a hardcoded limit of 16 clients connected at one time. + +.TP .BR \-v ", " \-\-version Display program version. @@ -159,14 +176,14 @@ Options can be set via a configuration file using the INI format. tio uses the configuration file first found in the following locations in the order listed: $XDG_CONFIG_HOME/tio/tiorc, $HOME/.config/tio/tiorc, $HOME/.tiorc .TP -Sections can be used to group settings and their names are only used for readability. +Config sections can be used to group settings. .TP .TP -tio will try to match the user input to a section pattern to get the tty and other options. +tio will try to match the user input to a config section by name or by pattern to get the tty and other options. .TP -Options without any section name sets the default options. +Options without any config section name sets the default options. .TP The following configuration file options are available: @@ -201,17 +218,19 @@ Map special characters on input or output .IP "\fBcolor" Colorize tio text using ANSI color code ranging from 0 to 255. +.IP "\fBsocket" +Set socket path (must include +.BR unix: ). .SH "CONFIGURATION EXAMPLES" .TP -A Typical section used as a short-hand would be defined as such: +A typical config section used as a short-hand would be defined as such: .RS .nf .eo -[ftdi device] -pattern=ftdi +[ftdi] baudrate=115200 tty=/dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 color=11 @@ -220,14 +239,14 @@ .RE .TP -With this section defined in the configuration file the following commands would be equivalent: +With this config section defined in the configuration file the following commands would be equivalent: tio ftdi tio -b 115200 /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 .TP -A pattern can also be a regular expression: +A config section can also be matched by its pattern which supports regular expressions: .RS .nf @@ -270,4 +289,4 @@ .SH "AUTHOR" .PP -Written by Martin Lund <martin.lund@keep\-it\-simple.com>. +Created by Martin Lund <martin.lund@keep\-it\-simple.com>. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/meson.build new/tio-1.38/meson.build --- old/tio-1.37/meson.build 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/meson.build 2022-06-02 09:36:36.000000000 +0200 @@ -1,12 +1,12 @@ project('tio', 'c', - version : '1.37', + version : '1.38', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] ) # The tag date of the project_version(), update when the version bumps. -version_date = '2022-04-13' +version_date = '2022-06-02' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/src/bash-completion/tio.in new/tio-1.38/src/bash-completion/tio.in --- old/tio-1.37/src/bash-completion/tio.in 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/src/bash-completion/tio.in 2022-06-02 09:36:36.000000000 +0200 @@ -80,6 +80,10 @@ COMPREPLY=( $(compgen -W "$(seq 0 255)" -- ${cur}) ) return 0 ;; + -S | --socket) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; -v | --version) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/src/configfile.c new/tio-1.38/src/configfile.c --- old/tio-1.37/src/configfile.c 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/src/configfile.c 2022-06-02 09:36:36.000000000 +0200 @@ -56,7 +56,7 @@ if (ret) { regerror(ret, &re, err, sizeof(err)); - printf("reg error: %s\n", err); + fprintf(stderr, "regex error: %s", err); return ret; } @@ -152,19 +152,24 @@ { option.color = atoi(value); } + else if (!strcmp(name, "socket")) + { + asprintf(&c->socket, "%s", value); + option.socket = c->socket; + } } return 0; } /** - * section_search_handler() - walk config file to find section matching user input + * section_pattern_search_handler() - walk config file to find section matching user input * * INIH handler used to resolve the section matching the user's input. * This will look for the pattern element of each section and try to match it * with the user input. */ -static int section_search_handler(void *user, const char *section, const char - *varname, const char *varval) +static int section_pattern_search_handler(void *user, const char *section, const char *varname, + const char *varval) { UNUSED(user); @@ -185,6 +190,28 @@ return 0; } +/** + * section_pattern_search_handler() - walk config file to find section matching user input + * + * INIH handler used to resolve the section matching the user's input. + * This will try to match the user input against a section with the name of the user input. + */ +static int section_name_search_handler(void *user, const char *section, const char *varname, + const char *varval) +{ + UNUSED(user); + UNUSED(varname); + UNUSED(varval); + + if (!strcmp(section, c->user)) + { + /* section name matches as plain text */ + asprintf(&c->section_name, "%s", section); + } + + return 0; +} + static int resolve_config_file(void) { asprintf(&c->path, "%s/tio/tiorc", getenv("XDG_CONFIG_HOME")); @@ -216,7 +243,12 @@ c = malloc(sizeof(struct config_t)); memset(c, 0, sizeof(struct config_t)); - resolve_config_file(); + // Find config file + if (resolve_config_file() != 0) + { + // None found - stop parsing + return; + } for (i = 1; i < argc; i++) { @@ -232,17 +264,33 @@ return; } - ret = ini_parse(c->path, section_search_handler, NULL); + // Parse default (unnamed) settings + asprintf(&c->section_name, "%s", ""); + ret = ini_parse(c->path, data_handler, NULL); + if (ret < 0) + { + fprintf(stderr, "Error: Unable to parse configuration file (%d)", ret); + exit(EXIT_FAILURE); + } + c->section_name = NULL; + + // Find matching section + ret = ini_parse(c->path, section_pattern_search_handler, NULL); if (!c->section_name) { - debug_printf("unable to match user input to configuration section (%d)\n", ret); - return; + ret = ini_parse(c->path, section_name_search_handler, NULL); + if (!c->section_name) + { + debug_printf("Unable to match user input to configuration section (%d)", ret); + return; + } } + // Parse settings of found section ret = ini_parse(c->path, data_handler, NULL); if (ret < 0) { - fprintf(stderr, "Error: unable to parse configuration file (%d)\n", ret); + fprintf(stderr, "Error: Unable to parse configuration file (%d)", ret); exit(EXIT_FAILURE); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/src/configfile.h new/tio-1.38/src/configfile.h --- old/tio-1.37/src/configfile.h 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/src/configfile.h 2022-06-02 09:36:36.000000000 +0200 @@ -34,6 +34,7 @@ char *flow; char *parity; char *log_filename; + char *socket; char *map; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/src/error.c new/tio-1.38/src/error.c --- old/tio-1.37/src/error.c 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/src/error.c 2022-06-02 09:36:36.000000000 +0200 @@ -35,11 +35,11 @@ if (error[0][0] != 0) { /* Print error */ - tio_printf("Error: %s", error[0]); + tio_error_printf("Error: %s", error[0]); } else if ((error[1][0] != 0) && (option.no_autoconnect)) { /* Print silent error */ - tio_printf("Error: %s", error[1]); + tio_error_printf("Error: %s", error[1]); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/src/log.c new/tio-1.38/src/log.c --- old/tio-1.37/src/log.c 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/src/log.c 2022-06-02 09:36:36.000000000 +0200 @@ -60,7 +60,7 @@ option.log_filename = automatic_filename; } - fp = fopen(filename, "w+"); + fp = fopen(filename, "a+"); if (fp == NULL) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/src/main.c new/tio-1.38/src/main.c --- old/tio-1.37/src/main.c 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/src/main.c 2022-06-02 09:36:36.000000000 +0200 @@ -30,6 +30,7 @@ #include "error.h" #include "print.h" #include "signals.h" +#include "socket.h" int main(int argc, char *argv[]) { @@ -78,6 +79,9 @@ tio_printf("tio v%s", VERSION); tio_printf("Press ctrl-t q to quit"); + /* Open socket */ + socket_configure(); + /* Connect to tty device */ if (option.no_autoconnect) status = tty_connect(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/src/meson.build new/tio-1.38/src/meson.build --- old/tio-1.37/src/meson.build 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/src/meson.build 2022-06-02 09:36:36.000000000 +0200 @@ -12,7 +12,8 @@ 'tty.c', 'print.c', 'configfile.c', - 'signals.c' + 'signals.c', + 'socket.c' ] tio_dep = dependency('inih', required: true, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/src/options.c new/tio-1.38/src/options.c --- old/tio-1.37/src/options.c 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/src/options.c 2022-06-02 09:36:36.000000000 +0200 @@ -50,14 +50,15 @@ .local_echo = false, .timestamp = TIMESTAMP_NONE, .list_devices = false, - .log_filename = "", + .log_filename = NULL, + .socket = NULL, .map = "", .color = -1, }; void print_help(char *argv[]) { - printf("Usage: %s [<options>] <tty-device>\n", argv[0]); + printf("Usage: %s [<options>] <tty-device|config>\n", argv[0]); printf("\n"); printf("Options:\n"); printf(" -b, --baudrate <bps> Baud rate (default: 115200)\n"); @@ -73,13 +74,17 @@ printf(" -l, --log[=<filename>] Log to file\n"); printf(" -m, --map <flags> Map special characters\n"); printf(" -c, --color <code> Colorize tio text\n"); + printf(" -S, --socket <socket> Listen on socket\n"); printf(" -v, --version Display version\n"); printf(" -h, --help Display help\n"); printf("\n"); - printf("See the man page for more details.\n"); + printf("Options may be set via configuration file.\n"); printf("\n"); printf("In session, press ctrl-t q to quit.\n"); printf("\n"); + printf("See the man page for more details.\n"); + printf("\n"); + } const char* timestamp_token(enum timestamp_t timestamp) @@ -147,6 +152,8 @@ tio_printf(" Map flags: %s", option.map); if (option.log) tio_printf(" Log file: %s", option.log_filename); + if (option.socket) + tio_printf(" Socket: %s", option.socket); } void options_parse(int argc, char *argv[]) @@ -174,6 +181,7 @@ {"timestamp", optional_argument, 0, 't'}, {"list-devices", no_argument, 0, 'L'}, {"log", optional_argument, 0, 'l'}, + {"socket", required_argument, 0, 'S'}, {"map", required_argument, 0, 'm'}, {"color", required_argument, 0, 'c'}, {"version", no_argument, 0, 'v'}, @@ -185,7 +193,7 @@ int option_index = 0; /* Parse argument using getopt_long */ - c = getopt_long(argc, argv, "b:d:f:s:p:o:net::Ll::m:c:vh", long_options, &option_index); + c = getopt_long(argc, argv, "b:d:f:s:p:o:net::Ll::S:m:c:vh", long_options, &option_index); /* Detect the end of the options */ if (c == -1) @@ -248,6 +256,10 @@ option.log_filename = optarg; break; + case 'S': + option.socket = optarg; + break; + case 'm': option.map = optarg; break; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/src/options.h new/tio-1.38/src/options.h --- old/tio-1.37/src/options.h 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/src/options.h 2022-06-02 09:36:36.000000000 +0200 @@ -53,6 +53,7 @@ bool list_devices; const char *log_filename; const char *map; + const char *socket; int color; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/src/print.h new/tio-1.38/src/print.h --- old/tio-1.37/src/print.h 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/src/print.h 2022-06-02 09:36:36.000000000 +0200 @@ -36,6 +36,12 @@ fflush(stdout); \ } +#define ansi_error_printf(format, args...) \ +{ \ + fprintf (stderr, "\r%s" format ANSI_RESET "\r\n", ansi_format, ## args); \ + fflush(stderr); \ +} + #define ansi_printf_raw(format, args...) \ { \ fprintf (stdout, "%s" format ANSI_RESET, ansi_format, ## args); \ @@ -56,6 +62,14 @@ print_tainted = false; \ } +#define tio_error_printf(format, args...) \ +{ \ + if (print_tainted) \ + putchar('\n'); \ + ansi_error_printf("[%s] " format, current_time(), ## args); \ + print_tainted = false; \ +} + #define error_printf(format, args...) \ snprintf(error[0], 1000, format, ## args); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/src/socket.c new/tio-1.38/src/socket.c --- old/tio-1.37/src/socket.c 1970-01-01 01:00:00.000000000 +0100 +++ new/tio-1.38/src/socket.c 2022-06-02 09:36:36.000000000 +0200 @@ -0,0 +1,197 @@ +/* + * tio - a simple serial terminal I/O tool + * + * Copyright (c) 2014-2022 Martin Lund + * Copyright (c) 2022 Google LLC + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "socket.h" +#include "options.h" +#include "print.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> + +#define MAX_SOCKET_CLIENTS 16 + +static int sockfd; +static int clientfds[MAX_SOCKET_CLIENTS]; + +static const char *socket_filename(void) +{ + /* skip 'unix:' */ + return option.socket + 5; +} + +static void socket_exit(void) +{ + unlink(socket_filename()); +} + +void socket_configure(void) +{ + if (!option.socket) + { + return; + } + + if (strncmp(option.socket, "unix:", 5) != 0) + { + error_printf("%s: Invalid socket scheme, must be 'unix:'", option.socket); + exit(EXIT_FAILURE); + } + + struct sockaddr_un sockaddr = {}; + if (strlen(socket_filename()) > sizeof(sockaddr.sun_path) - 1) + { + error_printf("Socket file path %s too long", option.socket); + exit(EXIT_FAILURE); + } + + sockaddr.sun_family = AF_UNIX; + strncpy(sockaddr.sun_path, socket_filename(), sizeof(sockaddr.sun_path) - 1); + + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) + { + error_printf("Failed to create socket: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + + unlink(socket_filename()); + if (bind(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) + { + error_printf("Failed to bind to socket %s: %s", socket_filename(), strerror(errno)); + exit(EXIT_FAILURE); + } + + if (listen(sockfd, MAX_SOCKET_CLIENTS) < 0) + { + error_printf("Failed to listen on socket %s: %s", socket_filename(), strerror(errno)); + exit(EXIT_FAILURE); + } + + memset(clientfds, -1, sizeof(clientfds)); + atexit(socket_exit); + + tio_printf("Listening on socket %s", socket_filename()); +} + +void socket_write(char input_char) +{ + if (!option.socket) + { + return; + } + + for (int i = 0; i != MAX_SOCKET_CLIENTS; ++i) + { + if (clientfds[i] != -1) + { + if (write(clientfds[i], &input_char, 1) <= 0) + { + error_printf_silent("Failed to write to socket: %s", strerror(errno)); + close(clientfds[i]); + clientfds[i] = -1; + } + } + } +} + +int socket_add_fds(fd_set *rdfs, bool connected) +{ + if (!option.socket) + { + return 0; + } + + int numclients = 0, maxfd = 0; + for (int i = 0; i != MAX_SOCKET_CLIENTS; ++i) + { + if (clientfds[i] != -1) + { + /* let clients block if they try to send while we're disconnected */ + if (connected) + { + FD_SET(clientfds[i], rdfs); + maxfd = MAX(maxfd, clientfds[i]); + } + numclients++; + } + } + /* don't bother to accept clients if we're already full */ + if (numclients != MAX_SOCKET_CLIENTS) + { + FD_SET(sockfd, rdfs); + maxfd = MAX(maxfd, sockfd); + } + return maxfd; +} + +bool socket_handle_input(fd_set *rdfs, char *output_char) +{ + if (!option.socket) + { + return false; + } + + if (FD_ISSET(sockfd, rdfs)) + { + int clientfd = accept(sockfd, NULL, NULL); + /* this loop should always succeed because we don't select on sockfd when full */ + for (int i = 0; i != MAX_SOCKET_CLIENTS; ++i) + { + if (clientfds[i] == -1) + { + clientfds[i] = clientfd; + break; + } + } + } + for (int i = 0; i != MAX_SOCKET_CLIENTS; ++i) + { + if (clientfds[i] != -1 && FD_ISSET(clientfds[i], rdfs)) + { + int status = read(clientfds[i], output_char, 1); + if (status == 0) + { + close(clientfds[i]); + clientfds[i] = -1; + continue; + } + if (status < 0) + { + error_printf_silent("Failed to read from socket: %s", strerror(errno)); + close(clientfds[i]); + clientfds[i] = -1; + continue; + } + /* match the behavior of a terminal in raw mode */ + if (*output_char == '\n') + { + *output_char = '\r'; + } + return true; + } + } + return false; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/src/socket.h new/tio-1.38/src/socket.h --- old/tio-1.37/src/socket.h 1970-01-01 01:00:00.000000000 +0100 +++ new/tio-1.38/src/socket.h 2022-06-02 09:36:36.000000000 +0200 @@ -0,0 +1,31 @@ +/* + * tio - a simple serial terminal I/O tool + * + * Copyright (c) 2014-2022 Martin Lund + * Copyright (c) 2022 Google LLC + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#pragma once + +#include <stdbool.h> +#include <sys/select.h> + +void socket_configure(void); +void socket_write(char input_char); +int socket_add_fds(fd_set *fds, bool connected); +bool socket_handle_input(fd_set *fds, char *output_char); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tio-1.37/src/tty.c new/tio-1.38/src/tty.c --- old/tio-1.37/src/tty.c 2022-04-13 18:00:34.000000000 +0200 +++ new/tio-1.38/src/tty.c 2022-06-02 09:36:36.000000000 +0200 @@ -31,6 +31,8 @@ #include <sys/param.h> #include <sys/file.h> #include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/un.h> #include <fcntl.h> #include <termios.h> #include <stdbool.h> @@ -45,6 +47,7 @@ #include "misc.h" #include "log.h" #include "error.h" +#include "socket.h" #ifdef HAVE_TERMIOS2 extern int setspeed2(int fd, int baudrate); @@ -164,7 +167,7 @@ break; case KEY_C: - tio_printf("Configuraiton:"); + tio_printf("Configuration:"); config_file_print(); options_print(); break; @@ -494,6 +497,7 @@ { fd_set rdfs; int status; + int maxfd; struct timeval tv; static char input_char, previous_char = 0; static bool first = true; @@ -517,26 +521,30 @@ FD_ZERO(&rdfs); FD_SET(STDIN_FILENO, &rdfs); + maxfd = MAX(STDIN_FILENO, socket_add_fds(&rdfs, false)); /* Block until input becomes available or timeout */ - status = select(STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv); + status = select(maxfd + 1, &rdfs, NULL, NULL, &tv); if (status > 0) { - /* Input from stdin ready */ - - /* Read one character */ - status = read(STDIN_FILENO, &input_char, 1); - if (status <= 0) + if (FD_ISSET(STDIN_FILENO, &rdfs)) { - error_printf("Could not read from stdin"); - exit(EXIT_FAILURE); - } + /* Input from stdin ready */ - /* Handle commands */ - handle_command_sequence(input_char, previous_char, NULL, NULL); + /* Read one character */ + status = read(STDIN_FILENO, &input_char, 1); + if (status <= 0) + { + error_printf("Could not read from stdin"); + exit(EXIT_FAILURE); + } - previous_char = input_char; + /* Handle commands */ + handle_command_sequence(input_char, previous_char, NULL, NULL); + previous_char = input_char; + } + socket_handle_input(&rdfs, NULL); } else if (status == -1) { error_printf("select() failed (%s)", strerror(errno)); @@ -686,19 +694,20 @@ } #endif - maxfd = MAX(fd, STDIN_FILENO) + 1; /* Maximum bit entry (fd) to test */ - /* Input loop */ while (true) { FD_ZERO(&rdfs); FD_SET(fd, &rdfs); FD_SET(STDIN_FILENO, &rdfs); + maxfd = MAX(fd, STDIN_FILENO); + maxfd = MAX(maxfd, socket_add_fds(&rdfs, true)); /* Block until input becomes available */ - status = select(maxfd, &rdfs, NULL, NULL, NULL); + status = select(maxfd + 1, &rdfs, NULL, NULL, NULL); if (status > 0) { + bool forward = false; if (FD_ISSET(fd, &rdfs)) { /* Input from tty device ready */ @@ -747,6 +756,8 @@ if (option.log) log_write(input_char); + socket_write(input_char); + print_tainted = true; if (input_char == '\n' && option.timestamp) @@ -760,7 +771,7 @@ } if (FD_ISSET(STDIN_FILENO, &rdfs)) { - bool forward = true; + forward = true; /* Input from stdin ready */ status = read(STDIN_FILENO, &input_char, 1); @@ -778,46 +789,50 @@ /* Handle commands */ handle_command_sequence(input_char, previous_char, &output_char, &forward); - if (forward) - { - /* Map output character */ - if ((output_char == 127) && (map_o_del_bs)) - output_char = '\b'; - if ((output_char == '\r') && (map_o_cr_nl)) - output_char = '\n'; - - /* Map newline character */ - if ((output_char == '\n' || output_char == '\r') && (map_o_nl_crnl)) { - const char *crlf = "\r\n"; - - optional_local_echo(crlf[0]); - optional_local_echo(crlf[1]); - status = write(fd, crlf, 2); - if (status < 0) - warning_printf("Could not write to tty device"); + /* Save previous key */ + previous_char = input_char; - tx_total += 2; - delay(option.output_delay); - } else - { - /* Send output to tty device */ - optional_local_echo(output_char); - status = write(fd, &output_char, 1); - if (status < 0) - warning_printf("Could not write to tty device"); - fsync(fd); + } + else + { + forward = socket_handle_input(&rdfs, &output_char); + } - /* Update transmit statistics */ - tx_total++; + if (forward) + { + /* Map output character */ + if ((output_char == 127) && (map_o_del_bs)) + output_char = '\b'; + if ((output_char == '\r') && (map_o_cr_nl)) + output_char = '\n'; + + /* Map newline character */ + if ((output_char == '\n' || output_char == '\r') && (map_o_nl_crnl)) { + const char *crlf = "\r\n"; + + optional_local_echo(crlf[0]); + optional_local_echo(crlf[1]); + status = write(fd, crlf, 2); + if (status < 0) + warning_printf("Could not write to tty device"); - /* Insert output delay */ - delay(option.output_delay); - } - } + tx_total += 2; + delay(option.output_delay); + } else + { + /* Send output to tty device */ + optional_local_echo(output_char); + status = write(fd, &output_char, 1); + if (status < 0) + warning_printf("Could not write to tty device"); + fsync(fd); - /* Save previous key */ - previous_char = input_char; + /* Update transmit statistics */ + tx_total++; + /* Insert output delay */ + delay(option.output_delay); + } } } else if (status == -1) {