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 2024 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);
}
|