Added SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN

This commit is contained in:
Sam Lantinga 2024-09-14 09:13:37 -07:00
parent 44c6cfda05
commit 76c469910e
6 changed files with 146 additions and 70 deletions

View File

@ -175,6 +175,7 @@ typedef enum SDL_ProcessIO
* output of the process should be redirected into the standard output of
* the process. This property has no effect if
* `SDL_PROP_PROCESS_CREATE_STDERR_NUMBER` is set.
* - `SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN`: true if the process should run in the background. In this case the default input and output is `SDL_PROCESS_STDIO_NULL` and the exitcode of the process is not available, and will always be 0.
*
* On POSIX platforms, wait() and waitpid(-1, ...) should not be called, and
* SIGCHLD should not be ignored or handled because those would prevent SDL
@ -208,6 +209,7 @@ extern SDL_DECLSPEC SDL_Process *SDLCALL SDL_CreateProcessWithProperties(SDL_Pro
#define SDL_PROP_PROCESS_CREATE_STDERR_NUMBER "SDL.process.create.stderr_option"
#define SDL_PROP_PROCESS_CREATE_STDERR_POINTER "SDL.process.create.stderr_source"
#define SDL_PROP_PROCESS_CREATE_STDERR_TO_STDOUT_BOOLEAN "SDL.process.create.stderr_to_stdout"
#define SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN "SDL.process.create.background"
/**
* Get the properties associated with a process.
@ -218,6 +220,7 @@ extern SDL_DECLSPEC SDL_Process *SDLCALL SDL_CreateProcessWithProperties(SDL_Pro
* - `SDL_PROP_PROCESS_STDIN_POINTER`: an SDL_IOStream that can be used to write input to the process, if it was created with `SDL_PROP_PROCESS_CREATE_STDIN_NUMBER` set to `SDL_PROCESS_STDIO_APP`.
* - `SDL_PROP_PROCESS_STDOUT_POINTER`: a non-blocking SDL_IOStream that can be used to read output from the process, if it was created with `SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER` set to `SDL_PROCESS_STDIO_APP`.
* - `SDL_PROP_PROCESS_STDERR_POINTER`: a non-blocking SDL_IOStream that can be used to read error output from the process, if it was created with `SDL_PROP_PROCESS_CREATE_STDERR_NUMBER` set to `SDL_PROCESS_STDIO_APP`.
* - `SDL_PROP_PROCESS_BACKGROUND_BOOLEAN`: true if the process is running in the background.
*
* \param process the process to query.
* \returns a valid property ID on success or 0 on failure; call
@ -232,10 +235,11 @@ extern SDL_DECLSPEC SDL_Process *SDLCALL SDL_CreateProcessWithProperties(SDL_Pro
*/
extern SDL_DECLSPEC SDL_PropertiesID SDL_GetProcessProperties(SDL_Process *process);
#define SDL_PROP_PROCESS_PID_NUMBER "SDL.process.pid"
#define SDL_PROP_PROCESS_STDIN_POINTER "SDL.process.stdin"
#define SDL_PROP_PROCESS_STDOUT_POINTER "SDL.process.stdout"
#define SDL_PROP_PROCESS_STDERR_POINTER "SDL.process.stderr"
#define SDL_PROP_PROCESS_PID_NUMBER "SDL.process.pid"
#define SDL_PROP_PROCESS_STDIN_POINTER "SDL.process.stdin"
#define SDL_PROP_PROCESS_STDOUT_POINTER "SDL.process.stdout"
#define SDL_PROP_PROCESS_STDERR_POINTER "SDL.process.stderr"
#define SDL_PROP_PROCESS_BACKGROUND_BOOLEAN "SDL.process.background"
/**
* Read all the output from a process.

View File

@ -35,51 +35,44 @@ extern char **environ;
bool SDL_SYS_OpenURL(const char *url)
{
const pid_t pid1 = fork();
if (pid1 == 0) { // child process
#ifdef USE_POSIX_SPAWN
pid_t pid2;
const char *args[] = { "xdg-open", url, NULL };
// Clear LD_PRELOAD so Chrome opens correctly when this application is launched by Steam
SDL_unsetenv_unsafe("LD_PRELOAD");
if (posix_spawnp(&pid2, args[0], NULL, NULL, (char **)args, environ) == 0) {
// Child process doesn't wait for possibly-blocking grandchild.
_exit(EXIT_SUCCESS);
} else {
_exit(EXIT_FAILURE);
}
#else
pid_t pid2;
// Clear LD_PRELOAD so Chrome opens correctly when this application is launched by Steam
SDL_unsetenv_unsafe("LD_PRELOAD");
// Notice this is vfork and not fork!
pid2 = vfork();
if (pid2 == 0) { // Grandchild process will try to launch the url
execlp("xdg-open", "xdg-open", url, NULL);
_exit(EXIT_FAILURE);
} else if (pid2 < 0) { // There was an error forking
_exit(EXIT_FAILURE);
} else {
// Child process doesn't wait for possibly-blocking grandchild.
_exit(EXIT_SUCCESS);
}
#endif // USE_POSIX_SPAWN
} else if (pid1 < 0) {
return SDL_SetError("fork() failed: %s", strerror(errno));
} else {
int status;
if (waitpid(pid1, &status, 0) == pid1) {
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == 0) {
return true; // success!
} else {
return SDL_SetError("xdg-open reported error or failed to launch: %d", WEXITSTATUS(status));
}
} else {
return SDL_SetError("xdg-open failed for some reason");
}
} else {
return SDL_SetError("Waiting on xdg-open failed: %s", strerror(errno));
}
SDL_Environment *env = NULL;
char **process_env = NULL;
const char *process_args[] = { "xdg-open", url, NULL };
SDL_Process *process = NULL;
bool result = false;
env = SDL_CreateEnvironment(SDL_FALSE);
if (!env) {
goto done;
}
// Clear LD_PRELOAD so Chrome opens correctly when this application is launched by Steam
SDL_UnsetEnvironmentVariable(env, "LD_PRELOAD");
process_env = SDL_GetEnvironmentVariables(env);
if (!process_env) {
goto done;
}
SDL_PropertiesID props = SDL_CreateProperties();
if (!props) {
goto done;
}
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, process_args);
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, process_env);
SDL_SetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN, true);
process = SDL_CreateProcessWithProperties(props);
SDL_DestroyProperties(props);
if (!process) {
goto done;
}
result = true;
done:
SDL_free(process_env);
SDL_DestroyEnvironment(env);
SDL_DestroyProcess(process);
return result;
}

View File

@ -54,12 +54,14 @@ SDL_Process *SDL_CreateProcessWithProperties(SDL_PropertiesID props)
if (!process) {
return NULL;
}
process->background = SDL_GetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN, false);
process->props = SDL_CreateProperties();
if (!process->props) {
SDL_DestroyProcess(process);
return NULL;
}
SDL_SetBooleanProperty(process->props, SDL_PROP_PROCESS_BACKGROUND_BOOLEAN, process->background);
if (!SDL_SYS_CreateProcessWithProperties(process, props)) {
SDL_DestroyProcess(process);
@ -180,6 +182,9 @@ SDL_bool SDL_WaitProcess(SDL_Process *process, SDL_bool block, int *exitcode)
if (SDL_SYS_WaitProcess(process, block, &process->exitcode)) {
process->alive = false;
if (exitcode) {
if (process->background) {
process->exitcode = 0;
}
*exitcode = process->exitcode;
}
return true;

View File

@ -25,6 +25,7 @@ typedef struct SDL_ProcessData SDL_ProcessData;
struct SDL_Process
{
bool alive;
bool background;
int exitcode;
SDL_PropertiesID props;
SDL_ProcessData *internal;

View File

@ -137,6 +137,19 @@ bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID
goto posix_spawn_fail_attr;
}
// Background processes don't have access to the terminal
if (process->background) {
if (stdin_option == SDL_PROCESS_STDIO_INHERITED) {
stdin_option = SDL_PROCESS_STDIO_NULL;
}
if (stdout_option == SDL_PROCESS_STDIO_INHERITED) {
stdout_option = SDL_PROCESS_STDIO_NULL;
}
if (stderr_option == SDL_PROCESS_STDIO_INHERITED) {
stderr_option = SDL_PROCESS_STDIO_NULL;
}
}
switch (stdin_option) {
case SDL_PROCESS_STDIO_REDIRECT:
if (!GetStreamFD(props, SDL_PROP_PROCESS_CREATE_STDIN_POINTER, &fd)) {
@ -276,9 +289,38 @@ bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID
}
// Spawn the new process
if (posix_spawnp(&data->pid, args[0], &fa, &attr, args, env) != 0) {
SDL_SetError("posix_spawn failed: %s", strerror(errno));
goto posix_spawn_fail_all;
if (process->background) {
int status = -1;
pid_t pid = vfork();
switch (pid) {
case -1:
SDL_SetError("vfork() failed: %s", strerror(errno));
goto posix_spawn_fail_all;
case 0:
// Detach from the terminal and launch the process
setsid();
if (posix_spawnp(&data->pid, args[0], &fa, &attr, args, env) != 0) {
_exit(errno);
}
_exit(0);
default:
if (waitpid(pid, &status, 0) < 0) {
SDL_SetError("waitpid() failed: %s", strerror(errno));
goto posix_spawn_fail_all;
}
if (status != 0) {
SDL_SetError("posix_spawn() failed: %s", strerror(status));
goto posix_spawn_fail_all;
}
break;
}
} else {
if (posix_spawnp(&data->pid, args[0], &fa, &attr, args, env) != 0) {
SDL_SetError("posix_spawn() failed: %s", strerror(errno));
goto posix_spawn_fail_all;
}
}
SDL_SetNumberProperty(process->props, SDL_PROP_PROCESS_PID_NUMBER, data->pid);
@ -353,26 +395,43 @@ bool SDL_SYS_KillProcess(SDL_Process *process, SDL_bool force)
bool SDL_SYS_WaitProcess(SDL_Process *process, SDL_bool block, int *exitcode)
{
int wstatus = 0;
int ret = waitpid(process->internal->pid, &wstatus, block ? 0 : WNOHANG);
int ret;
pid_t pid = process->internal->pid;
if (ret < 0) {
return SDL_SetError("Could not waitpid(): %s", strerror(errno));
}
if (ret == 0) {
SDL_ClearError();
return false;
}
if (WIFEXITED(wstatus)) {
*exitcode = WEXITSTATUS(wstatus);
} else if (WIFSIGNALED(wstatus)) {
*exitcode = -WTERMSIG(wstatus);
if (process->background) {
// We can't wait on the status, so we'll poll to see if it's alive
if (block) {
while (kill(pid, 0) == 0) {
SDL_Delay(10);
}
} else {
if (kill(pid, 0) == 0) {
return false;
}
}
*exitcode = 0;
return true;
} else {
*exitcode = -255;
}
ret = waitpid(pid, &wstatus, block ? 0 : WNOHANG);
if (ret < 0) {
return SDL_SetError("Could not waitpid(): %s", strerror(errno));
}
return true;
if (ret == 0) {
SDL_ClearError();
return false;
}
if (WIFEXITED(wstatus)) {
*exitcode = WEXITSTATUS(wstatus);
} else if (WIFSIGNALED(wstatus)) {
*exitcode = -WTERMSIG(wstatus);
} else {
*exitcode = -255;
}
return true;
}
}
void SDL_SYS_DestroyProcess(SDL_Process *process)

View File

@ -232,6 +232,20 @@ bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID
security_attributes.bInheritHandle = TRUE;
security_attributes.lpSecurityDescriptor = NULL;
// Background processes don't have access to the terminal
// This isn't necessary on Windows, but we keep the same behavior as the POSIX implementation.
if (process->background) {
if (stdin_option == SDL_PROCESS_STDIO_INHERITED) {
stdin_option = SDL_PROCESS_STDIO_NULL;
}
if (stdout_option == SDL_PROCESS_STDIO_INHERITED) {
stdout_option = SDL_PROCESS_STDIO_NULL;
}
if (stderr_option == SDL_PROCESS_STDIO_INHERITED) {
stderr_option = SDL_PROCESS_STDIO_NULL;
}
}
switch (stdin_option) {
case SDL_PROCESS_STDIO_REDIRECT:
if (!SetupRedirect(props, SDL_PROP_PROCESS_CREATE_STDIN_POINTER, &startup_info.hStdInput)) {