-
Notifications
You must be signed in to change notification settings - Fork 187
/
process.c
265 lines (238 loc) · 9.12 KB
/
process.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
Copyright 2021 Northern.tech AS
This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; version 3.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
To the extent this program is licensed as part of the Enterprise
versions of CFEngine, the applicable Commercial Open Source License
(COSL) may apply to this file if you as a licensee so wish it. See
included file COSL.txt.
*/
#include <platform.h>
#include <alloc-mini.h>
#include <process.h>
#include <log.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <libgen.h>
/* Different includes for Unix and Windows */
#ifndef __MINGW32__
#include <sys/wait.h>
#else
/*
* According to the documentation, the access rights given to the caller
* process (what would be the parent process in Unix) suffered a modification
* in Windows 2008 and upwards. The ALL_ACCESS flag changed value, therefore
* to keep backward compatibility we need to define the supported OS to the
* lowest OS level we want to support.
* If this is not defined, then we will have problems in Windows 2003.
* Yes, this is the right value there is no WIN2003 flag.
*/
#define _WIN32_WINNT _WIN32_WINNT_WINXP
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#endif // __MINGW32__ This covers both 32 and 64 bits MinGW
#ifndef __MINGW32__
/* Unix implementation */
int private_run_process_replace(const char *command, char **args, char **envp)
{
/* Execute the command */
execve(command, args, envp);
/* If we reach here, then we have already failed */
log_entry(LogCritical, "Temporary copy failed to run, aborting");
return -1;
}
/*
* Due to differences between Unixes and Windows, this code can only
* be compiled in Unix.
* Basically this code waits for the child process to die and then
* returns the status. In Windows we cannot do that because Windows does
* not have that kind of information. And the only kind of information
* that is available is by using their calls, therefore we implement this
* differently for Windows.
* This site contains more information:
* http://www.interopcommunity.com/dictionary/waitpid-entry.php
*/
int private_run_process_wait(const char *command, char **args, char **envp)
{
char *filename = basename(xstrdup(command));
const time_t now_seconds = time(NULL);
struct tm now;
gmtime_r(&now_seconds, &now);
size_t filenamelog_size = (strlen(filename) +
strlen("-YYYYMMDD-HHMMSS") +
strlen(".log") + 1);
char *filenamelog = xmalloc(filenamelog_size);
snprintf(filenamelog, filenamelog_size,
"%s-%04d%02d%02d-%02d%02d%02d.log", filename,
now.tm_year + 1900, now.tm_mon, now.tm_mday,
now.tm_hour, now.tm_min, now.tm_sec);
int exit_status = 0;
pid_t child = fork();
if (child < 0)
{
log_entry(LogCritical, "Could not fork child process: %s", command);
return RUN_PROCESS_FAILURE_VALUE;
}
else if (child == 0)
{
execve(command, args, envp);
/* If we reach here, the we failed */
log_entry(LogCritical, "Could not execute helper process %s", command);
exit(-1);
}
else
{
/* Parent */
int status = -1;
free(filenamelog);
waitpid(child, &status, 0);
if (WIFEXITED(status))
{
exit_status = WEXITSTATUS(status);
}
}
return exit_status;
}
#else
/*
* Windows implementations.
* The Windows implementations were taken from Microsoft's documentation and
* modified accordingly to fit our purposes.
*/
static void args_to_command_line(char *command_line, char **args,
unsigned long command_line_size)
{
/*
* Windows does not use an array for the command line arguments, but
* a string. Therefore we need to revert the parsing we did before and
* build the string.
*/
/* TODO put arguments in quotes! */
command_line[0] = '\0';
char *arg;
while ((arg = *args) != NULL)
{
strlcat(command_line, arg, command_line_size);
/* Add a space before the next argument */
strlcat(command_line, " ", command_line_size);
args++;
}
}
int private_run_process_replace(const char *command, char **args, char **envp)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
char command_line[32768];
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
ZeroMemory( command_line, sizeof(command_line) );
args_to_command_line(command_line, args, sizeof(command_line));
log_entry(LogVerbose,
"Creating process with command line: %s", command_line);
// Start the child process.
if( !CreateProcess( command, // No module name (use command line)
command_line, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
{
log_entry(LogCritical, "Temporary copy failed to run, aborting");
return -1;
}
/*
* The fork-exec paradigm does not exist in Windows. Basically once a process
* is created, there is no parent-child relationship as in Unix, the two
* processes are independent. So, now we just need to exit the parent process
* and hope for the best. Notice that if the process failed to start we
* would have caught it in the if loop above.
*/
exit(EXIT_SUCCESS);
}
/*
* There is an interesting difference in the Windows way of doing things versus
* the Unix way. On Windows, programs usually do not use STDOUT or STDERR because
* they have other reporting mechanisms. In addition the installers we run
* are run using the silent flags, so they do not disturb the user. Therefore
* there is no need to redirect the output to a log file.
*/
int private_run_process_wait(const char *command, char **args, char **envp)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
char command_line[32768];
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
ZeroMemory( command_line, sizeof(command_line) );
args_to_command_line(command_line, args, sizeof(command_line));
log_entry(LogVerbose,
"Creating process with command line: %s", command_line);
// Start the child process.
if( !CreateProcess( command, // No module name (use command line)
command_line, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
{
log_entry(LogCritical, "Could not create child process: %s", command);
return RUN_PROCESS_FAILURE_VALUE;
}
// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
/* Get exit status */
DWORD exit_status = 0;
if ( !GetExitCodeProcess( pi.hProcess, &exit_status) )
{
log_entry(LogCritical, "Could not get exit status from process: %s", command);
}
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
return (int)exit_status;
}
#endif
int run_process_replace(const char *command, char **args, char **envp)
{
if (!command || !args || !envp)
{
return -1;
}
return private_run_process_replace(command, args, envp);
}
int run_process_wait(const char *command, char **args, char **envp)
{
if (!command || !args || !envp)
{
return RUN_PROCESS_FAILURE_VALUE;
}
return private_run_process_wait(command, args, envp);
}