C-Menu 0.2.9
A User Interface Toolkit
Loading...
Searching...
No Matches
exec.c
Go to the documentation of this file.
1/** @file exec.c
2 @brief Functions to execute external commands
3 @author Bill Waller
4 Copyright (c) 2025
5 MIT License
6 billxwaller@gmail.com
7 @date 2026-02-09
8 */
9
10/** @defgroup exec External Commands
11 @brief This module provides functions to execute external commands
12 @note handles terminal settings, signal handling, and error reporting to
13 ensure a smooth user experience when executing commands from within the
14 application. The main functions include full_screen_fork_exec,
15 full_screen_shell, and fork_exec, which manage the execution of commands
16 while maintaining the integrity of the application's user interface.
17 */
18
19#include <cm.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <stddef.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/types.h>
26#include <sys/wait.h>
27#include <termios.h>
28#include <unistd.h>
29
30int full_screen_fork_exec(char **);
31int full_screen_shell(char *);
32int shell(char *);
33int fork_exec(char **);
34int nf_error(int ec, char *s);
35/** @brief Execute a command in full screen mode
36 @ingroup exec
37 @param argv - array of arguments for the command to execute
38 @return the return code from the executed command
39 @note Clear the screen,
40 @note move the cursor to the bottom, and refresh the screen before executing
41 the command.
42 @note After the command completes, clear the screen, move the cursor to the
43 top, refresh the screen, and restore the windows. */
44int full_screen_fork_exec(char **argv) {
45 int rc;
46
47 fflush(stderr);
48 wmove(stdscr, LINES - 1, 0);
49 rc = fork_exec(argv);
50 return (rc);
51}
52/** @brief Execute a shell command in full screen mode
53 @ingroup exec
54 @param shellCmdPtr - pointer to the shell command string
55 @return the return code from the executed shell command
56 @note Clear the screen, move the cursor to the top, and refresh the screen
57 before executing the shell command. @note After the command completes,
58 restore the windows.
59 */
60int full_screen_shell(char *shellCmdPtr) {
61 int rc;
62
63 fflush(stderr);
64 werase(stdscr);
65 wmove(stdscr, 0, 0);
66 wrefresh(stdscr);
67 rc = shell(shellCmdPtr);
68 touchwin(stdscr);
69 wnoutrefresh(stdscr);
71 wrefresh(stdscr);
72 return (rc);
73}
74/** @brief Execute a shell command
75 @ingroup exec
76 @param shellCmdPtr - pointer to the shell command string
77 @return the return code from the executed shell command
78 @note Executes the command string using the user's shell.
79 @note If the SHELL environment variable is not set, use /bin/sh. */
80int shell(char *shellCmdPtr) {
81 int Eargc;
82 char *Eargv[MAXARGS];
83 char *shellPtr;
84 int rc;
85
86 Eargc = 0;
87 shellPtr = getenv("SHELL");
88 if (shellPtr == nullptr || *shellPtr == '\0')
89 shellPtr = DEFAULTSHELL;
90 Eargv[Eargc++] = strdup(shellPtr);
91 Eargv[Eargc++] = "-c";
92 Eargv[Eargc++] = shellCmdPtr;
93 Eargv[Eargc++] = nullptr;
94 rc = fork_exec(Eargv);
95 free(Eargv[0]);
96 return (rc);
97}
98/** @brief Fork and exec a command
99 @ingroup exec
100 @param argv - array of arguments for the command to execute
101 @return the return code from the executed command, or -1 on error
102 @note Captures and restores terminal settings around the fork and exec.
103 @note Sets signal handlers to default in the child process.
104 @note Waits for the child process to complete in the parent process.
105 @note Handles errors from fork and execvp, and reports child exit status.
106 @note Restores curses mode and keypad settings after execution.
107 @note Restores window states after execution.
108 @note Uses a temporary string buffer tmp_str for error messages.
109 @note Uses Perror for error reporting.
110 @note Uses sig_dfl_mode and sig_prog_mode for signal handling.
111 @note Uses capture_curses_tioctl and restore_curses_tioctl for terminal
112 settings.
113 @note Uses restore_shell_tioctl for shell terminal settings.
114 @note Uses waitpid to wait for the child process.
115 @note Uses WIFEXITED, WEXITSTATUS, WIFSIGNALED, and WTERMSIG to interpret
116 child status.
117 @note Uses keypad to manage keypad mode in curses.
118 @note Uses restore_wins to restore window states.
119 @note Uses errno for error codes.
120 @note Uses pid_t for process IDs.
121 @note Uses standard file descriptors STDIN_FILENO, STDOUT_FILENO,
122 STDERR_FILENO.
123 @note Uses execvp for executing the command.
124 @note Uses fork for creating a new process.
125 @note Uses ssnprintf for formatting error messages.
126 @note Uses switch-case for handling fork results.
127 @note Uses default shell if SHELL environment variable is not set. */
128int fork_exec(char **argv) {
129 char tmp_str[MAXLEN];
130 pid_t pid;
131 int status;
132 int rc;
133
134 if (argv[0] == 0) {
135 Perror("fork_exec: missing argument for execvp");
136 return (-1);
137 }
139 curs_set(1);
141
142 tmp_str[0] = '\0';
143 pid = fork();
144 switch (pid) {
145 case -1: // parent fork failed
147 keypad(stdscr, true);
148 ssnprintf(tmp_str, sizeof(tmp_str), "fork failed: %s, errno: %d",
149 argv[0], errno);
150 Perror(tmp_str);
151 return (-1);
152 case 0: // child
154 werase(stdscr);
155 wrefresh(stdscr);
156 execvp(argv[0], argv);
159 keypad(stdscr, true);
160 ssnprintf(tmp_str, sizeof(tmp_str), "execvp failed: %s, errno: %d",
161 argv[0], errno);
162 Perror(tmp_str);
163 exit(-1);
164 default: // parent
165 rc = 0;
168 waitpid(pid, &status, 0);
169 if (WIFEXITED(status)) {
170 rc = WEXITSTATUS(status);
171 if (rc != 0) {
172 keypad(stdscr, true);
173 ssnprintf(tmp_str, sizeof(tmp_str),
174 "Child %s exited with status %d", argv[0], rc);
175 }
176 } else {
177 if (WIFSIGNALED(status)) {
178 rc = WTERMSIG(status);
179 keypad(stdscr, true);
180 ssnprintf(tmp_str, sizeof(tmp_str),
181 "Child %s terminated by signal %d", argv[0], rc);
182 } else {
183 keypad(stdscr, true);
184 ssnprintf(tmp_str, sizeof(tmp_str),
185 "Child %s terminated abnormally", argv[0]);
186 }
187 }
188 break;
189 }
192 touchwin(stdscr);
193 wnoutrefresh(stdscr);
194 wrefresh(stdscr);
196 tmp_str[0] = '\0';
197 if (tmp_str[0] != '\0') {
198 Perror(tmp_str);
199 }
200 return (rc);
201}
#define DEFAULTSHELL
Definition cm.h:189
#define MAXARGS
Definition cm.h:30
#define nullptr
Definition cm.h:25
#define MAXLEN
Definition curskeys.c:15
void restore_wins()
Restore all windows after a screen resize.
Definition dwin.c:939
int nf_error(int, char *)
Display error message and wait for key press.
Definition dwin.c:1325
int Perror(char *)
Display a simple error message window or print to stderr.
Definition dwin.c:1115
int fork_exec(char **)
Fork and exec a command.
Definition exec.c:128
int shell(char *)
Execute a shell command.
Definition exec.c:80
int full_screen_fork_exec(char **)
Execute a command in full screen mode.
Definition exec.c:44
int full_screen_shell(char *)
Execute a shell command in full screen mode.
Definition exec.c:60
size_t ssnprintf(char *, size_t, const char *,...)
ssnprintf was designed to be a safer alternative to snprintf.
Definition futil.c:156
bool restore_curses_tioctl()
restore_curses_tioctl() - restore curses terminal settings
Definition scriou.c:81
bool capture_curses_tioctl()
capture_curses_tioctl() - capture curses terminal settings
Definition scriou.c:68
bool restore_shell_tioctl()
restore_shell_tioctl() - restore shell terminal settings
Definition scriou.c:56
void sig_dfl_mode()
Set signal handlers to default behavior.
Definition sig.c:42
void sig_prog_mode()
Set up signal handlers for interrupt signals.
Definition sig.c:62