mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 02:49:02 -04:00
Sync the OS interface with the daemon
This commit is contained in:
parent
f627f3be80
commit
d6b51d61e7
4 changed files with 147 additions and 58 deletions
|
|
@ -129,7 +129,8 @@ mktest_SOURCES = \
|
|||
cmdline/mingwapp.c \
|
||||
os/unix.c \
|
||||
cmdline/unixapp.c \
|
||||
cmdline/support.c
|
||||
cmdline/support.c \
|
||||
tommyds/tommy.c
|
||||
|
||||
mkstream_SOURCES = \
|
||||
cmdline/mkstream.c \
|
||||
|
|
@ -140,7 +141,8 @@ mkstream_SOURCES = \
|
|||
cmdline/support.c \
|
||||
cmdline/util.c \
|
||||
cmdline/stream.c \
|
||||
raid/memory.c
|
||||
raid/memory.c \
|
||||
tommyds/tommy.c
|
||||
|
||||
# Checker binary share the snapraid variables, with the exception of the checker.c source
|
||||
checker_SOURCES = $(snapraid_SOURCES:cmdline/snapraid.c=cmdline/checker.c)
|
||||
|
|
|
|||
176
os/mingw.c
176
os/mingw.c
|
|
@ -2732,28 +2732,43 @@ pid_t os_wait(pid_t pid, int* status)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate a running child process gracefully on Windows.
|
||||
*
|
||||
* Child processes spawned via os_spawn() with CREATE_NO_WINDOW have an invisible
|
||||
* console host allocated by the OS. Attaching to the child's console allows
|
||||
* sending a CTRL_BREAK_EVENT signal so SnapRAID CLI catches it and flushes state/checkpoints.
|
||||
*/
|
||||
int os_term(pid_t pid)
|
||||
{
|
||||
HANDLE h = (void*)pid;
|
||||
DWORD id = GetProcessId(h);
|
||||
|
||||
if (id == 0)
|
||||
if (id == 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* detach from current console (if any) */
|
||||
FreeConsole();
|
||||
|
||||
/* attach to the child's console */
|
||||
if (!AttachConsole(id))
|
||||
return -1;
|
||||
if (!AttachConsole(id)) {
|
||||
/* fallback: terminate process forcibly if console attachment fails */
|
||||
if (!TerminateProcess(h, 1)) {
|
||||
windows_errno(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Disable Ctrl-C for the PARENT so we don't kill ourselves */
|
||||
/* disable Ctrl-C for the PARENT so we don't kill ourselves */
|
||||
SetConsoleCtrlHandler(0, TRUE);
|
||||
|
||||
/* This will now reach the child's SetConsoleCtrlHandler */
|
||||
/* this will now reach the child's SetConsoleCtrlHandler */
|
||||
GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, id);
|
||||
|
||||
/* Clean up */
|
||||
/* clean up */
|
||||
FreeConsole();
|
||||
|
||||
return 0;
|
||||
|
|
@ -2951,76 +2966,131 @@ int os_command(const char* command, const char* run_as_user, const char* stdin_t
|
|||
}
|
||||
}
|
||||
|
||||
struct env_item {
|
||||
tommy_node node;
|
||||
WCHAR str[1];
|
||||
};
|
||||
|
||||
static size_t env_key_len(const WCHAR* s)
|
||||
{
|
||||
size_t i = 0;
|
||||
if (s[0] == '=')
|
||||
i = 1;
|
||||
while (s[i] != 0 && s[i] != '=')
|
||||
++i;
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Case-insensitive comparison of environment variable names (keys only).
|
||||
*
|
||||
* Compares two environment strings up to the '=' character separating the key from the value.
|
||||
* Returns negative if a < b, positive if a > b, or 0 if key names match.
|
||||
*/
|
||||
static int env_item_compare(const void* void_a, const void* void_b)
|
||||
{
|
||||
const struct env_item* a = void_a;
|
||||
const struct env_item* b = void_b;
|
||||
|
||||
size_t len_a = env_key_len(a->str);
|
||||
size_t len_b = env_key_len(b->str);
|
||||
size_t min_len = len_a < len_b ? len_a : len_b;
|
||||
|
||||
int cmp = _wcsnicmp(a->str, b->str, min_len);
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
|
||||
if (len_a < len_b)
|
||||
return -1;
|
||||
if (len_a > len_b)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine base environment strings with extra envp variables into a single block.
|
||||
*
|
||||
* Win32 CreateProcessW requires environment blocks to be sorted alphabetically
|
||||
* (case-insensitive) by variable name. Unsorted environment blocks cause Win32 CRT
|
||||
* variable lookups (e.g. GetEnvironmentVariableW) in child processes to fail.
|
||||
*/
|
||||
static WCHAR* env_combine(const WCHAR* base_env, char** envp)
|
||||
{
|
||||
size_t base_len = 0;
|
||||
tommy_list list;
|
||||
tommy_list_init(&list);
|
||||
|
||||
size_t total_len = 0;
|
||||
WCHAR* new_env = NULL;
|
||||
|
||||
/* add base_env strings */
|
||||
if (base_env) {
|
||||
const WCHAR* p = base_env;
|
||||
while (*p != 0) {
|
||||
p += wcslen(p) + 1;
|
||||
size_t len = wcslen(p);
|
||||
struct env_item* item = malloc(sizeof(struct env_item) + len * sizeof(WCHAR));
|
||||
if (!item)
|
||||
goto bail;
|
||||
memcpy(item->str, p, (len + 1) * sizeof(WCHAR));
|
||||
tommy_list_insert_tail(&list, &item->node, item);
|
||||
total_len += len + 1;
|
||||
p += len + 1;
|
||||
}
|
||||
base_len = p - base_env + 1;
|
||||
} else {
|
||||
base_len = 1;
|
||||
}
|
||||
|
||||
size_t envv_w_total_len = 0;
|
||||
int envv_count = 0;
|
||||
/* add envp strings */
|
||||
if (envp) {
|
||||
while (envp[envv_count] != NULL) {
|
||||
envv_count++;
|
||||
}
|
||||
}
|
||||
|
||||
WCHAR** envv_w = NULL;
|
||||
WCHAR* new_env = NULL;
|
||||
|
||||
if (envv_count > 0) {
|
||||
envv_w = calloc(envv_count, sizeof(WCHAR*));
|
||||
if (!envv_w) {
|
||||
goto bail;
|
||||
}
|
||||
for (int i = 0; i < envv_count; ++i) {
|
||||
for (int i = 0; envp[i] != NULL; ++i) {
|
||||
wchar_t conv[CONV_MAX];
|
||||
u8tou16(conv, envp[i]);
|
||||
size_t len = wcslen(conv);
|
||||
envv_w[i] = malloc((len + 1) * sizeof(WCHAR));
|
||||
if (!envv_w[i]) {
|
||||
errno = ENOMEM;
|
||||
struct env_item* item = malloc(sizeof(struct env_item) + len * sizeof(WCHAR));
|
||||
if (!item)
|
||||
goto bail;
|
||||
}
|
||||
memcpy(envv_w[i], conv, (len + 1) * sizeof(WCHAR));
|
||||
envv_w_total_len += len + 1;
|
||||
memcpy(item->str, conv, (len + 1) * sizeof(WCHAR));
|
||||
tommy_list_insert_tail(&list, &item->node, item);
|
||||
total_len += len + 1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t total_len = base_len + envv_w_total_len + 1;
|
||||
new_env = malloc(total_len * sizeof(WCHAR));
|
||||
if (!new_env) {
|
||||
errno = ENOMEM;
|
||||
goto bail;
|
||||
if (total_len == 0) {
|
||||
WCHAR* empty_env = malloc(2 * sizeof(WCHAR));
|
||||
if (empty_env) {
|
||||
empty_env[0] = 0;
|
||||
empty_env[1] = 0;
|
||||
}
|
||||
return empty_env;
|
||||
}
|
||||
|
||||
WCHAR* dst = new_env;
|
||||
if (base_len > 1 && base_env) {
|
||||
memcpy(dst, base_env, (base_len - 1) * sizeof(WCHAR));
|
||||
dst += base_len - 1;
|
||||
}
|
||||
for (int i = 0; i < envv_count; ++i) {
|
||||
if (envv_w && envv_w[i]) {
|
||||
size_t len = wcslen(envv_w[i]);
|
||||
memcpy(dst, envv_w[i], (len + 1) * sizeof(WCHAR));
|
||||
tommy_list_sort(&list, env_item_compare);
|
||||
|
||||
new_env = malloc((total_len + 1) * sizeof(WCHAR));
|
||||
if (new_env) {
|
||||
WCHAR* dst = new_env;
|
||||
for (tommy_node* node = tommy_list_head(&list); node; node = node->next) {
|
||||
struct env_item* item = node->data;
|
||||
if (node->next) {
|
||||
struct env_item* next_item = node->next->data;
|
||||
|
||||
/* skip earlier duplicates so only the latest value is kept */
|
||||
if (env_item_compare(item, next_item) == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t len = wcslen(item->str);
|
||||
memcpy(dst, item->str, (len + 1) * sizeof(WCHAR));
|
||||
dst += len + 1;
|
||||
}
|
||||
*dst = 0; /* double-null terminator */
|
||||
}
|
||||
*dst = 0;
|
||||
|
||||
bail:
|
||||
if (envv_w) {
|
||||
for (int i = 0; i < envv_count; ++i) {
|
||||
free(envv_w[i]);
|
||||
}
|
||||
free(envv_w);
|
||||
/* free all list items */
|
||||
while (tommy_list_head(&list)) {
|
||||
tommy_node* head = tommy_list_head(&list);
|
||||
struct env_item* item = head->data;
|
||||
tommy_list_remove_existing(&list, head);
|
||||
free(item);
|
||||
}
|
||||
|
||||
return new_env;
|
||||
|
|
|
|||
7
os/os.h
7
os/os.h
|
|
@ -78,6 +78,8 @@ typedef struct OS_FILE {
|
|||
* with its stdin and stderr redirected to /dev/null. The underlying pipe is
|
||||
* created with O_CLOEXEC set to prevent leakage across concurrent forks.
|
||||
*
|
||||
* The caller is expected to call os_privileges_acquire() before this operation if permission is needed.
|
||||
*
|
||||
* \param argv A NULL-terminated array of strings representing the argument vector.
|
||||
* argv[0] must contain the absolute path to the verified executable.
|
||||
* \return A pointer to an initialized OS_FILE structure on success, or NULL on failure.
|
||||
|
|
@ -114,6 +116,7 @@ int os_pclose(OS_FILE* stream);
|
|||
|
||||
/**
|
||||
* Spawn a new process with the specified argument vector, optionally capturing stdout and/or stderr.
|
||||
* The caller is expected to call os_privileges_acquire() before this operation if permission is needed.
|
||||
* \param argv Array of command line arguments.
|
||||
* \param stdout_read_fd Pointer to store file descriptor for stdout, or NULL to redirect to /dev/null.
|
||||
* \param stderr_read_fd Pointer to store file descriptor for stderr, or NULL to redirect to /dev/null.
|
||||
|
|
@ -147,6 +150,8 @@ int os_term(pid_t pid);
|
|||
* The child is placed in its own process group (setpgid) to isolate it
|
||||
* from signals sent to the daemon's process group.
|
||||
*
|
||||
* The caller is expected to call os_privileges_acquire() before this operation if permission is needed.
|
||||
*
|
||||
* \param argv NULL-terminated argument vector. argv[0] must be the absolute path
|
||||
* to the executable.
|
||||
* \return The child exit status on success, or -1 on failure.
|
||||
|
|
@ -155,6 +160,7 @@ int os_spawn_and_wait(const char** argv);
|
|||
|
||||
/**
|
||||
* Execute a system command with optional user context and input.
|
||||
* The caller is expected to call os_privileges_acquire() before this operation if permission is needed.
|
||||
* \param command Command to execute.
|
||||
* \param run_as_user User to run command as (NULL for current user).
|
||||
* \param stdin_text Text to provide as stdin (NULL for no input).
|
||||
|
|
@ -164,6 +170,7 @@ int os_command(const char* command, const char* run_as_user, const char* stdin_t
|
|||
|
||||
/**
|
||||
* Execute a script file with specified user context.
|
||||
* The caller is expected to call os_privileges_acquire() before this operation if permission is needed.
|
||||
* \param argv Array of command line arguments.
|
||||
* \param envp Environment variables (NULL-terminated list of strings).
|
||||
* \param run_as_user User to run script as (NULL for current user).
|
||||
|
|
|
|||
16
os/unix.c
16
os/unix.c
|
|
@ -1029,9 +1029,19 @@ int os_command(const char* command, const char* run_as_user, const char* stdin_t
|
|||
|
||||
if (pipe_fds[1] != -1) {
|
||||
/* write text to child's stdin */
|
||||
ssize_t len = strlen(stdin_text);
|
||||
if (write(pipe_fds[1], stdin_text, len) != len) {
|
||||
os_syslog(OS_LVL_INFO, "failed to write full stdin to command %s", command);
|
||||
size_t len = strlen(stdin_text);
|
||||
size_t written = 0;
|
||||
while (written < len) {
|
||||
ssize_t ret_write = write(pipe_fds[1], stdin_text + written, len - written);
|
||||
if (ret_write < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
if (errno == EPIPE)
|
||||
break; /* child closed stdin early */
|
||||
os_syslog(OS_LVL_INFO, "failed to write stdin to command %s, errno=%s(%d)", command, strerror(errno), errno);
|
||||
break;
|
||||
}
|
||||
written += (size_t)ret_write;
|
||||
}
|
||||
/* closing the pipe sends EOF to the child (e.g., tells curl data is done) */
|
||||
close(pipe_fds[1]);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue