mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-25 18:40:19 -04:00
Add timeout_sec parameter to os_command and os_script interface
This commit is contained in:
parent
c6951393e8
commit
819b843163
3 changed files with 117 additions and 94 deletions
50
os/mingw.c
50
os/mingw.c
|
|
@ -3568,10 +3568,34 @@ int os_kill(pid_t* pid_slot)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a process with an optional timeout, terminating it if exceeded.
|
||||
*/
|
||||
static void wait_process_timeout(HANDLE process, uint64_t timeout_sec, int* timed_out)
|
||||
{
|
||||
DWORD wait_ms = INFINITE;
|
||||
|
||||
if (timeout_sec != 0) {
|
||||
if (timeout_sec > 0xFFFFFFFE / 1000)
|
||||
wait_ms = 0xFFFFFFFE;
|
||||
else
|
||||
wait_ms = (DWORD)(timeout_sec * 1000);
|
||||
}
|
||||
|
||||
*timed_out = 0;
|
||||
|
||||
DWORD wait_res = WaitForSingleObject(process, wait_ms);
|
||||
if (wait_res == WAIT_TIMEOUT) {
|
||||
*timed_out = 1;
|
||||
TerminateProcess(process, 1);
|
||||
WaitForSingleObject(process, INFINITE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a shell command synchronously and wait for termination.
|
||||
*/
|
||||
int os_command(const char* command, const char* run_as_user, const char* stdin_text, pid_t* pid_slot)
|
||||
int os_command(const char* command, const char* run_as_user, const char* stdin_text, uint64_t timeout_sec, pid_t* pid_slot)
|
||||
{
|
||||
wchar_t conv[CONV_MAX];
|
||||
HANDLE stdin_read_handle;
|
||||
|
|
@ -3794,7 +3818,8 @@ int os_command(const char* command, const char* run_as_user, const char* stdin_t
|
|||
CloseHandle(stdin_write_handle);
|
||||
|
||||
/* wait for completion and get exit code */
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
int timed_out;
|
||||
wait_process_timeout(pi.hProcess, timeout_sec, &timed_out);
|
||||
|
||||
DWORD status;
|
||||
GetExitCodeProcess(pi.hProcess, &status);
|
||||
|
|
@ -3806,6 +3831,12 @@ int os_command(const char* command, const char* run_as_user, const char* stdin_t
|
|||
|
||||
stop = os_tick_sec();
|
||||
int64_t execution_time = stop - start;
|
||||
|
||||
if (timed_out) {
|
||||
os_syslog(OS_LVL_INFO, "command %s timeout after %" PRId64 " seconds", command, execution_time);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (execution_time > 30)
|
||||
os_syslog(OS_LVL_WARNING, "command %s ran for %" PRId64 " seconds that is unexpectedly long", command, execution_time);
|
||||
|
||||
|
|
@ -3961,7 +3992,7 @@ bail:
|
|||
/**
|
||||
* Execute an external script synchronously and wait for termination.
|
||||
*/
|
||||
int os_script(char** argv, char** envp, const char* run_as_user, pid_t* pid_slot)
|
||||
int os_script(char** argv, char** envp, const char* run_as_user, uint64_t timeout_sec, pid_t* pid_slot)
|
||||
{
|
||||
wchar_t conv[CONV_MAX];
|
||||
PROCESS_INFORMATION pi;
|
||||
|
|
@ -4170,7 +4201,9 @@ int os_script(char** argv, char** envp, const char* run_as_user, pid_t* pid_slot
|
|||
pid_t pid = (intptr_t)pi.hProcess;
|
||||
pid_publish(pid_slot, pid);
|
||||
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
/* wait for completion and get exit code */
|
||||
int timed_out;
|
||||
wait_process_timeout(pi.hProcess, timeout_sec, &timed_out);
|
||||
|
||||
DWORD status;
|
||||
GetExitCodeProcess(pi.hProcess, &status);
|
||||
|
|
@ -4182,6 +4215,12 @@ int os_script(char** argv, char** envp, const char* run_as_user, pid_t* pid_slot
|
|||
|
||||
stop = os_tick_sec();
|
||||
int64_t execution_time = stop - start;
|
||||
|
||||
if (timed_out) {
|
||||
os_syslog(OS_LVL_INFO, "script %s timeout after %" PRId64 " seconds", resolved_path, execution_time);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (execution_time > 30)
|
||||
os_syslog(OS_LVL_WARNING, "script %s took %" PRId64 " seconds", resolved_path, execution_time);
|
||||
|
||||
|
|
@ -4396,8 +4435,9 @@ void os_privileges_release(void)
|
|||
{
|
||||
}
|
||||
|
||||
void os_privileges_drop(void)
|
||||
int os_privileges_drop(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void os_abort(void)
|
||||
|
|
|
|||
17
os/os.h
17
os/os.h
|
|
@ -244,10 +244,11 @@ int os_spawn_and_wait(const char** argv);
|
|||
* \param command Command to execute.
|
||||
* \param run_as_user User to run command as (0 for current user).
|
||||
* \param stdin_text Text to provide as stdin (0 for no input).
|
||||
* \param timeout_sec Execution timeout in seconds (0 for no timeout).
|
||||
* \param pid_slot Optional pointer to publish the process reference while active, or 0.
|
||||
* \return Exit status of command, or -1 on failure.
|
||||
*/
|
||||
int os_command(const char* command, const char* run_as_user, const char* stdin_text, pid_t* pid_slot);
|
||||
int os_command(const char* command, const char* run_as_user, const char* stdin_text, uint64_t timeout_sec, pid_t* pid_slot);
|
||||
|
||||
/**
|
||||
* Execute a script file with specified user context.
|
||||
|
|
@ -258,10 +259,11 @@ int os_command(const char* command, const char* run_as_user, const char* stdin_t
|
|||
* \param argv Array of command line arguments.
|
||||
* \param envp Environment variables (0-terminated list of strings).
|
||||
* \param run_as_user User to run script as (0 for current user).
|
||||
* \param timeout_sec Execution timeout in seconds (0 for no timeout).
|
||||
* \param pid_slot Optional pointer to publish the process reference while active, or 0.
|
||||
* \return Exit status of script, or -1 on failure.
|
||||
*/
|
||||
int os_script(char** argv, char** envp, const char* run_as_user, pid_t* pid_slot);
|
||||
int os_script(char** argv, char** envp, const char* run_as_user, uint64_t timeout_sec, pid_t* pid_slot);
|
||||
|
||||
/**
|
||||
* Validates a string for exec.
|
||||
|
|
@ -351,11 +353,17 @@ void os_privileges_acquire(void);
|
|||
void os_privileges_release(void);
|
||||
|
||||
/**
|
||||
* Drop effective privileges permanently to an unprivileged user (e.g., "nobody").
|
||||
* Drop effective privileges permanently to an unprivileged user
|
||||
* (e.g., "nobody").
|
||||
* Called after startup/initialization is complete to transition the daemon into
|
||||
* the Bracketed Privileges execution mode.
|
||||
*
|
||||
* On failure the process credentials may have been partially changed and the
|
||||
* caller must terminate the process.
|
||||
*
|
||||
* \return 0 on success, -1 on failure.
|
||||
*/
|
||||
void os_privileges_drop(void);
|
||||
int os_privileges_drop(void);
|
||||
|
||||
/****************************************************************************/
|
||||
/* os */
|
||||
|
|
@ -404,7 +412,6 @@ uint64_t os_tick_sec(void);
|
|||
*/
|
||||
int os_usleep(uint64_t usec);
|
||||
|
||||
|
||||
/**
|
||||
* Abort the process with a stacktrace.
|
||||
*/
|
||||
|
|
|
|||
144
os/unix.c
144
os/unix.c
|
|
@ -353,11 +353,6 @@ int filephy(const char* path, uint64_t size, uint64_t* physical)
|
|||
/****************************************************************************/
|
||||
/* exec */
|
||||
|
||||
/**
|
||||
* Maximum execution time for helper scripts and commands, in seconds.
|
||||
*/
|
||||
#define OS_EXEC_TIMEOUT_SEC 300
|
||||
|
||||
/*
|
||||
* Scrubbed environment
|
||||
* Only provide the bare essentials.
|
||||
|
|
@ -1053,7 +1048,7 @@ static void pid_unpublish(pid_t* pid_slot, pid_t pid)
|
|||
* as a zombie until the caller unpublishes the process slot and performs the final reap.
|
||||
* Optionally delivers input to input_fd using non-blocking writes.
|
||||
*/
|
||||
static pid_t waitpid_timeout_group(pid_t pid, int64_t start, int input_fd, const char* input, int* timed_out, int* input_error)
|
||||
static pid_t waitpid_timeout_group(pid_t pid, int64_t start, int input_fd, const char* input, uint64_t timeout_sec, int* timed_out, int* input_error)
|
||||
{
|
||||
size_t input_len = input != 0 ? strlen(input) : 0;
|
||||
size_t input_pos = 0;
|
||||
|
|
@ -1132,7 +1127,7 @@ static pid_t waitpid_timeout_group(pid_t pid, int64_t start, int input_fd, const
|
|||
}
|
||||
}
|
||||
|
||||
if (os_tick_sec() - start >= OS_EXEC_TIMEOUT_SEC) {
|
||||
if (timeout_sec != 0 && os_tick_sec() - start >= timeout_sec) {
|
||||
*timed_out = 1;
|
||||
|
||||
if (input_fd != -1) {
|
||||
|
|
@ -1165,7 +1160,7 @@ static pid_t waitpid_timeout_group(pid_t pid, int64_t start, int input_fd, const
|
|||
/**
|
||||
* Executes a script directly via its file descriptor.
|
||||
*/
|
||||
int os_script(char** argv, char** envp, const char* run_as_user, pid_t* pid_slot)
|
||||
int os_script(char** argv, char** envp, const char* run_as_user, uint64_t timeout_sec, pid_t* pid_slot)
|
||||
{
|
||||
char resolved_path[PATH_MAX];
|
||||
pid_t pid;
|
||||
|
|
@ -1310,7 +1305,7 @@ int os_script(char** argv, char** envp, const char* run_as_user, pid_t* pid_slot
|
|||
|
||||
pid_publish(pid_slot, pid);
|
||||
|
||||
ret = waitpid_timeout_group(pid, start, -1, 0, &timed_out, 0);
|
||||
ret = waitpid_timeout_group(pid, start, -1, 0, timeout_sec, &timed_out, 0);
|
||||
|
||||
/*
|
||||
* Unpublish the PID slot before reaping the zombie process.
|
||||
|
|
@ -1364,7 +1359,7 @@ int os_script(char** argv, char** envp, const char* run_as_user, pid_t* pid_slot
|
|||
}
|
||||
}
|
||||
|
||||
int os_command(const char* command, const char* run_as_user, const char* stdin_text, pid_t* pid_slot)
|
||||
int os_command(const char* command, const char* run_as_user, const char* stdin_text, uint64_t timeout_sec, pid_t* pid_slot)
|
||||
{
|
||||
pid_t pid;
|
||||
int ret;
|
||||
|
|
@ -1501,7 +1496,7 @@ int os_command(const char* command, const char* run_as_user, const char* stdin_t
|
|||
|
||||
pid_publish(pid_slot, pid);
|
||||
|
||||
ret = waitpid_timeout_group(pid, start, pipe_fds[1], stdin_text, &timed_out, &input_error);
|
||||
ret = waitpid_timeout_group(pid, start, pipe_fds[1], stdin_text, timeout_sec, &timed_out, &input_error);
|
||||
pipe_fds[1] = -1;
|
||||
|
||||
/*
|
||||
|
|
@ -1989,7 +1984,7 @@ static int groups_dropped = 0;
|
|||
static gid_t* privileged_groups = 0;
|
||||
static int privileged_group_count = 0;
|
||||
|
||||
void os_privileges_drop(void)
|
||||
int os_privileges_drop(void)
|
||||
{
|
||||
/*
|
||||
* Detect if running as root
|
||||
|
|
@ -2000,80 +1995,61 @@ void os_privileges_drop(void)
|
|||
if (getuid() == 0 || geteuid() == 0) {
|
||||
/* find the unprivileged user "nobody" */
|
||||
struct passwd* pw = getpwnam("nobody");
|
||||
if (pw) {
|
||||
unpriv_uid = pw->pw_uid;
|
||||
unpriv_gid = pw->pw_gid;
|
||||
|
||||
/*
|
||||
* Save the supplementary groups before dropping them.
|
||||
* They must be restored by os_privileges_acquire() because
|
||||
* spawned user scripts may depend on them.
|
||||
*/
|
||||
privileged_group_count = getgroups(0, 0);
|
||||
if (privileged_group_count < 0) {
|
||||
os_syslog(OS_LVL_INFO, "failed to get supplementary group count, errno=%s(%d)", strerror(errno), errno);
|
||||
os_abort();
|
||||
}
|
||||
|
||||
if (privileged_group_count != 0) {
|
||||
privileged_groups = malloc(privileged_group_count * sizeof(gid_t));
|
||||
if (!privileged_groups) {
|
||||
os_syslog(OS_LVL_CRITICAL, "failed to allocate supplementary group list");
|
||||
os_abort();
|
||||
}
|
||||
|
||||
if (getgroups(privileged_group_count, privileged_groups) != privileged_group_count) {
|
||||
os_syslog(OS_LVL_INFO, "failed to get supplementary groups, errno=%s(%d)", strerror(errno), errno);
|
||||
os_abort();
|
||||
}
|
||||
|
||||
if (setgroups(0, 0) != 0) {
|
||||
if (errno == EPERM) {
|
||||
/*
|
||||
* If EPERM, process lacks permission to switch
|
||||
* supplementary groups. Continue with them active.
|
||||
*/
|
||||
os_syslog(OS_LVL_INFO, "permission denied to release supplementary group privileges, continuing with active privileges");
|
||||
} else {
|
||||
os_syslog(OS_LVL_INFO, "failed to release supplementary group privileges, errno=%s(%d)", strerror(errno), errno);
|
||||
os_abort();
|
||||
}
|
||||
} else {
|
||||
groups_dropped = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (setegid(unpriv_gid) != 0) {
|
||||
if (errno == EPERM) {
|
||||
/*
|
||||
* If EPERM, process lacks permission to switch privileges
|
||||
* (e.g. dropped capabilities); continue with active privileges.
|
||||
*/
|
||||
os_syslog(OS_LVL_INFO, "permission denied to release group privileges, continuing with active privileges");
|
||||
} else {
|
||||
os_syslog(OS_LVL_INFO, "failed to release group privileges, errno=%s(%d)", strerror(errno), errno);
|
||||
os_abort();
|
||||
}
|
||||
} else {
|
||||
gid_dropped = 1;
|
||||
}
|
||||
|
||||
if (seteuid(unpriv_uid) != 0) {
|
||||
if (errno == EPERM) {
|
||||
/*
|
||||
* If EPERM, process lacks permission to switch privileges
|
||||
* (e.g. dropped capabilities); continue with active privileges.
|
||||
*/
|
||||
os_syslog(OS_LVL_INFO, "permission denied to release privileges, continuing with active privileges");
|
||||
} else {
|
||||
os_syslog(OS_LVL_INFO, "failed to release privileges, errno=%s(%d)", strerror(errno), errno);
|
||||
os_abort();
|
||||
}
|
||||
} else {
|
||||
uid_dropped = 1;
|
||||
}
|
||||
if (!pw) {
|
||||
os_syslog(OS_LVL_INFO, "unprivileged user 'nobody' not found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unpriv_uid = pw->pw_uid;
|
||||
unpriv_gid = pw->pw_gid;
|
||||
|
||||
/*
|
||||
* Save the supplementary groups before dropping them.
|
||||
* They must be restored by os_privileges_acquire() because
|
||||
* spawned user scripts may depend on them.
|
||||
*/
|
||||
privileged_group_count = getgroups(0, 0);
|
||||
if (privileged_group_count < 0) {
|
||||
os_syslog(OS_LVL_INFO, "failed to get supplementary group count, errno=%s(%d)", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (privileged_group_count != 0) {
|
||||
privileged_groups = malloc(privileged_group_count * sizeof(gid_t));
|
||||
if (!privileged_groups) {
|
||||
os_syslog(OS_LVL_CRITICAL, "failed to allocate supplementary group list");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (getgroups(privileged_group_count, privileged_groups) != privileged_group_count) {
|
||||
os_syslog(OS_LVL_INFO, "failed to get supplementary groups, errno=%s(%d)", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setgroups(0, 0) != 0) {
|
||||
os_syslog(OS_LVL_INFO, "failed to release supplementary group privileges, errno=%s(%d)", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
groups_dropped = 1;
|
||||
}
|
||||
|
||||
if (setegid(unpriv_gid) != 0) {
|
||||
os_syslog(OS_LVL_INFO, "failed to release group privileges, errno=%s(%d)", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gid_dropped = 1;
|
||||
|
||||
if (seteuid(unpriv_uid) != 0) {
|
||||
os_syslog(OS_LVL_INFO, "failed to release privileges, errno=%s(%d)", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uid_dropped = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void os_privileges_acquire(void)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue