1 /***********************************************************************************************************************************
2 Execute Process
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5 
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/types.h>
10 #include <sys/wait.h>
11 #include <unistd.h>
12 
13 #include "common/debug.h"
14 #include "common/log.h"
15 #include "common/exec.h"
16 #include "common/fork.h"
17 #include "common/io/fdRead.h"
18 #include "common/io/fdWrite.h"
19 #include "common/io/io.h"
20 #include "common/io/read.h"
21 #include "common/io/write.h"
22 #include "common/wait.h"
23 
24 /***********************************************************************************************************************************
25 Object type
26 ***********************************************************************************************************************************/
27 struct Exec
28 {
29     ExecPub pub;                                                    // Publicly accessible variables
30     String *command;                                                // Command to execute
31     StringList *param;                                              // List of parameters to pass to command
32     const String *name;                                             // Name to display in log/error messages
33     TimeMSec timeout;                                               // Timeout for any i/o operation (read, write, etc.)
34 
35     pid_t processId;                                                // Process id of the child process
36 
37     int fdRead;                                                     // Read file descriptor
38     int fdWrite;                                                    // Write file descriptor
39     int fdError;                                                    // Error file descriptor
40 
41     IoRead *ioReadFd;                                               // File descriptor read interface
42     IoWrite *ioWriteFd;                                             // File descriptor write interface
43 };
44 
45 /***********************************************************************************************************************************
46 Macro to close file descriptors after dup2() in the child process
47 
48 If the parent process is daemomized and has closed stdout, stdin, and stderr or some combination of them, then the newly created
49 descriptors might overlap stdout, stdin, or stderr.  In that case we don't want to accidentally close the descriptor that we have
50 just copied.
51 
52 Note that this is pretty specific to the way that file descriptors are handled in this module and may not be generally applicable in
53 other code.
54 ***********************************************************************************************************************************/
55 #define PIPE_DUP2(pipe, pipeIdx, fd)                                                                                               \
56     do                                                                                                                             \
57     {                                                                                                                              \
58         dup2(pipe[pipeIdx], fd);                                                                                                   \
59                                                                                                                                    \
60         if (pipe[0] != fd)                                                                                                         \
61             close(pipe[0]);                                                                                                        \
62                                                                                                                                    \
63         if (pipe[1] != fd)                                                                                                         \
64             close(pipe[1]);                                                                                                        \
65     }                                                                                                                              \
66     while (0);
67 
68 /***********************************************************************************************************************************
69 Free exec file descriptors and ensure process is shut down
70 ***********************************************************************************************************************************/
71 static void
execFreeResource(THIS_VOID)72 execFreeResource(THIS_VOID)
73 {
74     THIS(Exec);
75 
76     FUNCTION_LOG_BEGIN(logLevelTrace);
77         FUNCTION_LOG_PARAM(EXEC, this);
78     FUNCTION_LOG_END();
79 
80     ASSERT(this != NULL);
81 
82     // Close file descriptors
83     close(this->fdRead);
84     close(this->fdWrite);
85     close(this->fdError);
86 
87     // Wait for the child to exit. We don't really care how it exits as long as it does.
88     if (this->processId != 0)
89     {
90         MEM_CONTEXT_TEMP_BEGIN()
91         {
92             int processResult = 0;
93             Wait *wait = waitNew(this->timeout);
94 
95             do
96             {
97                 THROW_ON_SYS_ERROR(
98                     (processResult = waitpid(this->processId, NULL, WNOHANG)) == -1, ExecuteError,
99                     "unable to wait on child process");
100             }
101             while (processResult == 0 && waitMore(wait));
102 
103             // If the process did not exit then error -- else we may end up with a collection of zombie processes
104             if (processResult == 0)
105                 THROW_FMT(ExecuteError, "%s did not exit when expected", strZ(this->name));
106         }
107         MEM_CONTEXT_TEMP_END();
108     }
109 
110     FUNCTION_LOG_RETURN_VOID();
111 }
112 
113 /**********************************************************************************************************************************/
114 Exec *
execNew(const String * command,const StringList * param,const String * name,TimeMSec timeout)115 execNew(const String *command, const StringList *param, const String *name, TimeMSec timeout)
116 {
117     FUNCTION_LOG_BEGIN(logLevelDebug)
118         FUNCTION_LOG_PARAM(STRING, command);
119         FUNCTION_LOG_PARAM(STRING_LIST, param);
120         FUNCTION_LOG_PARAM(STRING, name);
121         FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
122     FUNCTION_LOG_END();
123 
124     ASSERT(command != NULL);
125     ASSERT(name != NULL);
126     ASSERT(timeout > 0);
127 
128     Exec *this = NULL;
129 
130     MEM_CONTEXT_NEW_BEGIN("Exec")
131     {
132         this = memNew(sizeof(Exec));
133 
134         *this = (Exec)
135         {
136             .pub =
137             {
138                 .memContext = MEM_CONTEXT_NEW(),
139             },
140             .command = strDup(command),
141             .name = strDup(name),
142             .timeout = timeout,
143 
144             // Parameter list is optional but if not specified we need to build one with the command
145             .param = param == NULL ? strLstNew() : strLstDup(param),
146         };
147 
148         // The first parameter must be the command
149         strLstInsert(this->param, 0, this->command);
150     }
151     MEM_CONTEXT_NEW_END();
152 
153     FUNCTION_LOG_RETURN(EXEC, this);
154 }
155 
156 /***********************************************************************************************************************************
157 Check if the process is still running
158 
159 This should be called when anything unexpected happens while reading or writing, including errors and eof.  If this function returns
160 then the original error should be rethrown.
161 ***********************************************************************************************************************************/
162 static void
execCheck(Exec * this)163 execCheck(Exec *this)
164 {
165     FUNCTION_LOG_BEGIN(logLevelTrace);
166         FUNCTION_LOG_PARAM(EXEC, this);
167     FUNCTION_LOG_END();
168 
169     ASSERT(this != NULL);
170 
171     int processStatus;
172     int processResult;
173 
174     THROW_ON_SYS_ERROR(
175         (processResult = waitpid(this->processId, &processStatus, WNOHANG)) == -1, ExecuteError, "unable to wait on child process");
176 
177     if (processResult != 0)
178     {
179         // Clear the process id so we don't try to wait for this process on free
180         this->processId = 0;
181 
182         // If the process exited normally
183         if (WIFEXITED(processStatus))
184         {
185             // Get data from stderr to help diagnose the problem
186             String *errorStr = strTrim(
187                 strNewBuf(ioReadBuf(ioFdReadNewOpen(strNewFmt("%s error", strZ(this->name)), this->fdError, 0))));
188 
189             // Throw the error with as much information as is available
190             THROWP_FMT(
191                 errorTypeFromCode(WEXITSTATUS(processStatus)), "%s terminated unexpectedly [%d]%s%s", strZ(this->name),
192                 WEXITSTATUS(processStatus), strSize(errorStr) > 0 ? ": " : "", strSize(errorStr) > 0 ? strZ(errorStr) : "");
193         }
194 
195         // If the process did not exit normally then it must have been a signal
196         THROW_FMT(ExecuteError, "%s terminated unexpectedly on signal %d", strZ(this->name), WTERMSIG(processStatus));
197     }
198 
199     FUNCTION_LOG_RETURN_VOID();
200 }
201 
202 /***********************************************************************************************************************************
203 Read from the process
204 ***********************************************************************************************************************************/
205 static size_t
execRead(THIS_VOID,Buffer * buffer,bool block)206 execRead(THIS_VOID, Buffer *buffer, bool block)
207 {
208     THIS(Exec);
209 
210     FUNCTION_LOG_BEGIN(logLevelTrace);
211         FUNCTION_LOG_PARAM(EXEC, this);
212         FUNCTION_LOG_PARAM(BUFFER, buffer);
213         FUNCTION_LOG_PARAM(BOOL, block);
214     FUNCTION_LOG_END();
215 
216     ASSERT(this != NULL);
217     ASSERT(buffer != NULL);
218 
219     size_t result = 0;
220 
221     TRY_BEGIN()
222     {
223         result = ioReadInterface(this->ioReadFd)->read(ioReadDriver(this->ioReadFd), buffer, block);
224     }
225     CATCH_ANY()
226     {
227         execCheck(this);
228         RETHROW();
229     }
230     TRY_END();
231 
232     FUNCTION_LOG_RETURN(SIZE, result);
233 }
234 
235 /***********************************************************************************************************************************
236 Write to the process
237 ***********************************************************************************************************************************/
238 static void
execWrite(THIS_VOID,const Buffer * buffer)239 execWrite(THIS_VOID, const Buffer *buffer)
240 {
241     THIS(Exec);
242 
243     FUNCTION_LOG_BEGIN(logLevelTrace);
244         FUNCTION_LOG_PARAM(EXEC, this);
245         FUNCTION_LOG_PARAM(BUFFER, buffer);
246     FUNCTION_LOG_END();
247 
248     ASSERT(this != NULL);
249     ASSERT(buffer != NULL);
250 
251     TRY_BEGIN()
252     {
253         ioWrite(this->ioWriteFd, buffer);
254         ioWriteFlush(this->ioWriteFd);
255     }
256     CATCH_ANY()
257     {
258         execCheck(this);
259         RETHROW();
260     }
261     TRY_END();
262 
263     FUNCTION_LOG_RETURN_VOID();
264 }
265 
266 /***********************************************************************************************************************************
267 Is the process eof?
268 ***********************************************************************************************************************************/
269 static bool
execEof(THIS_VOID)270 execEof(THIS_VOID)
271 {
272     THIS(Exec);
273 
274     FUNCTION_LOG_BEGIN(logLevelTrace);
275         FUNCTION_LOG_PARAM(EXEC, this);
276     FUNCTION_LOG_END();
277 
278     ASSERT(this != NULL);
279 
280     // Check if the process is still running on eof
281     if (ioReadInterface(this->ioReadFd)->eof(ioReadDriver(this->ioReadFd)))
282         execCheck(this);
283 
284     FUNCTION_LOG_RETURN(BOOL, false);
285 }
286 
287 /***********************************************************************************************************************************
288 Get the read file descriptor
289 ***********************************************************************************************************************************/
290 static int
execFdRead(const THIS_VOID)291 execFdRead(const THIS_VOID)
292 {
293     THIS(const Exec);
294 
295     FUNCTION_TEST_BEGIN();
296         FUNCTION_TEST_PARAM(EXEC, this);
297     FUNCTION_TEST_END();
298 
299     ASSERT(this != NULL);
300 
301     FUNCTION_TEST_RETURN(this->fdRead);
302 }
303 
304 /**********************************************************************************************************************************/
305 void
execOpen(Exec * this)306 execOpen(Exec *this)
307 {
308     FUNCTION_LOG_BEGIN(logLevelDebug)
309         FUNCTION_LOG_PARAM(EXEC, this);
310     FUNCTION_LOG_END();
311 
312     ASSERT(this != NULL);
313 
314     // Create pipes to communicate with the subprocess.  The names of the pipes are from the perspective of the parent process since
315     // the child process will use them only briefly before exec'ing.
316     int pipeRead[2];
317     int pipeWrite[2];
318     int pipeError[2];
319 
320     THROW_ON_SYS_ERROR(pipe(pipeRead) == -1, KernelError, "unable to create read pipe");
321     THROW_ON_SYS_ERROR(pipe(pipeWrite) == -1, KernelError, "unable to create write pipe");
322     THROW_ON_SYS_ERROR(pipe(pipeError) == -1, KernelError, "unable to create error pipe");
323 
324     // Fork the subprocess
325     this->processId = forkSafe();
326 
327     // Exec command in the child process
328     if (this->processId == 0)
329     {
330         // Disable logging and close log file
331         logClose();
332 
333         // Assign stdout to the input side of the read pipe
334         PIPE_DUP2(pipeRead, 1, STDOUT_FILENO);
335 
336         // Assign stdin to the output side of the write pipe
337         PIPE_DUP2(pipeWrite, 0, STDIN_FILENO);
338 
339         // Assign stderr to the input side of the error pipe
340         PIPE_DUP2(pipeError, 1, STDERR_FILENO);
341 
342         // Execute the binary.  This statement will not return if it is successful
343         execvp(strZ(this->command), (char ** const)strLstPtr(this->param));
344 
345         // If we got here then there was an error.  We can't use a throw as we normally would because we have already shutdown
346         // logging and we don't want to execute exit paths that might free parent resources which we still have references to.
347         fprintf(stderr, "unable to execute '%s': [%d] %s\n", strZ(this->command), errno, strerror(errno));
348         exit(errorTypeCode(&ExecuteError));
349     }
350 
351     // Close the unused file descriptors
352     close(pipeRead[1]);
353     close(pipeWrite[0]);
354     close(pipeError[1]);
355 
356     // Store the file descriptors we'll use and need to close when the process terminates
357     this->fdRead = pipeRead[0];
358     this->fdWrite = pipeWrite[1];
359     this->fdError = pipeError[0];
360 
361     // Assign file descriptors to io interfaces
362     this->ioReadFd = ioFdReadNew(strNewFmt("%s read", strZ(this->name)), this->fdRead, this->timeout);
363     this->ioWriteFd = ioFdWriteNewOpen(strNewFmt("%s write", strZ(this->name)), this->fdWrite, this->timeout);
364 
365     // Create wrapper interfaces that check process state
366     this->pub.ioReadExec = ioReadNewP(this, .block = true, .read = execRead, .eof = execEof, .fd = execFdRead);
367     ioReadOpen(execIoRead(this));
368     this->pub.ioWriteExec = ioWriteNewP(this, .write = execWrite);
369     ioWriteOpen(execIoWrite(this));
370 
371     // Set a callback so the file descriptors will get freed
372     memContextCallbackSet(execMemContext(this), execFreeResource, this);
373 
374     FUNCTION_LOG_RETURN_VOID();
375 }
376