#include <curl/curl.h>
#include <sys/prctl.h>
-#include "set.h"
+#include "sd-daemon.h"
#include "json.h"
#include "strv.h"
#include "btrfs-util.h"
#include "import-common.h"
#include "import-dkr.h"
+typedef enum DkrProgress {
+ DKR_SEARCHING,
+ DKR_RESOLVING,
+ DKR_METADATA,
+ DKR_DOWNLOADING,
+ DKR_COPYING,
+} DkrProgress;
+
struct DkrImport {
sd_event *event;
CurlGlue *glue;
char **response_registries;
char **ancestry;
+ unsigned n_ancestry;
unsigned current_ancestry;
DkrImportFinished on_finished;
return 0;
}
+static void dkr_import_report_progress(DkrImport *i, DkrProgress p) {
+ unsigned percent;
+
+ assert(i);
+
+ switch (p) {
+
+ case DKR_SEARCHING:
+ percent = 0;
+ if (i->images_job)
+ percent += i->images_job->progress_percent * 5 / 100;
+ break;
+
+ case DKR_RESOLVING:
+ percent = 5;
+ if (i->tags_job)
+ percent += i->tags_job->progress_percent * 5 / 100;
+ break;
+
+ case DKR_METADATA:
+ percent = 10;
+ if (i->ancestry_job)
+ percent += i->ancestry_job->progress_percent * 5 / 100;
+ if (i->json_job)
+ percent += i->json_job->progress_percent * 5 / 100;
+ break;
+
+ case DKR_DOWNLOADING:
+ percent = 20;
+ percent += 75 * i->current_ancestry / MAX(1U, i->n_ancestry);
+ if (i->layer_job)
+ percent += i->layer_job->progress_percent * 75 / MAX(1U, i->n_ancestry) / 100;
+
+ break;
+
+ case DKR_COPYING:
+ percent = 95;
+ break;
+
+ default:
+ assert_not_reached("Unknown progress state");
+ }
+
+ sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
+ log_debug("Combined progress %u%%", percent);
+}
+
static int parse_id(const void *payload, size_t size, char **ret) {
_cleanup_free_ char *buf = NULL, *id = NULL, *other = NULL;
union json_value v = {};
assert(j);
if (i->response_token)
- t = strappenda("Authorization: Token ", i->response_token);
+ t = strjoina("Authorization: Token ", i->response_token);
else
t = HEADER_TOKEN " true";
}
static int dkr_import_job_on_open_disk(ImportJob *j) {
- _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
const char *base;
DkrImport *i;
int r;
if (base) {
const char *base_path;
- base_path = strappenda(i->image_root, "/.dkr-", base);
+ base_path = strjoina(i->image_root, "/.dkr-", base);
r = btrfs_subvol_snapshot(base_path, i->temp_path, false, true);
} else
r = btrfs_subvol_make(i->temp_path);
if (r < 0)
return log_error_errno(r, "Failed to make btrfs subvolume %s: %m", i->temp_path);
- if (pipe2(pipefd, O_CLOEXEC) < 0)
- return log_error_errno(errno, "Failed to create pipe for tar: %m");
-
- i->tar_pid = fork();
- if (i->tar_pid < 0)
- return log_error_errno(errno, "Failed to fork off tar: %m");
- if (i->tar_pid == 0) {
- int null_fd;
-
- /* Child */
-
- reset_all_signal_handlers();
- reset_signal_mask();
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
- pipefd[1] = safe_close(pipefd[1]);
-
- if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
- log_error_errno(errno, "Failed to dup2() fd: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (pipefd[0] != STDIN_FILENO)
- pipefd[0] = safe_close(pipefd[0]);
-
- null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
- if (null_fd < 0) {
- log_error_errno(errno, "Failed to open /dev/null: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
- log_error_errno(errno, "Failed to dup2() fd: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (null_fd != STDOUT_FILENO)
- null_fd = safe_close(null_fd);
+ j->disk_fd = import_fork_tar(i->temp_path, &i->tar_pid);
+ if (j->disk_fd < 0)
+ return j->disk_fd;
- fd_cloexec(STDIN_FILENO, false);
- fd_cloexec(STDOUT_FILENO, false);
- fd_cloexec(STDERR_FILENO, false);
+ return 0;
+}
- execlp("tar", "tar", "--numeric-owner", "-C", i->temp_path, "-px", NULL);
- log_error_errno(errno, "Failed to execute tar: %m");
- _exit(EXIT_FAILURE);
- }
+static void dkr_import_job_on_progress(ImportJob *j) {
+ DkrImport *i;
- pipefd[0] = safe_close(pipefd[0]);
+ assert(j);
+ assert(j->userdata);
- j->disk_fd = pipefd[1];
- pipefd[1] = -1;
+ i = j->userdata;
- return 0;
+ dkr_import_report_progress(
+ i,
+ j == i->images_job ? DKR_SEARCHING :
+ j == i->tags_job ? DKR_RESOLVING :
+ j == i->ancestry_job || j == i->json_job ? DKR_METADATA :
+ DKR_DOWNLOADING);
}
static int dkr_import_pull_layer(DkrImport *i) {
i->final_path = path;
path = NULL;
- url = strappenda(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", layer, "/layer");
+ url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", layer, "/layer");
r = import_job_new(&i->layer_job, url, i->glue, i);
if (r < 0)
return log_error_errno(r, "Failed to allocate layer job: %m");
i->layer_job->on_finished = dkr_import_job_on_finished;
i->layer_job->on_open_disk = dkr_import_job_on_open_disk;
+ i->layer_job->on_progress = dkr_import_job_on_progress;
r = import_job_begin(i->layer_job);
if (r < 0)
}
log_info("Index lookup succeeded, directed to registry %s.", i->response_registries[0]);
+ dkr_import_report_progress(i, DKR_RESOLVING);
- url = strappenda(PROTOCOL_PREFIX, i->response_registries[0], "/v1/repositories/", i->name, "/tags/", i->tag);
+ url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/repositories/", i->name, "/tags/", i->tag);
r = import_job_new(&i->tags_job, url, i->glue, i);
if (r < 0) {
log_error_errno(r, "Failed to allocate tags job: %m");
}
i->tags_job->on_finished = dkr_import_job_on_finished;
+ i->tags_job->on_progress = dkr_import_job_on_progress;
r = import_job_begin(i->tags_job);
if (r < 0) {
i->id = id;
log_info("Tag lookup succeeded, resolved to layer %s.", i->id);
+ dkr_import_report_progress(i, DKR_METADATA);
- url = strappenda(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/ancestry");
+ url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/ancestry");
r = import_job_new(&i->ancestry_job, url, i->glue, i);
if (r < 0) {
log_error_errno(r, "Failed to allocate ancestry job: %m");
}
i->ancestry_job->on_finished = dkr_import_job_on_finished;
+ i->ancestry_job->on_progress = dkr_import_job_on_progress;
- url = strappenda(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/json");
+ url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/json");
r = import_job_new(&i->json_job, url, i->glue, i);
if (r < 0) {
log_error_errno(r, "Failed to allocate json job: %m");
}
i->json_job->on_finished = dkr_import_job_on_finished;
+ i->json_job->on_progress = dkr_import_job_on_progress;
r = import_job_begin(i->ancestry_job);
if (r < 0) {
strv_free(i->ancestry);
i->ancestry = ancestry;
-
+ i->n_ancestry = n;
i->current_ancestry = 0;
+
+ dkr_import_report_progress(i, DKR_DOWNLOADING);
+
r = dkr_import_pull_layer(i);
if (r < 0)
goto finish;
r = btrfs_subvol_set_read_only(i->temp_path, true);
if (r < 0) {
- log_error_errno(r, "Failed to mark snapshort read-only: %m");
+ log_error_errno(r, "Failed to mark snapshot read-only: %m");
goto finish;
}
if (!dkr_import_is_done(i))
return;
+ dkr_import_report_progress(i, DKR_COPYING);
+
r = dkr_import_make_local_copy(i);
if (r < 0)
goto finish;
if (r < 0)
return r;
- url = strappenda(i->index_url, "/v1/repositories/", name, "/images");
+ url = strjoina(i->index_url, "/v1/repositories/", name, "/images");
r = import_job_new(&i->images_job, url, i->glue, i);
if (r < 0)
i->images_job->on_finished = dkr_import_job_on_finished;
i->images_job->on_header = dkr_import_job_on_header;
+ i->images_job->on_progress = dkr_import_job_on_progress;
return import_job_begin(i->images_job);
}