From e75e19eb0054704c953b6480234b54b76d48e107 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 7 May 2024 14:38:19 +0200 Subject: [PATCH] Add option '--exec ' for running shell command Runs shell command with I/O redirected to device. --- TODO | 10 ------- man/tio.1.in | 8 ++++++ src/bash-completion/tio.in | 5 ++++ src/configfile.c | 1 + src/misc.c | 59 ++++++++++++++++++++++++++++++++++++++ src/misc.h | 1 + src/options.c | 8 ++++++ src/options.h | 1 + src/tty.c | 6 ++++ 9 files changed, 89 insertions(+), 10 deletions(-) diff --git a/TODO b/TODO index bf36c62..d1609fa 100644 --- a/TODO +++ b/TODO @@ -19,16 +19,6 @@ key navigation left/right and insert/overwrite support. Also history browsing pressing up/down. - * Support for running external command - - Add key command e.g. 'ctrl-t r' which prompts user to run external command. - The command will be run in a process which stdin/stdout is redirected to the - serial port. - - This is the first step towards maybe also adding automatic support for - x/y/zmodem data transfer protocols by calling external programs such as - rb/sb, rx/sx, rz/sz, etc. - * Allow tio to connect to socket After some more consideration I think it makes sense to support connecting to a diff --git a/man/tio.1.in b/man/tio.1.in index 4ea2499..7e8d7b7 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -358,6 +358,11 @@ Run script on connect once, always, or never. Default value is "always". +.TP +.BR "\-\-exec \fI + +Execute shell command with I/O redirected to device + .TP .BR \-v ", " \-\-version @@ -571,6 +576,9 @@ Run script from string Run script from file .IP "\fBscript-run" Run script on connect +.IP "\fBexec" +Execute shell command with I/O redirected to device + .SH "CONFIGURATION FILE EXAMPLES" diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 75a207c..a07e728 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -45,6 +45,7 @@ _tio() --script \ --script-file \ --script-run \ + --exec \ -v --version \ -h --help" @@ -179,6 +180,10 @@ _tio() COMPREPLY=( $(compgen -W "once always never" -- ${cur}) ) return 0 ;; + --exec) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; -v | --version) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 diff --git a/src/configfile.c b/src/configfile.c index 5d43b61..cb73146 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -282,6 +282,7 @@ static void config_parse_keys(GKeyFile *key_file, char *group) g_free((void *)string); string = NULL; } + config_get_string(key_file, group, "exec", &option.exec, NULL); config_get_string(key_file, group, "prefix-ctrl-key", &string, NULL); if (string != NULL) { diff --git a/src/misc.c b/src/misc.c index 630671e..9342769 100644 --- a/src/misc.c +++ b/src/misc.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include "error.h" @@ -199,3 +200,61 @@ bool match_patterns(const char *string, const char *patterns) free(patterns_copy); return false; } + +// Function that forks subprocess, redirects its stdin and stdout to the +// specified filedescriptor, and runs command. +int execute_shell_command(int fd, const char *command) +{ + pid_t pid; + int status; + + // Fork a child process + pid = fork(); + if (pid == -1) + { + // Error occurred + tio_error_print("fork() failed (%s)", strerror(errno)); + exit(EXIT_FAILURE); + } + else if (pid == 0) + { + // Child process + + tio_printf("Executing shell command '%s'", command); + + // Redirect stdout and stderr to the file descriptor + if (dup2(fd, STDOUT_FILENO) == -1 || dup2(fd, STDERR_FILENO) == -1) + { + tio_error_print("dup2() failed (%s)", strerror(errno)); + exit(EXIT_FAILURE); + } + + // Execute the shell command + execl("/bin/sh", "sh", "-c", command, (char *)NULL); + + // If execlp() returns, it means an error occurred + perror("execlp"); + tio_error_print("execlp() failed (%s)", strerror(errno)); + exit(EXIT_FAILURE); + } + else + { + // Parent process + + // Wait for the child process to finish + waitpid(pid, &status, 0); + + if (WIFEXITED(status)) + { + tio_printf("Command exited with status %d", WEXITSTATUS(status)); + return WEXITSTATUS(status); + } + else + { + tio_error_printf("Child process exited abnormally\n"); + return -1; + } + } + + return 0; +} diff --git a/src/misc.h b/src/misc.h index edbb511..9f8a1d4 100644 --- a/src/misc.h +++ b/src/misc.h @@ -34,3 +34,4 @@ char *base62_encode(unsigned long num); int read_poll(int fd, void *data, size_t len, int timeout); double get_current_time(void); bool match_patterns(const char *string, const char *patterns); +int execute_shell_command(int fd, const char *command); diff --git a/src/options.c b/src/options.c index c0316ce..0eefe7e 100644 --- a/src/options.c +++ b/src/options.c @@ -69,6 +69,7 @@ enum opt_t OPT_EXCLUDE_DEVICES, OPT_EXCLUDE_DRIVERS, OPT_EXCLUDE_TIDS, + OPT_EXEC, }; /* Default options */ @@ -121,6 +122,7 @@ struct option_t option = .exclude_tids = NULL, .hex_n_value = 0, .vt100 = false, + .exec = NULL, }; void option_print_help(char *argv[]) @@ -167,6 +169,7 @@ void option_print_help(char *argv[]) printf(" --script Run script from string\n"); printf(" --script-file Run script from file\n"); printf(" --script-run once|always|never Run script on connect (default: always)\n"); + printf(" --exec Execute shell command with I/O redirected to device\n"); printf(" -v, --version Display version\n"); printf(" -h, --help Display help\n"); printf("\n"); @@ -815,6 +818,7 @@ void options_parse(int argc, char *argv[]) {"script", required_argument, 0, OPT_SCRIPT }, {"script-file", required_argument, 0, OPT_SCRIPT_FILE }, {"script-run", required_argument, 0, OPT_SCRIPT_RUN }, + {"exec", required_argument, 0, OPT_EXEC }, {"version", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, {"complete-profiles", no_argument, 0, OPT_COMPLETE_PROFILES }, @@ -985,6 +989,10 @@ void options_parse(int argc, char *argv[]) option_parse_script_run(optarg, &option.script_run); break; + case OPT_EXEC: + option.exec = optarg; + break; + case 'v': printf("tio v%s\n", VERSION); exit(EXIT_SUCCESS); diff --git a/src/options.h b/src/options.h index 31e807a..ee73b8e 100644 --- a/src/options.h +++ b/src/options.h @@ -96,6 +96,7 @@ struct option_t char *exclude_tids; int hex_n_value; bool vt100; + char *exec; }; extern struct option_t option; diff --git a/src/tty.c b/src/tty.c index 8aa1606..3a3d80a 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2277,6 +2277,12 @@ int tty_connect(void) exit(EXIT_SUCCESS); } + if (option.exec != NULL) + { + int status = execute_shell_command(device_fd, option.exec); + exit(status); + } + /* Input loop */ while (true) {