Add option '--exec <command>' for running shell command

Runs shell command with I/O redirected to device.
This commit is contained in:
Martin Lund 2024-05-07 14:38:19 +02:00
parent 545d473220
commit e75e19eb00
9 changed files with 89 additions and 10 deletions

10
TODO
View file

@ -19,16 +19,6 @@
key navigation left/right and insert/overwrite support. Also history browsing key navigation left/right and insert/overwrite support. Also history browsing
pressing up/down. 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 * Allow tio to connect to socket
After some more consideration I think it makes sense to support connecting to a After some more consideration I think it makes sense to support connecting to a

View file

@ -358,6 +358,11 @@ Run script on connect once, always, or never.
Default value is "always". Default value is "always".
.TP
.BR "\-\-exec \fI<command>
Execute shell command with I/O redirected to device
.TP .TP
.BR \-v ", " \-\-version .BR \-v ", " \-\-version
@ -571,6 +576,9 @@ Run script from string
Run script from file Run script from file
.IP "\fBscript-run" .IP "\fBscript-run"
Run script on connect Run script on connect
.IP "\fBexec"
Execute shell command with I/O redirected to device
.SH "CONFIGURATION FILE EXAMPLES" .SH "CONFIGURATION FILE EXAMPLES"

View file

@ -45,6 +45,7 @@ _tio()
--script \ --script \
--script-file \ --script-file \
--script-run \ --script-run \
--exec \
-v --version \ -v --version \
-h --help" -h --help"
@ -179,6 +180,10 @@ _tio()
COMPREPLY=( $(compgen -W "once always never" -- ${cur}) ) COMPREPLY=( $(compgen -W "once always never" -- ${cur}) )
return 0 return 0
;; ;;
--exec)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
-v | --version) -v | --version)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0 return 0

View file

@ -282,6 +282,7 @@ static void config_parse_keys(GKeyFile *key_file, char *group)
g_free((void *)string); g_free((void *)string);
string = NULL; string = NULL;
} }
config_get_string(key_file, group, "exec", &option.exec, NULL);
config_get_string(key_file, group, "prefix-ctrl-key", &string, NULL); config_get_string(key_file, group, "prefix-ctrl-key", &string, NULL);
if (string != NULL) if (string != NULL)
{ {

View file

@ -33,6 +33,7 @@
#include <time.h> #include <time.h>
#include <errno.h> #include <errno.h>
#include <sys/poll.h> #include <sys/poll.h>
#include <sys/wait.h>
#include <termios.h> #include <termios.h>
#include <fnmatch.h> #include <fnmatch.h>
#include "error.h" #include "error.h"
@ -199,3 +200,61 @@ bool match_patterns(const char *string, const char *patterns)
free(patterns_copy); free(patterns_copy);
return false; 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;
}

View file

@ -34,3 +34,4 @@ char *base62_encode(unsigned long num);
int read_poll(int fd, void *data, size_t len, int timeout); int read_poll(int fd, void *data, size_t len, int timeout);
double get_current_time(void); double get_current_time(void);
bool match_patterns(const char *string, const char *patterns); bool match_patterns(const char *string, const char *patterns);
int execute_shell_command(int fd, const char *command);

View file

@ -69,6 +69,7 @@ enum opt_t
OPT_EXCLUDE_DEVICES, OPT_EXCLUDE_DEVICES,
OPT_EXCLUDE_DRIVERS, OPT_EXCLUDE_DRIVERS,
OPT_EXCLUDE_TIDS, OPT_EXCLUDE_TIDS,
OPT_EXEC,
}; };
/* Default options */ /* Default options */
@ -121,6 +122,7 @@ struct option_t option =
.exclude_tids = NULL, .exclude_tids = NULL,
.hex_n_value = 0, .hex_n_value = 0,
.vt100 = false, .vt100 = false,
.exec = NULL,
}; };
void option_print_help(char *argv[]) void option_print_help(char *argv[])
@ -167,6 +169,7 @@ void option_print_help(char *argv[])
printf(" --script <string> Run script from string\n"); printf(" --script <string> Run script from string\n");
printf(" --script-file <filename> Run script from file\n"); printf(" --script-file <filename> Run script from file\n");
printf(" --script-run once|always|never Run script on connect (default: always)\n"); printf(" --script-run once|always|never Run script on connect (default: always)\n");
printf(" --exec <command> Execute shell command with I/O redirected to device\n");
printf(" -v, --version Display version\n"); printf(" -v, --version Display version\n");
printf(" -h, --help Display help\n"); printf(" -h, --help Display help\n");
printf("\n"); printf("\n");
@ -815,6 +818,7 @@ void options_parse(int argc, char *argv[])
{"script", required_argument, 0, OPT_SCRIPT }, {"script", required_argument, 0, OPT_SCRIPT },
{"script-file", required_argument, 0, OPT_SCRIPT_FILE }, {"script-file", required_argument, 0, OPT_SCRIPT_FILE },
{"script-run", required_argument, 0, OPT_SCRIPT_RUN }, {"script-run", required_argument, 0, OPT_SCRIPT_RUN },
{"exec", required_argument, 0, OPT_EXEC },
{"version", no_argument, 0, 'v' }, {"version", no_argument, 0, 'v' },
{"help", no_argument, 0, 'h' }, {"help", no_argument, 0, 'h' },
{"complete-profiles", no_argument, 0, OPT_COMPLETE_PROFILES }, {"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); option_parse_script_run(optarg, &option.script_run);
break; break;
case OPT_EXEC:
option.exec = optarg;
break;
case 'v': case 'v':
printf("tio v%s\n", VERSION); printf("tio v%s\n", VERSION);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);

View file

@ -96,6 +96,7 @@ struct option_t
char *exclude_tids; char *exclude_tids;
int hex_n_value; int hex_n_value;
bool vt100; bool vt100;
char *exec;
}; };
extern struct option_t option; extern struct option_t option;

View file

@ -2277,6 +2277,12 @@ int tty_connect(void)
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
if (option.exec != NULL)
{
int status = execute_shell_command(device_fd, option.exec);
exit(status);
}
/* Input loop */ /* Input loop */
while (true) while (true)
{ {