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 fork_detach_execvp(char **);
36/** @brief Execute a command in full screen mode
37 @ingroup exec
38 @param argv - array of arguments for the command to execute
39 @return the return code from the executed command
40 @details Clear the screen,
41 move the cursor to the bottom, and update the screen before executing
42 the command.
43 After the command completes, clear the screen, move the cursor to the
44 top, update the screen, and restore the windows. */
45int full_screen_fork_exec(char **argv) {
46 int rc;
47 rc = fork_exec(argv);
48 return (rc);
49}
50/** @brief Execute a shell command in full screen mode
51 @ingroup exec
52 @param shellCmdPtr - pointer to the shell command string
53 @return the return code from the executed shell command
54 @details Clear the screen, move the cursor to the top, and update the
55 screen before executing the shell command. After the command completes,
56 restore the windows.
57 */
58int full_screen_shell(char *shellCmdPtr) {
59 int rc;
60
61 fflush(stderr);
64 rc = shell(shellCmdPtr);
66 return (rc);
67}
68/** @brief Execute a shell command
69 @ingroup exec
70 @param shellCmdPtr - pointer to the shell command string
71 @return the return code from the executed shell command
72 @details Executes the command string using the user's shell.
73 If the SHELL environment variable is not set, use /bin/sh. */
74int shell(char *shellCmdPtr) {
75 int Eargc;
76 char *Eargv[MAXARGS];
77 char *shellPtr;
78 int rc;
79
80 Eargc = 0;
81 shellPtr = getenv("SHELL");
82 if (shellPtr == nullptr || *shellPtr == '\0')
83 shellPtr = DEFAULTSHELL;
84 Eargv[Eargc++] = strdup(shellPtr);
85 Eargv[Eargc++] = "-c";
86 Eargv[Eargc++] = shellCmdPtr;
87 Eargv[Eargc++] = nullptr;
88 rc = fork_exec(Eargv);
89 free(Eargv[0]);
90 return (rc);
91}
92/** @brief Fork and exec a command
93 @ingroup exec
94 @param argv - array of arguments for the command to execute
95 @return the return code from the executed command, or -1 on error
96 @details Captures and restores terminal settings around the fork and exec.
97 Sets signal handlers to default in the child process.
98 Waits for the child process to complete in the parent process.
99 Handles errors from fork and execvp, and reports child exit status.
100 Restores curses mode and keypad settings after execution.
101 Restores window states after execution.
102 Uses a temporary string buffer tmp_str for error messages.
103 Uses Perror for error reporting.
104 Uses sig_dfl_mode and sig_prog_mode for signal handling.
105 Uses capture_curses_tioctl and restore_curses_tioctl for terminal
106 settings.
107 Uses restore_shell_tioctl for shell terminal settings.
108 Uses waitpid to wait for the child process.
109 Uses WIFEXITED, WEXITSTATUS, WIFSIGNALED, and WTERMSIG to interpret
110 child status.
111 Uses keypad to manage keypad mode in curses.
112 Uses restore_wins to restore window states.
113 Uses errno for error codes.
114 Uses pid_t for process IDs.
115 Uses standard file descriptors STDIN_FILENO, STDOUT_FILENO,
116 STDERR_FILENO.
117 Uses execvp for executing the command.
118 Uses fork for creating a new process.
119 Uses ssnprintf for formatting error messages.
120 Uses switch-case for handling fork results.
121 Uses default shell if SHELL environment variable is not set. */
122int fork_exec(char **argv) {
123 char tmp_str[MAXLEN];
124 pid_t pid;
125 int status;
126 int rc;
127
128 if (argv[0] == 0) {
129 Perror("fork_exec: missing argument for execvp");
130 return (-1);
131 }
138 tmp_str[0] = '\0';
139 pid = fork();
140 if (pid < 0) {
143 ssnprintf(tmp_str, sizeof(tmp_str), "fork failed: %s, errno: %d",
144 argv[0], errno);
145 Perror(tmp_str);
146 return (-1);
147 } else if (pid == 0) {
150 execvp(argv[0], argv);
151 fprintf(stderr, "execvp failed: %s, errno: %d\n", argv[0], errno);
152 exit(EXIT_FAILURE);
153 }
154 waitpid(pid, &status, 0);
155 if (WIFEXITED(status)) {
156 rc = WEXITSTATUS(status);
157 } else if (WIFSIGNALED(status)) {
158 ssnprintf(tmp_str, sizeof(tmp_str), "Child process terminated by signal: %d",
159 WTERMSIG(status));
160 Perror(tmp_str);
161 rc = -1;
162 } else {
163 ssnprintf(tmp_str, sizeof(tmp_str), "Child process terminated abnormally");
164 Perror(tmp_str);
165 rc = -1;
166 }
169 return (rc);
170}
171/** @brief Fork, detach, and exec a command
172 @ingroup exec
173 @param eargv - array of arguments for the command to execute
174 @return 0 on success, or exits on failure
175 @details Forks a new process, detaches it from the terminal, and executes
176 the specified command using execvp.
177 Closes standard input, output, and error file descriptors in the child
178 process.
179 Redirects standard input, output, and error to /dev/null in the child
180 process.
181 Closes all other file descriptors in the child process.
182 Sets the session ID for the child process to detach it from the terminal.
183 Restores curses mode and signal handling in the parent process after
184 forking.
185 Restores window states in the parent process after forking. */
186int fork_detach_execvp(char **eargv) {
187 pid_t pid = fork();
191
192 if (pid < 0) {
193 fprintf(stderr, "First fork failed: %s\n", strerror(errno));
194 exit(EXIT_FAILURE);
195 }
196 if (pid == 0) {
197 if (setsid() < 0) {
198 fprintf(stderr, "Set session ID failed: %s\n", strerror(errno));
199 exit(EXIT_FAILURE);
200 }
201 close(STDIN_FILENO);
202 close(STDOUT_FILENO);
203 close(STDERR_FILENO);
204 int dev_null = open("/dev/null", O_RDWR);
205 if (dev_null != -1) {
206 dup2(dev_null, STDIN_FILENO);
207 dup2(dev_null, STDOUT_FILENO);
208 dup2(dev_null, STDERR_FILENO);
209 if (dev_null > 2) {
210 close(dev_null);
211 }
212 }
213 long max_fd = sysconf(_SC_OPEN_MAX);
214 for (long fd = 3; fd < max_fd; fd++)
215 close(fd);
216 execvp(eargv[0], eargv);
217 perror("execvp failed");
218 exit(EXIT_FAILURE);
219 }
223 return 0;
224}
#define DEFAULTSHELL
Definition cm.h:326
char stdio_names_str[4096]
Definition futil.c:127
#define MAXARGS
Definition cm.h:50
char * stdio_names(char *, char *)
Definition futil.c:1057
void ui_restore_wins()
int ui_wmove(UiSurface *s, uint w, uint y, uint x)
Definition ui_ncurses.c:732
int ui_werase(UiSurface *s, uint w)
Definition ui_ncurses.c:560
UiSurface * stdsfc
Definition ui_ncurses.c:36
int ui_curs_set(int visibility)
Definition ui_ncurses.c:737
int ui_resume()
Definition ui_ncurses.c:403
void ui_endwin()
Definition ui_ncurses.c:341
int ui_keypad(UiSurface *s, uint w, bool enable)
Definition ui_ncurses.c:635
#define MAXLEN
Definition curskeys.c:15
int Perror(char *emsg_str)
Display a simple error message window or print to stderr.
Definition dwin.c:841
int fork_exec(char **)
Fork and exec a command.
Definition exec.c:122
int shell(char *)
Execute a shell command.
Definition exec.c:74
int full_screen_fork_exec(char **)
Execute a command in full screen mode.
Definition exec.c:45
int full_screen_shell(char *)
Execute a shell command in full screen mode.
Definition exec.c:58
int fork_detach_execvp(char **eargv)
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:413
bool restore_curses_tioctl()
restore_curses_tioctl() - restore curses terminal settings
Definition scriou.c:84
bool capture_curses_tioctl()
capture_curses_tioctl() - capture curses terminal settings
Definition scriou.c:70
bool restore_shell_tioctl()
restore_shell_tioctl() - restore shell terminal settings
Definition scriou.c:57
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