importd.c revision 6bedfcbb2970e06a4d3280c8fb62083d252ede73
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2015 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "sd-bus.h"
#include "bus-common-errors.h"
#include "bus-util.h"
#include "def.h"
#include "fd-util.h"
#include "hostname-util.h"
#include "import-util.h"
#include "machine-pool.h"
#include "missing.h"
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "signal-util.h"
#include "socket-util.h"
#include "strv.h"
#include "util.h"
typedef enum TransferType {
_TRANSFER_TYPE_INVALID = -1,
} TransferType;
struct Transfer {
char *object_path;
char *remote;
char *local;
bool force_local;
bool read_only;
char *dkr_index_url;
char *format;
int log_fd;
char log_message[LINE_MAX];
unsigned n_canceled;
unsigned progress_percent;
int stdin_fd;
int stdout_fd;
};
struct Manager {
int notify_fd;
};
#define TRANSFERS_MAX 64
static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
[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",
};
if (!t)
return NULL;
if (t->manager)
free(t->dkr_index_url);
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;
}
int r;
assert(m);
return -E2BIG;
if (r < 0)
return r;
if (!t)
return -ENOMEM;
t->type = _TRANSFER_TYPE_INVALID;
t->log_fd = -1;
t->stdin_fd = -1;
t->stdout_fd = -1;
t->verify = _IMPORT_VERIFY_INVALID;
return -ENOMEM;
if (r < 0)
return r;
m->current_transfer_id = id;
t->manager = m;
*ret = t;
t = NULL;
return 0;
}
assert(t);
r = sd_bus_emit_signal(
t->object_path,
"org.freedesktop.import1.Transfer",
"LogMessage",
"us",
line);
if (r < 0)
log_error_errno(r, "Cannot emit message: %m");
}
assert(t);
/* Try to send out all log messages, if we can. But if we
* can't we remove the messages from the buffer, but don't
* fail */
while (t->log_message_size > 0) {
_cleanup_free_ char *n = NULL;
char *e;
if (t->log_message_size >= sizeof(t->log_message))
e = t->log_message + sizeof(t->log_message);
else {
char *a, *b;
if (a && b)
e = a < b ? a : b;
else if (a)
e = a;
else
e = b;
}
if (!e) {
if (!flush)
return;
e = t->log_message + t->log_message_size;
}
/* Skip over NUL and newlines */
e++;
t->log_message_size -= e - t->log_message;
if (!n) {
log_oom();
continue;
}
if (isempty(n))
continue;
transfer_send_log_line(t, n);
}
}
int r;
assert(t);
transfer_send_logs(t, true);
r = sd_bus_emit_signal(
"/org/freedesktop/import1",
"org.freedesktop.import1.Manager",
"TransferRemoved",
"uos",
t->id,
t->object_path,
success ? "done" :
if (r < 0)
log_error_errno(r, "Cannot emit message: %m");
transfer_unref(t);
return 0;
}
static int transfer_cancel(Transfer *t) {
int r;
assert(t);
if (r < 0)
return r;
t->n_canceled++;
return 0;
}
bool success = false;
assert(s);
assert(t);
else {
log_debug("Import process succeeded.");
success = true;
}
else
log_error("Import process failed due to unknown reason.");
t->pid = 0;
return transfer_finalize(t, success);
}
ssize_t l;
assert(s);
assert(t);
if (l <= 0) {
* close the watch, waiting for the SIGCHLD to arrive,
* before we do anything else. */
if (l < 0)
return 0;
}
t->log_message_size += l;
transfer_send_logs(t, false);
return 0;
}
static int transfer_start(Transfer *t) {
int r;
assert(t);
return -errno;
if (t->pid < 0)
return -errno;
if (t->pid == 0) {
const char *cmd[] = {
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, /* if so: the actual URL */
NULL, /* maybe --format= */
NULL, /* if so: the actual format */
NULL, /* remote */
NULL, /* local */
};
unsigned k = 0;
/* Child */
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
}
if (t->stdout_fd >= 0) {
}
if (t->stdout_fd != STDOUT_FILENO)
safe_close(t->stdout_fd);
} else {
}
}
if (t->stdin_fd >= 0) {
}
if (t->stdin_fd != STDIN_FILENO)
safe_close(t->stdin_fd);
} else {
int null_fd;
if (null_fd < 0) {
}
}
if (null_fd != STDIN_FILENO)
}
fd_cloexec(STDIN_FILENO, false);
fd_cloexec(STDOUT_FILENO, false);
fd_cloexec(STDERR_FILENO, false);
cmd[k++] = SYSTEMD_IMPORT_PATH;
cmd[k++] = SYSTEMD_EXPORT_PATH;
else
cmd[k++] = SYSTEMD_PULL_PATH;
cmd[k++] = "tar";
cmd[k++] = "raw";
else
cmd[k++] = "dkr";
if (t->verify != _IMPORT_VERIFY_INVALID) {
cmd[k++] = "--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;
}
if (t->format) {
cmd[k++] = "--format";
}
if (t->remote)
else
cmd[k++] = "-";
}
if (t->local)
}
pipefd[0] = -1;
r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
if (r < 0)
return r;
r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t);
if (r < 0)
return r;
/* Make sure always process logging before SIGCHLD */
if (r < 0)
return r;
r = sd_bus_emit_signal(
"/org/freedesktop/import1",
"org.freedesktop.import1.Manager",
"TransferNew",
"uo",
t->id,
t->object_path);
if (r < 0)
return r;
return 0;
}
Transfer *t;
if (!m)
return NULL;
safe_close(m->notify_fd);
while ((t = hashmap_first(m->transfers)))
transfer_unref(t);
hashmap_free(m->transfers);
sd_event_unref(m->event);
free(m);
return NULL;
}
};
union {
CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
} control = {};
.msg_iovlen = 1,
.msg_control = &control,
.msg_controllen = sizeof(control),
};
unsigned percent;
char *p, *e;
Transfer *t;
Iterator i;
ssize_t n;
int r;
if (n < 0) {
return 0;
return -errno;
}
log_warning("Got overly long notification datagram, ignoring.");
return 0;
}
log_warning("Got notification datagram lacking credential information, ignoring.");
return 0;
}
HASHMAP_FOREACH(t, m->transfers, i)
break;
if (!t) {
log_warning("Got notification datagram from unexpected peer, ignoring.");
return 0;
}
buf[n] = 0;
if (!p) {
if (!p)
return 0;
p += 19;
}
e = strchrnul(p, '\n');
*e = 0;
if (r < 0 || percent > 100) {
log_warning("Got invalid percent value, ignoring.");
return 0;
}
t->progress_percent = percent;
return 0;
}
static const union sockaddr_union sa = {
};
static const int one = 1;
int r;
if (!m)
return -ENOMEM;
r = sd_event_default(&m->event);
if (r < 0)
return r;
sd_event_set_watchdog(m->event, true);
r = sd_bus_default_system(&m->bus);
if (r < 0)
return r;
if (m->notify_fd < 0)
return -errno;
if (bind(m->notify_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0)
return -errno;
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 0;
}
static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
Transfer *t;
Iterator i;
assert(m);
HASHMAP_FOREACH(t, m->transfers, i) {
return t;
}
return NULL;
}
assert(m);
msg,
"org.freedesktop.import1.import",
NULL,
false,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
if (r < 0)
return r;
if (!machine_name_is_valid(local))
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->force_local = force;
if (!t->local)
return -ENOMEM;
if (t->stdin_fd < 0)
return -errno;
r = transfer_start(t);
if (r < 0)
return r;
object = t->object_path;
t = NULL;
}
int fd, r;
assert(m);
msg,
"org.freedesktop.import1.export",
NULL,
false,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
if (r < 0)
return r;
if (!machine_name_is_valid(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;
if (!t->format)
return -ENOMEM;
}
if (!t->local)
return -ENOMEM;
if (t->stdout_fd < 0)
return -errno;
r = transfer_start(t);
if (r < 0)
return r;
object = t->object_path;
t = NULL;
}
ImportVerify v;
int force, r;
assert(m);
msg,
"org.freedesktop.import1.pull",
NULL,
false,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
if (r < 0)
return r;
if (!http_url_is_valid(remote))
else if (!machine_name_is_valid(local))
else
if (v < 0)
if (r < 0)
return r;
type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_PULL_TAR : TRANSFER_PULL_RAW;
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->verify = v;
t->force_local = force;
if (!t->remote)
return -ENOMEM;
if (local) {
if (!t->local)
return -ENOMEM;
}
r = transfer_start(t);
if (r < 0)
return r;
object = t->object_path;
t = NULL;
}
ImportVerify v;
int force, r;
assert(m);
msg,
"org.freedesktop.import1.pull",
NULL,
false,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
if (r < 0)
return r;
if (!index_url)
if (!http_url_is_valid(index_url))
if (!dkr_name_is_valid(remote))
tag = "latest";
else if (!dkr_tag_is_valid(tag))
else if (!machine_name_is_valid(local))
else
if (v < 0)
if (v != IMPORT_VERIFY_NO)
if (r < 0)
return r;
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_PULL_DKR;
t->verify = v;
t->force_local = force;
if (!t->dkr_index_url)
return -ENOMEM;
if (!t->remote)
return -ENOMEM;
if (local) {
if (!t->local)
return -ENOMEM;
}
r = transfer_start(t);
if (r < 0)
return r;
object = t->object_path;
t = NULL;
}
Transfer *t;
Iterator i;
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
HASHMAP_FOREACH(t, m->transfers, i) {
"(usssdo)",
t->id,
t->remote,
t->local,
(double) t->progress_percent / 100.0,
t->object_path);
if (r < 0)
return r;
}
if (r < 0)
return r;
}
int r;
assert(t);
msg,
"org.freedesktop.import1.pull",
NULL,
false,
&t->manager->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
r = transfer_cancel(t);
if (r < 0)
return r;
}
Transfer *t;
int r;
assert(m);
msg,
"org.freedesktop.import1.pull",
NULL,
false,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
if (r < 0)
return r;
if (id <= 0)
if (!t)
r = transfer_cancel(t);
if (r < 0)
return r;
}
static int property_get_progress(
const char *path,
const char *interface,
const char *property,
void *userdata,
sd_bus_error *error) {
assert(t);
}
static const sd_bus_vtable transfer_vtable[] = {
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),
};
static const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
};
static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
Transfer *t;
const char *p;
int r;
assert(m);
if (!p)
return 0;
r = safe_atou32(p, &id);
if (r < 0 || id == 0)
return 0;
if (!t)
return 0;
*found = t;
return 1;
}
static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
_cleanup_strv_free_ char **l = NULL;
Transfer *t;
unsigned k = 0;
Iterator i;
if (!l)
return -ENOMEM;
HASHMAP_FOREACH(t, m->transfers, i) {
l[k] = strdup(t->object_path);
if (!l[k])
return -ENOMEM;
k++;
}
*nodes = l;
l = NULL;
return 1;
}
static int manager_add_bus_objects(Manager *m) {
int r;
assert(m);
r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
if (r < 0)
return log_error_errno(r, "Failed to register object: %m");
r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
if (r < 0)
return log_error_errno(r, "Failed to register object: %m");
r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
if (r < 0)
return log_error_errno(r, "Failed to add transfer enumerator: %m");
if (r < 0)
return log_error_errno(r, "Failed to register name: %m");
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
return 0;
}
static bool manager_check_idle(void *userdata) {
return hashmap_isempty(m->transfers);
}
static int manager_run(Manager *m) {
assert(m);
return bus_event_loop_with_idle(
m->event,
m->bus,
"org.freedesktop.import1",
m);
}
int r;
log_open();
umask(0022);
if (argc != 1) {
log_error("This program takes no arguments.");
r = -EINVAL;
goto finish;
}
r = manager_new(&m);
if (r < 0) {
log_error_errno(r, "Failed to allocate manager object: %m");
goto finish;
}
r = manager_add_bus_objects(m);
if (r < 0)
goto finish;
r = manager_run(m);
if (r < 0) {
log_error_errno(r, "Failed to run event loop: %m");
goto finish;
}
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}