diff --git a/lib/libalpm/meson.build b/lib/libalpm/meson.build index bd5db9fb..224fbf5f 100644 --- a/lib/libalpm/meson.build +++ b/lib/libalpm/meson.build @@ -24,7 +24,7 @@ libalpm_sources = files(''' pkghash.h pkghash.c rawstr.c remove.h remove.c - sandbox.c + sandbox.h sandbox.c signing.c signing.h sync.h sync.c trans.h trans.c diff --git a/lib/libalpm/sandbox.c b/lib/libalpm/sandbox.c index f10e9405..0f3d7084 100644 --- a/lib/libalpm/sandbox.c +++ b/lib/libalpm/sandbox.c @@ -24,6 +24,8 @@ #include #include "alpm.h" +#include "log.h" +#include "sandbox.h" #include "util.h" int SYMEXPORT alpm_sandbox_setup_child(const char* sandboxuser) @@ -40,3 +42,183 @@ int SYMEXPORT alpm_sandbox_setup_child(const char* sandboxuser) return 0; } +static int should_retry(int errnum) +{ + return errnum == EINTR; +} + +static int read_from_pipe(int fd, void *buf, size_t count) +{ + size_t nread = 0; + + ASSERT(count > 0, return -1); + + while(nread < count) { + ssize_t r = read(fd, (char *)buf + nread, count-nread); + if(r < 0) { + if(!should_retry(errno)) { + return -1; + } + continue; + } + if(r == 0) { + /* we hit EOF unexpectedly - bail */ + return -1; + } + nread += r; + } + + return 0; +} + +static int write_to_pipe(int fd, const void *buf, size_t count) +{ + size_t nwrite = 0; + + ASSERT(count > 0, return -1); + + while(nwrite < count) { + ssize_t r = write(fd, (char *)buf + nwrite, count-nwrite); + if(r < 0) { + if(!should_retry(errno)) { + return -1; + } + continue; + } + nwrite += r; + } + + return 0; +} + +void _alpm_sandbox_cb_log(void *ctx, alpm_loglevel_t level, const char *fmt, va_list args) +{ + _alpm_sandbox_callback_t type = ALPM_SANDBOX_CB_LOG; + _alpm_sandbox_callback_context *context = ctx; + char *string = NULL; + int string_size = 0; + + if(!context || context->callback_pipe == -1) { + return; + } + + /* compute the required size, as allowed by POSIX.1-2001 and C99 */ + /* first we need to copy the va_list as it will be consumed by the first call */ + va_list copy; + va_copy(copy, args); + string_size = vsnprintf(NULL, 0, fmt, copy); + if(string_size <= 0) { + va_end(copy); + return; + } + MALLOC(string, string_size + 1, return); + string_size = vsnprintf(string, string_size + 1, fmt, args); + if(string_size > 0) { + write_to_pipe(context->callback_pipe, &type, sizeof(type)); + write_to_pipe(context->callback_pipe, &level, sizeof(level)); + write_to_pipe(context->callback_pipe, &string_size, sizeof(string_size)); + write_to_pipe(context->callback_pipe, string, string_size); + } + va_end(copy); + FREE(string); +} + +void _alpm_sandbox_cb_dl(void *ctx, const char *filename, alpm_download_event_type_t event, void *data) +{ + _alpm_sandbox_callback_t type = ALPM_SANDBOX_CB_DOWNLOAD; + _alpm_sandbox_callback_context *context = ctx; + size_t filename_len; + + if(!context || context->callback_pipe == -1) { + return; + } + + ASSERT(filename != NULL, return); + ASSERT(event == ALPM_DOWNLOAD_INIT || event == ALPM_DOWNLOAD_PROGRESS || event == ALPM_DOWNLOAD_RETRY || event == ALPM_DOWNLOAD_COMPLETED, return); + + filename_len = strlen(filename); + + write_to_pipe(context->callback_pipe, &type, sizeof(type)); + write_to_pipe(context->callback_pipe, &event, sizeof(event)); + switch(event) { + case ALPM_DOWNLOAD_INIT: + write_to_pipe(context->callback_pipe, data, sizeof(alpm_download_event_init_t)); + break; + case ALPM_DOWNLOAD_PROGRESS: + write_to_pipe(context->callback_pipe, data, sizeof(alpm_download_event_progress_t)); + break; + case ALPM_DOWNLOAD_RETRY: + write_to_pipe(context->callback_pipe, data, sizeof(alpm_download_event_retry_t)); + break; + case ALPM_DOWNLOAD_COMPLETED: + write_to_pipe(context->callback_pipe, data, sizeof(alpm_download_event_completed_t)); + break; + } + write_to_pipe(context->callback_pipe, &filename_len, sizeof(filename_len)); + write_to_pipe(context->callback_pipe, filename, filename_len); +} + + +bool _alpm_sandbox_process_cb_log(alpm_handle_t *handle, int callback_pipe) { + alpm_loglevel_t level; + char *string = NULL; + int string_size = 0; + + ASSERT(read_from_pipe(callback_pipe, &level, sizeof(level)) != -1, return false); + ASSERT(read_from_pipe(callback_pipe, &string_size, sizeof(string_size)) != -1, return false); + + MALLOC(string, string_size + 1, return false); + + ASSERT(read_from_pipe(callback_pipe, string, string_size) != -1, FREE(string); return false); + string[string_size] = '\0'; + + _alpm_log(handle, level, "%s", string); + FREE(string); + return true; +} + +bool _alpm_sandbox_process_cb_download(alpm_handle_t *handle, int callback_pipe) { + alpm_download_event_type_t type; + char *filename = NULL; + size_t filename_size, cb_data_size; + union { + alpm_download_event_init_t init; + alpm_download_event_progress_t progress; + alpm_download_event_retry_t retry; + alpm_download_event_completed_t completed; + } cb_data; + + ASSERT(read_from_pipe(callback_pipe, &type, sizeof(type)) != -1, return false); + + switch (type) { + case ALPM_DOWNLOAD_INIT: + cb_data_size = sizeof(alpm_download_event_init_t); + ASSERT(read_from_pipe(callback_pipe, &cb_data.init, cb_data_size) != -1, return false); + break; + case ALPM_DOWNLOAD_PROGRESS: + cb_data_size = sizeof(alpm_download_event_progress_t); + ASSERT(read_from_pipe(callback_pipe, &cb_data.progress, cb_data_size) != -1, return false); + break; + case ALPM_DOWNLOAD_RETRY: + cb_data_size = sizeof(alpm_download_event_retry_t); + ASSERT(read_from_pipe(callback_pipe, &cb_data.retry, cb_data_size) != -1, return false); + break; + case ALPM_DOWNLOAD_COMPLETED: + cb_data_size = sizeof(alpm_download_event_completed_t); + ASSERT(read_from_pipe(callback_pipe, &cb_data.completed, cb_data_size) != -1, return false); + break; + default: + return false; + } + + ASSERT(read_from_pipe(callback_pipe, &filename_size, sizeof(filename_size)) != -1, return false);; + + MALLOC(filename, filename_size + 1, return false); + + ASSERT(read_from_pipe(callback_pipe, filename, filename_size) != -1, FREE(filename); return false); + filename[filename_size] = '\0'; + + handle->dlcb(handle->dlcb_ctx, filename, type, &cb_data); + FREE(filename); + return true; +} diff --git a/lib/libalpm/sandbox.h b/lib/libalpm/sandbox.h new file mode 100644 index 00000000..0b42be1e --- /dev/null +++ b/lib/libalpm/sandbox.h @@ -0,0 +1,51 @@ +/* + * sandbox.h + * + * Copyright (c) 2021-2022 Pacman Development Team + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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, see . + */ + +#ifndef ALPM_SANDBOX_H +#define ALPM_SANDBOX_H + +#include + + +/* The type of callbacks that can happen during a sandboxed operation */ +typedef enum { + ALPM_SANDBOX_CB_LOG, + ALPM_SANDBOX_CB_DOWNLOAD +} _alpm_sandbox_callback_t; + +typedef struct { + int callback_pipe; +} _alpm_sandbox_callback_context; + + +/* Sandbox callbacks */ + +__attribute__((format(printf, 3, 0))) +void _alpm_sandbox_cb_log(void *ctx, alpm_loglevel_t level, const char *fmt, va_list args); + +void _alpm_sandbox_cb_dl(void *ctx, const char *filename, alpm_download_event_type_t event, void *data); + + +/* Functions to capture sandbox callbacks and convert them to alpm callbacks */ + +bool _alpm_sandbox_process_cb_log(alpm_handle_t *handle, int callback_pipe); +bool _alpm_sandbox_process_cb_download(alpm_handle_t *handle, int callback_pipe); + + +#endif /* ALPM_SANDBOX_H */