diff --git a/src/misc.c b/src/misc.c index ec39f51..eff7e3d 100644 --- a/src/misc.c +++ b/src/misc.c @@ -28,6 +28,9 @@ #include #include #include "print.h" +#include "misc.h" + +static pid_t shell_command_pid = 0; void delay(long ms) { @@ -111,7 +114,7 @@ int read_poll(int fd, void *data, size_t len, int timeout) return 0; } -ssize_t write_poll(int fd, void *data, size_t len, int timeout) +ssize_t write_poll(int fd, const void *data, size_t len, int timeout) { struct pollfd fds; ssize_t ret = 0; @@ -234,46 +237,72 @@ bool match_patterns(const char *string, const char *patterns) // specified filedescriptor, and runs command. int execute_shell_command(int fd, const char *command) { -#define READ_END 0 -#define WRITE_END 1 - pid_t pid; + #define READ_END 0 + #define WRITE_END 1 int status; - int pipe_fd[2]; + int pipefd_c2p[2]; + int pipefd_p2c[2]; + +#if defined(__linux__) + static bool done_once = false; + if (!done_once) + { + atexit(&terminate_shell_command); + done_once = true; + } +#endif // Create Pipes - if (pipe(pipe_fd) == -1) + if (pipe(pipefd_c2p) == -1 || pipe(pipefd_p2c) == -1) { tio_error_print("pipe() failed (%s)", strerror(errno)); exit(EXIT_FAILURE); } // Fork a child process - pid = fork(); - if (pid == -1) + shell_command_pid = fork(); + if (shell_command_pid == -1) { // Error occurred tio_error_print("fork() failed (%s)", strerror(errno)); exit(EXIT_FAILURE); } - else if (pid == 0) + else if (shell_command_pid == 0) { // Child process + close(pipefd_c2p[READ_END]); + close(pipefd_p2c[WRITE_END]); tio_printf("Executing shell command '%s'", command); - // Redirect stdout and stderr to the file descriptor - close(pipe_fd[READ_END]); - if (dup2(pipe_fd[WRITE_END], STDOUT_FILENO) == -1 || dup2(pipe_fd[WRITE_END], STDERR_FILENO) == -1) + // Redirect stdin and stdout to the parent-pipe + if (dup2(pipefd_c2p[WRITE_END], STDOUT_FILENO) == -1 || + dup2(pipefd_p2c[READ_END], STDIN_FILENO) == -1) { tio_error_print("dup2() failed (%s)", strerror(errno)); exit(EXIT_FAILURE); } + // command prefix '?' excludes stderr from redirection + if (command[0] == '?') + { + command += 1; + } + else + { + if (dup2(pipefd_c2p[WRITE_END], 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 - close(pipe_fd[WRITE_END]); + close(pipefd_c2p[WRITE_END]); + close(pipefd_p2c[READ_END]); perror("execlp"); tio_error_print("execlp() failed (%s)", strerror(errno)); exit(EXIT_FAILURE); @@ -281,23 +310,69 @@ int execute_shell_command(int fd, const char *command) else { // Parent process + fd_set rdfs; + int maxfd; char buf[BUFSIZ]; int bytes; - // Read pipe and transfer to tty device. - close(pipe_fd[WRITE_END]); - while ( (bytes = read(pipe_fd[READ_END], buf, sizeof(buf))) > 0) + close(pipefd_c2p[WRITE_END]); + close(pipefd_p2c[READ_END]); + + while (true) { - if (tty_write(fd, buf, bytes) < 0) + FD_ZERO(&rdfs); + FD_SET(fd, &rdfs); + FD_SET(pipefd_c2p[READ_END], &rdfs); + maxfd = MAX(fd, pipefd_c2p[READ_END]); + + /* Block until input becomes available or timeout */ + status = select(maxfd + 1, &rdfs, NULL, NULL, NULL); + if (status < 0) { - tio_warning_printf("Could not write to tty device"); + tio_warning_printf("select() failed(%s)", strerror(errno)); + break; + } + + if (FD_ISSET(fd, &rdfs)) + { + bytes = read(fd, buf, sizeof(buf)); + if (bytes <= 0) + { + tio_warning_printf("Could not read from tty device"); + break; + } + rx_total += bytes; + write(pipefd_p2c[WRITE_END], buf, bytes); + } + + if (FD_ISSET(pipefd_c2p[READ_END], &rdfs)) + { + // Read pipe and transfer to tty device. + bytes = read(pipefd_c2p[READ_END], buf, sizeof(buf)); + if (bytes < 0) + { + tio_warning_printf("Could not write to tty device"); + } + else if (bytes == 0) + { + // Shell command has finished. + break; + } + + if (tty_write(fd, buf, bytes) < 0) + { + tio_warning_printf("Could not write to tty device"); + } + tty_sync(fd); } } - tty_sync(fd); - close(pipe_fd[READ_END]); + + close(pipefd_p2c[WRITE_END]); + close(pipefd_c2p[READ_END]); // Wait for the child process to finish - waitpid(pid, &status, 0); + waitpid(shell_command_pid, &status, 0); + shell_command_pid = 0; if (WIFEXITED(status)) { @@ -313,6 +388,26 @@ int execute_shell_command(int fd, const char *command) return 0; } +#if defined(__linux__) + +void terminate_shell_command(void) +{ + // If previous shell command pid is remain, terminate it. + if (shell_command_pid != 0) + { + #define PKILL_BUFSIZ 80 + char pkill_buf[PKILL_BUFSIZ] = {0}; + int bytes; + bytes = snprintf(pkill_buf, PKILL_BUFSIZ, "/usr/bin/pkill -P %d", shell_command_pid); + if (bytes > 0 && bytes < PKILL_BUFSIZ) + { + system(pkill_buf); + } + } +} + +#endif + void clear_line() { printf("\r\033[K"); diff --git a/src/misc.h b/src/misc.h index 9f26d82..c064bde 100644 --- a/src/misc.h +++ b/src/misc.h @@ -38,3 +38,7 @@ double get_current_time(void); bool match_patterns(const char *string, const char *patterns); int execute_shell_command(int fd, const char *command); void clear_line(); + +#if defined(__linux__) +void terminate_shell_command(void); +#endif diff --git a/src/tty.c b/src/tty.c index 441ea3d..3cf6628 100644 --- a/src/tty.c +++ b/src/tty.c @@ -175,7 +175,7 @@ char key_hit = 0xff; const char* device_name = NULL; GList *device_list = NULL; static struct termios tio, tio_raw, tio_old, stdout_new, stdout_old, stdin_new, stdin_old; -static unsigned long rx_total = 0, tx_total = 0; +unsigned long rx_total = 0, tx_total = 0; static bool connected = false; static bool standard_baudrate = true; static void (*printchar)(char c); @@ -557,6 +557,15 @@ void *tty_stdin_input_thread(void *arg) tio_printf("Flushed data I/O buffers"); tcflush(device_fd, TCIOFLUSH); break; +#if defined(__linux__) + case KEY_SHIFT_R: + if (state == STATE_EXEC_SHELL_COMMAND) + { + tio_printf("Terminated shell command"); + terminate_shell_command(); + } + break; +#endif default: break; } diff --git a/src/tty.h b/src/tty.h index 5135394..987e57f 100644 --- a/src/tty.h +++ b/src/tty.h @@ -81,6 +81,7 @@ typedef enum extern const char *device_name; extern bool interactive_mode; extern state_t state; +extern unsigned long rx_total, tx_total; void stdout_configure(void); void stdin_configure(void);