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 @details 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/stat.h>
26#include <sys/types.h>
27#include <sys/wait.h>
28#include <termios.h>
29#include <unistd.h>
30
31int full_screen_fork_exec(char **);
32int full_screen_shell(char *);
33int shell(char *);
34int fork_exec(char **);
35int nf_error(int ec, char *s);
36int fork_detach_execvp(char **);
37/** @brief Execute a command in full screen mode
38 @ingroup exec
39 @param argv - array of arguments for the command to execute
40 @return the return code from the executed command
41 @details Clear the screen,
42 move the cursor to the bottom, and update the screen before executing
43 the command.
44 After the command completes, clear the screen, move the cursor to the
45 top, update the screen, and restore the windows. */
46int full_screen_fork_exec(char **argv) {
47 int rc;
48
49 fflush(stderr);
50 wmove(stdscr, LINES - 1, 0);
51 rc = fork_exec(argv);
52 return (rc);
53}
54/** @brief Execute a shell command in full screen mode
55 @ingroup exec
56 @param shellCmdPtr - pointer to the shell command string
57 @return the return code from the executed shell command
58 @details Clear the screen, move the cursor to the top, and update the
59 screen before executing the shell command. After the command completes,
60 restore the windows.
61 */
62int full_screen_shell(char *shellCmdPtr) {
63 int rc;
64
65 fflush(stderr);
66 werase(stdscr);
67 wmove(stdscr, 0, 0);
68 rc = shell(shellCmdPtr);
70 return (rc);
71}
72/** @brief Execute a shell command
73 @ingroup exec
74 @param shellCmdPtr - pointer to the shell command string
75 @return the return code from the executed shell command
76 @details Executes the command string using the user's shell.
77 If the SHELL environment variable is not set, use /bin/sh. */
78int shell(char *shellCmdPtr) {
79 int Eargc;
80 char *Eargv[MAXARGS];
81 char *shellPtr;
82 int rc;
83
84 Eargc = 0;
85 shellPtr = getenv("SHELL");
86 if (shellPtr == nullptr || *shellPtr == '\0')
87 shellPtr = DEFAULTSHELL;
88 Eargv[Eargc++] = strdup(shellPtr);
89 Eargv[Eargc++] = "-c";
90 Eargv[Eargc++] = shellCmdPtr;
91 Eargv[Eargc++] = nullptr;
92 rc = fork_exec(Eargv);
93 free(Eargv[0]);
94 return (rc);
95}
96/** @brief Fork and exec a command
97 @ingroup exec
98 @param argv - array of arguments for the command to execute
99 @return the return code from the executed command, or -1 on error
100 @details Captures and restores terminal settings around the fork and exec.
101 Sets signal handlers to default in the child process.
102 Waits for the child process to complete in the parent process.
103 Handles errors from fork and execvp, and reports child exit status.
104 Restores curses mode and keypad settings after execution.
105 Restores window states after execution.
106 Uses a temporary string buffer tmp_str for error messages.
107 Uses Perror for error reporting.
108 Uses sig_dfl_mode and sig_prog_mode for signal handling.
109 Uses capture_curses_tioctl and restore_curses_tioctl for terminal
110 settings.
111 Uses restore_shell_tioctl for shell terminal settings.
112 Uses waitpid to wait for the child process.
113 Uses WIFEXITED, WEXITSTATUS, WIFSIGNALED, and WTERMSIG to interpret
114 child status.
115 Uses keypad to manage keypad mode in curses.
116 Uses restore_wins to restore window states.
117 Uses errno for error codes.
118 Uses pid_t for process IDs.
119 Uses standard file descriptors STDIN_FILENO, STDOUT_FILENO,
120 STDERR_FILENO.
121 Uses execvp for executing the command.
122 Uses fork for creating a new process.
123 Uses ssnprintf for formatting error messages.
124 Uses switch-case for handling fork results.
125 Uses default shell if SHELL environment variable is not set. */
126int fork_exec(char **argv) {
127 char tmp_str[MAXLEN];
128 pid_t pid;
129 int status;
130 int rc;
131
132 if (argv[0] == 0) {
133 Perror("fork_exec: missing argument for execvp");
134 return (-1);
135 }
137 curs_set(1);
140 endwin();
142 tmp_str[0] = '\0';
143 pid = fork();
144 if (pid < 0) {
146 keypad(stdscr, true);
147 ssnprintf(tmp_str, sizeof(tmp_str), "fork failed: %s, errno: %d",
148 argv[0], errno);
149 Perror(tmp_str);
150 return (-1);
151 } else if (pid == 0) {
154 execvp(argv[0], argv);
155 fprintf(stderr, "execvp failed: %s, errno: %d\n", argv[0], errno);
156 exit(EXIT_FAILURE);
157 }
158 waitpid(pid, &status, 0);
159 if (WIFEXITED(status)) {
160 rc = WEXITSTATUS(status);
161 } else if (WIFSIGNALED(status)) {
162 ssnprintf(tmp_str, sizeof(tmp_str), "Child process terminated by signal: %d",
163 WTERMSIG(status));
164 Perror(tmp_str);
165 rc = -1;
166 } else {
167 ssnprintf(tmp_str, sizeof(tmp_str), "Child process terminated abnormally");
168 Perror(tmp_str);
169 rc = -1;
170 }
171 reset_prog_mode();
173 return (rc);
174}
175/** @brief Fork, detach, and exec a command
176 @ingroup exec
177 @param eargv - array of arguments for the command to execute
178 @return 0 on success, or exits on failure
179 @details Forks a new process, detaches it from the terminal, and executes
180 the specified command using execvp.
181 Closes standard input, output, and error file descriptors in the child
182 process.
183 Redirects standard input, output, and error to /dev/null in the child
184 process.
185 Closes all other file descriptors in the child process.
186 Sets the session ID for the child process to detach it from the terminal.
187 Restores curses mode and signal handling in the parent process after
188 forking.
189 Restores window states in the parent process after forking. */
190int fork_detach_execvp(char **eargv) {
191 pid_t pid = fork();
193 curs_set(1);
195
196 if (pid < 0) {
197 fprintf(stderr, "First fork failed: %s\n", strerror(errno));
198 exit(EXIT_FAILURE);
199 }
200 if (pid == 0) {
201 if (setsid() < 0) {
202 fprintf(stderr, "Set session ID failed: %s\n", strerror(errno));
203 exit(EXIT_FAILURE);
204 }
205 close(STDIN_FILENO);
206 close(STDOUT_FILENO);
207 close(STDERR_FILENO);
208 int dev_null = open("/dev/null", O_RDWR);
209 if (dev_null != -1) {
210 dup2(dev_null, STDIN_FILENO);
211 dup2(dev_null, STDOUT_FILENO);
212 dup2(dev_null, STDERR_FILENO);
213 if (dev_null > 2) {
214 close(dev_null);
215 }
216 }
217 long max_fd = sysconf(_SC_OPEN_MAX);
218 for (long fd = 3; fd < max_fd; fd++)
219 close(fd);
220 execvp(eargv[0], eargv);
221 perror("execvp failed");
222 exit(EXIT_FAILURE);
223 }
227 return 0;
228}
#define DEFAULTSHELL
Definition cm.h:284
#define MAXARGS
Definition cm.h:48
char * stdio_names(char *, char *)
Definition futil.c:1064
char stdio_names_str[MAXLEN]
Definition futil.c:134
#define MAXLEN
Definition curskeys.c:15
void restore_wins()
Restore all windows after a screen resize.
Definition dwin.c:1225
int nf_error(int, char *)
Display error message and wait for key press.
Definition dwin.c:2006
int Perror(char *)
Display a simple error message window or print to stderr.
Definition dwin.c:1526
int fork_exec(char **)
Fork and exec a command.
Definition exec.c:126
int shell(char *)
Execute a shell command.
Definition exec.c:78
int full_screen_fork_exec(char **)
Execute a command in full screen mode.
Definition exec.c:46
int full_screen_shell(char *)
Execute a shell command in full screen mode.
Definition exec.c:62
int fork_detach_execvp(char **)
Fork, set new session ID, close files, and execute detached command.
Definition detach.c:29
size_t ssnprintf(char *, size_t, const char *,...)
ssnprintf was designed to be a safer alternative to snprintf.
Definition futil.c:420
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