#include "bus-util.h"
#include "bus-common-errors.h"
#include "def.h"
+#include "socket-util.h"
+#include "mkdir.h"
+#include "def.h"
+#include "missing.h"
+#include "machine-pool.h"
+#include "path-util.h"
#include "import-util.h"
typedef struct Transfer Transfer;
typedef struct Manager Manager;
typedef enum TransferType {
- TRANSFER_TAR,
- TRANSFER_RAW,
- TRANSFER_DKR,
+ TRANSFER_IMPORT_TAR,
+ TRANSFER_IMPORT_RAW,
+ TRANSFER_EXPORT_TAR,
+ TRANSFER_EXPORT_RAW,
+ TRANSFER_PULL_TAR,
+ TRANSFER_PULL_RAW,
+ TRANSFER_PULL_DKR,
_TRANSFER_TYPE_MAX,
_TRANSFER_TYPE_INVALID = -1,
} TransferType;
char *remote;
char *local;
bool force_local;
+ bool read_only;
char *dkr_index_url;
+ char *format;
pid_t pid;
sd_event_source *log_event_source;
unsigned n_canceled;
+ unsigned progress_percent;
+
+ int stdin_fd;
+ int stdout_fd;
};
struct Manager {
Hashmap *transfers;
Hashmap *polkit_registry;
+
+ int notify_fd;
+
+ sd_event_source *notify_event_source;
};
#define TRANSFERS_MAX 64
static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
- [TRANSFER_TAR] = "tar",
- [TRANSFER_RAW] = "raw",
- [TRANSFER_DKR] = "dkr",
+ [TRANSFER_IMPORT_TAR] = "import-tar",
+ [TRANSFER_IMPORT_RAW] = "import-raw",
+ [TRANSFER_EXPORT_TAR] = "export-tar",
+ [TRANSFER_EXPORT_RAW] = "export-raw",
+ [TRANSFER_PULL_TAR] = "pull-tar",
+ [TRANSFER_PULL_RAW] = "pull-raw",
+ [TRANSFER_PULL_DKR] = "pull-dkr",
};
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP(transfer_type, TransferType);
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
static Transfer *transfer_unref(Transfer *t) {
if (!t)
free(t->remote);
free(t->local);
free(t->dkr_index_url);
+ free(t->format);
free(t->object_path);
if (t->pid > 0) {
}
safe_close(t->log_fd);
+ safe_close(t->stdin_fd);
+ safe_close(t->stdout_fd);
free(t);
return NULL;
t->type = _TRANSFER_TYPE_INVALID;
t->log_fd = -1;
+ t->stdin_fd = -1;
+ t->verify = _IMPORT_VERIFY_INVALID;
id = m->current_transfer_id + 1;
return -errno;
if (t->pid == 0) {
const char *cmd[] = {
- "systemd-pull",
- transfer_type_to_string(t->type),
- "--verify",
+ NULL, /* systemd-import, systemd-export or systemd-pull */
+ NULL, /* tar, raw, dkr */
+ NULL, /* --verify= */
NULL, /* verify argument */
NULL, /* maybe --force */
+ NULL, /* maybe --read-only */
NULL, /* maybe --dkr-index-url */
- NULL, /* the actual URL */
+ NULL, /* if so: the actual URL */
+ NULL, /* maybe --format= */
+ NULL, /* if so: the actual format */
NULL, /* remote */
NULL, /* local */
NULL
};
- int null_fd;
- unsigned k = 3;
+ unsigned k = 0;
/* Child */
pipefd[0] = safe_close(pipefd[0]);
- if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
+ if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
log_error_errno(errno, "Failed to dup2() fd: %m");
_exit(EXIT_FAILURE);
}
- if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
- log_error_errno(errno, "Failed to dup2() fd: %m");
- _exit(EXIT_FAILURE);
+ if (t->stdout_fd >= 0) {
+ if (dup2(t->stdout_fd, STDOUT_FILENO) != STDOUT_FILENO) {
+ log_error_errno(errno, "Failed to dup2() fd: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (t->stdout_fd != STDOUT_FILENO)
+ safe_close(t->stdout_fd);
+ } else {
+ if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
+ log_error_errno(errno, "Failed to dup2() fd: %m");
+ _exit(EXIT_FAILURE);
+ }
}
if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO)
pipefd[1] = safe_close(pipefd[1]);
- null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
- if (null_fd < 0) {
- log_error_errno(errno, "Failed to open /dev/null: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
- log_error_errno(errno, "Failed to dup2() fd: %m");
- _exit(EXIT_FAILURE);
+ if (t->stdin_fd >= 0) {
+ if (dup2(t->stdin_fd, STDIN_FILENO) != STDIN_FILENO) {
+ log_error_errno(errno, "Failed to dup2() fd: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (t->stdin_fd != STDIN_FILENO)
+ safe_close(t->stdin_fd);
+ } else {
+ int null_fd;
+
+ null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
+ if (null_fd < 0) {
+ log_error_errno(errno, "Failed to open /dev/null: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
+ log_error_errno(errno, "Failed to dup2() fd: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (null_fd != STDIN_FILENO)
+ safe_close(null_fd);
}
- if (null_fd != STDIN_FILENO)
- safe_close(null_fd);
-
fd_cloexec(STDIN_FILENO, false);
fd_cloexec(STDOUT_FILENO, false);
fd_cloexec(STDERR_FILENO, false);
- putenv((char*) "SYSTEMD_LOG_TARGET=console-prefixed");
+ setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
+ setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
+
+ if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_IMPORT_RAW))
+ cmd[k++] = SYSTEMD_IMPORT_PATH;
+ else if (IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW))
+ cmd[k++] = SYSTEMD_EXPORT_PATH;
+ else
+ cmd[k++] = SYSTEMD_PULL_PATH;
+
+ if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_EXPORT_TAR, TRANSFER_PULL_TAR))
+ cmd[k++] = "tar";
+ else if (IN_SET(t->type, TRANSFER_IMPORT_RAW, TRANSFER_EXPORT_RAW, TRANSFER_PULL_RAW))
+ cmd[k++] = "raw";
+ else
+ cmd[k++] = "dkr";
+
+ if (t->verify != _IMPORT_VERIFY_INVALID) {
+ cmd[k++] = "--verify";
+ cmd[k++] = import_verify_to_string(t->verify);
+ }
- cmd[k++] = import_verify_to_string(t->verify);
if (t->force_local)
cmd[k++] = "--force";
+ if (t->read_only)
+ cmd[k++] = "--read-only";
if (t->dkr_index_url) {
cmd[k++] = "--dkr-index-url";
cmd[k++] = t->dkr_index_url;
}
- cmd[k++] = t->remote;
+ if (t->format) {
+ cmd[k++] = "--format";
+ cmd[k++] = t->format;
+ }
+
+ if (!IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW)) {
+ if (t->remote)
+ cmd[k++] = t->remote;
+ else
+ cmd[k++] = "-";
+ }
+
if (t->local)
cmd[k++] = t->local;
cmd[k] = NULL;
- execv(SYSTEMD_PULL_PATH, (char * const *) cmd);
- log_error_errno(errno, "Failed to execute import tool: %m");
+ execv(cmd[0], (char * const *) cmd);
+ log_error_errno(errno, "Failed to execute %s tool: %m", cmd[0]);
_exit(EXIT_FAILURE);
}
t->log_fd = pipefd[0];
pipefd[0] = -1;
+ t->stdin_fd = safe_close(t->stdin_fd);
+
r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
if (r < 0)
return r;
if (!m)
return NULL;
+ sd_event_source_unref(m->notify_event_source);
+ safe_close(m->notify_fd);
+
while ((t = hashmap_first(m->transfers)))
transfer_unref(t);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
+static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+
+ char buf[NOTIFY_BUFFER_MAX+1];
+ struct iovec iovec = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf)-1,
+ };
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
+ CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
+ } control = {};
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct ucred *ucred = NULL;
+ Manager *m = userdata;
+ struct cmsghdr *cmsg;
+ unsigned percent;
+ char *p, *e;
+ Transfer *t;
+ Iterator i;
+ ssize_t n;
+ int r;
+
+ n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
+ if (n < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ return -errno;
+ }
+
+ cmsg_close_all(&msghdr);
+
+ for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
+
+ ucred = (struct ucred*) CMSG_DATA(cmsg);
+ }
+ }
+
+ if (msghdr.msg_flags & MSG_TRUNC) {
+ log_warning("Got overly long notification datagram, ignoring.");
+ return 0;
+ }
+
+ if (!ucred || ucred->pid <= 0) {
+ log_warning("Got notification datagram lacking credential information, ignoring.");
+ return 0;
+ }
+
+ HASHMAP_FOREACH(t, m->transfers, i)
+ if (ucred->pid == t->pid)
+ break;
+
+ if (!t) {
+ log_warning("Got notification datagram from unexpected peer, ignoring.");
+ return 0;
+ }
+
+ buf[n] = 0;
+
+ p = startswith(buf, "X_IMPORT_PROGRESS=");
+ if (!p) {
+ p = strstr(buf, "\nX_IMPORT_PROGRESS=");
+ if (!p)
+ return 0;
+
+ p += 19;
+ }
+
+ e = strchrnul(p, '\n');
+ *e = 0;
+
+ r = safe_atou(p, &percent);
+ if (r < 0 || percent > 100) {
+ log_warning("Got invalid percent value, ignoring.");
+ return 0;
+ }
+
+ t->progress_percent = percent;
+
+ log_debug("Got percentage from client: %u%%", percent);
+ return 0;
+}
+
static int manager_new(Manager **ret) {
_cleanup_(manager_unrefp) Manager *m = NULL;
+ static const union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/import/notify",
+ };
+ static const int one = 1;
int r;
assert(ret);
if (r < 0)
return r;
+ m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (m->notify_fd < 0)
+ return -errno;
+
+ (void) mkdir_parents_label(sa.un.sun_path, 0755);
+ (void) unlink(sa.un.sun_path);
+
+ if (bind(m->notify_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0)
+ return -errno;
+
+ if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
+ return -errno;
+
+ r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
+ if (r < 0)
+ return r;
+
*ret = m;
m = NULL;
return NULL;
}
+static int method_import_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
+ _cleanup_(transfer_unrefp) Transfer *t = NULL;
+ int fd, force, read_only, r;
+ const char *local, *object;
+ Manager *m = userdata;
+ TransferType type;
+ uint32_t id;
+
+ r = bus_verify_polkit_async(
+ msg,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.import1.import",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
+ if (r < 0)
+ return r;
+
+ if (!machine_name_is_valid(local))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
+
+ r = setup_machine_directory((uint64_t) -1, error);
+ if (r < 0)
+ return r;
+
+ type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ? TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW;
+
+ r = transfer_new(m, &t);
+ if (r < 0)
+ return r;
+
+ t->type = type;
+ t->force_local = force;
+ t->read_only = read_only;
+
+ t->local = strdup(local);
+ if (!t->local)
+ return -ENOMEM;
+
+ t->stdin_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (t->stdin_fd < 0)
+ return -errno;
+
+ r = transfer_start(t);
+ if (r < 0)
+ return r;
+
+ object = t->object_path;
+ id = t->id;
+ t = NULL;
+
+ return sd_bus_reply_method_return(msg, "uo", id, object);
+}
+
+static int method_export_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
+ _cleanup_(transfer_unrefp) Transfer *t = NULL;
+ int fd, r;
+ const char *local, *object, *format;
+ Manager *m = userdata;
+ TransferType type;
+ uint32_t id;
+
+ r = bus_verify_polkit_async(
+ msg,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.import1.export",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = sd_bus_message_read(msg, "shs", &local, &fd, &format);
+ if (r < 0)
+ return r;
+
+ if (!machine_name_is_valid(local))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
+
+ type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ? TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW;
+
+ r = transfer_new(m, &t);
+ if (r < 0)
+ return r;
+
+ t->type = type;
+
+ if (!isempty(format)) {
+ t->format = strdup(format);
+ if (!t->format)
+ return -ENOMEM;
+ }
+
+ t->local = strdup(local);
+ if (!t->local)
+ return -ENOMEM;
+
+ t->stdout_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (t->stdout_fd < 0)
+ return -errno;
+
+ r = transfer_start(t);
+ if (r < 0)
+ return r;
+
+ object = t->object_path;
+ id = t->id;
+ t = NULL;
+
+ return sd_bus_reply_method_return(msg, "uo", id, object);
+}
+
static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
_cleanup_(transfer_unrefp) Transfer *t = NULL;
const char *remote, *local, *verify, *object;
CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
false,
+ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
if (v < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
- type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_TAR : TRANSFER_RAW;
+ r = setup_machine_directory((uint64_t) -1, error);
+ if (r < 0)
+ return r;
+
+ type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_PULL_TAR : TRANSFER_PULL_RAW;
if (manager_find(m, type, NULL, remote))
return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
if (!t->remote)
return -ENOMEM;
- t->local = strdup(local);
- if (!t->local)
- return -ENOMEM;
+ if (local) {
+ t->local = strdup(local);
+ if (!t->local)
+ return -ENOMEM;
+ }
r = transfer_start(t);
if (r < 0)
CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
false,
+ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
if (v != IMPORT_VERIFY_NO)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
- if (manager_find(m, TRANSFER_DKR, index_url, remote))
+ r = setup_machine_directory((uint64_t) -1, error);
+ if (r < 0)
+ return r;
+
+ if (manager_find(m, TRANSFER_PULL_DKR, index_url, remote))
return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
r = transfer_new(m, &t);
if (r < 0)
return r;
- t->type = TRANSFER_DKR;
+ t->type = TRANSFER_PULL_DKR;
t->verify = v;
t->force_local = force;
if (!t->remote)
return -ENOMEM;
- t->local = strdup(local);
- if (!t->local)
- return -ENOMEM;
+ if (local) {
+ t->local = strdup(local);
+ if (!t->local)
+ return -ENOMEM;
+ }
r = transfer_start(t);
if (r < 0)
if (r < 0)
return r;
- r = sd_bus_message_open_container(reply, 'a', "(ussso)");
+ r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
if (r < 0)
return r;
r = sd_bus_message_append(
reply,
- "(ussso)",
+ "(usssdo)",
t->id,
transfer_type_to_string(t->type),
t->remote,
t->local,
+ (double) t->progress_percent / 100.0,
t->object_path);
if (r < 0)
return r;
CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
false,
+ UID_INVALID,
&t->manager->polkit_registry,
error);
if (r < 0)
CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
false,
+ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
return sd_bus_reply_method_return(msg, NULL);
}
+static int property_get_progress(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Transfer *t = userdata;
+
+ assert(bus);
+ assert(reply);
+ assert(t);
+
+ return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
+}
+
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("LogMessage", "us", 0),
SD_BUS_VTABLE_END,
static const sd_bus_vtable manager_vtable[] = {
SD_BUS_VTABLE_START(0),
+ SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("ListTransfers", NULL, "a(ussso)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("TransferNew", "uo", 0),
SD_BUS_SIGNAL("TransferRemoved", "uos", 0),