]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/export.c
tree-wide: drop 'This file is part of systemd' blurb
[thirdparty/systemd.git] / src / import / export.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
587fec42 2/***
587fec42 3 Copyright 2015 Lennart Poettering
587fec42
LP
4***/
5
6#include <getopt.h>
7
8#include "sd-event.h"
dccca82b 9#include "sd-id128.h"
3f6fd1ba 10
b5efdb8a 11#include "alloc-util.h"
3f6fd1ba
LP
12#include "export-raw.h"
13#include "export-tar.h"
3ffd4af2 14#include "fd-util.h"
f4f15635 15#include "fs-util.h"
25300b5a 16#include "hostname-util.h"
587fec42 17#include "import-util.h"
3f6fd1ba
LP
18#include "machine-image.h"
19#include "signal-util.h"
07630cea 20#include "string-util.h"
3f6fd1ba 21#include "verbs.h"
587fec42
LP
22
23static ImportCompressType arg_compress = IMPORT_COMPRESS_UNKNOWN;
24
25static void determine_compression_from_filename(const char *p) {
26
27 if (arg_compress != IMPORT_COMPRESS_UNKNOWN)
28 return;
29
30 if (!p) {
31 arg_compress = IMPORT_COMPRESS_UNCOMPRESSED;
32 return;
33 }
34
35 if (endswith(p, ".xz"))
36 arg_compress = IMPORT_COMPRESS_XZ;
37 else if (endswith(p, ".gz"))
38 arg_compress = IMPORT_COMPRESS_GZIP;
39 else if (endswith(p, ".bz2"))
40 arg_compress = IMPORT_COMPRESS_BZIP2;
41 else
42 arg_compress = IMPORT_COMPRESS_UNCOMPRESSED;
43}
44
45static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
46 log_notice("Transfer aborted.");
47 sd_event_exit(sd_event_source_get_event(s), EINTR);
48 return 0;
49}
50
51static void on_tar_finished(TarExport *export, int error, void *userdata) {
52 sd_event *event = userdata;
53 assert(export);
54
55 if (error == 0)
56 log_info("Operation completed successfully.");
57
58 sd_event_exit(event, abs(error));
59}
60
61static int export_tar(int argc, char *argv[], void *userdata) {
62 _cleanup_(tar_export_unrefp) TarExport *export = NULL;
4afd3348 63 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
587fec42
LP
64 _cleanup_(image_unrefp) Image *image = NULL;
65 const char *path = NULL, *local = NULL;
66 _cleanup_close_ int open_fd = -1;
67 int r, fd;
68
69 if (machine_name_is_valid(argv[1])) {
5ef46e5f 70 r = image_find(IMAGE_MACHINE, argv[1], &image);
3a6ce860
LP
71 if (r == -ENOENT)
72 return log_error_errno(r, "Machine image %s not found.", argv[1]);
587fec42
LP
73 if (r < 0)
74 return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]);
587fec42
LP
75
76 local = image->path;
77 } else
78 local = argv[1];
79
80 if (argc >= 3)
81 path = argv[2];
82 if (isempty(path) || streq(path, "-"))
83 path = NULL;
84
85 determine_compression_from_filename(path);
86
87 if (path) {
88 open_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
89 if (open_fd < 0)
90 return log_error_errno(errno, "Failed to open tar image for export: %m");
91
92 fd = open_fd;
93
94 log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, path, import_compress_type_to_string(arg_compress));
95 } else {
96 _cleanup_free_ char *pretty = NULL;
97
98 fd = STDOUT_FILENO;
99
100 (void) readlink_malloc("/proc/self/fd/1", &pretty);
101 log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress));
102 }
103
104 r = sd_event_default(&event);
105 if (r < 0)
106 return log_error_errno(r, "Failed to allocate event loop: %m");
107
72c0a2c2
LP
108 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
109 (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
110 (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
587fec42
LP
111
112 r = tar_export_new(&export, event, on_tar_finished, event);
113 if (r < 0)
114 return log_error_errno(r, "Failed to allocate exporter: %m");
115
116 r = tar_export_start(export, local, fd, arg_compress);
117 if (r < 0)
118 return log_error_errno(r, "Failed to export image: %m");
119
120 r = sd_event_loop(event);
121 if (r < 0)
122 return log_error_errno(r, "Failed to run event loop: %m");
123
124 log_info("Exiting.");
125 return -r;
126}
127
128static void on_raw_finished(RawExport *export, int error, void *userdata) {
129 sd_event *event = userdata;
130 assert(export);
131
132 if (error == 0)
133 log_info("Operation completed successfully.");
134
135 sd_event_exit(event, abs(error));
136}
137
138static int export_raw(int argc, char *argv[], void *userdata) {
139 _cleanup_(raw_export_unrefp) RawExport *export = NULL;
4afd3348 140 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
587fec42
LP
141 _cleanup_(image_unrefp) Image *image = NULL;
142 const char *path = NULL, *local = NULL;
143 _cleanup_close_ int open_fd = -1;
144 int r, fd;
145
146 if (machine_name_is_valid(argv[1])) {
5ef46e5f 147 r = image_find(IMAGE_MACHINE, argv[1], &image);
3a6ce860
LP
148 if (r == -ENOENT)
149 return log_error_errno(r, "Machine image %s not found.", argv[1]);
587fec42
LP
150 if (r < 0)
151 return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]);
587fec42
LP
152
153 local = image->path;
154 } else
155 local = argv[1];
156
157 if (argc >= 3)
158 path = argv[2];
159 if (isempty(path) || streq(path, "-"))
160 path = NULL;
161
162 determine_compression_from_filename(path);
163
164 if (path) {
165 open_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
166 if (open_fd < 0)
167 return log_error_errno(errno, "Failed to open raw image for export: %m");
168
169 fd = open_fd;
170
171 log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, path, import_compress_type_to_string(arg_compress));
172 } else {
173 _cleanup_free_ char *pretty = NULL;
174
175 fd = STDOUT_FILENO;
176
177 (void) readlink_malloc("/proc/self/fd/1", &pretty);
178 log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress));
179 }
180
181 r = sd_event_default(&event);
182 if (r < 0)
183 return log_error_errno(r, "Failed to allocate event loop: %m");
184
72c0a2c2
LP
185 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
186 (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
187 (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
587fec42
LP
188
189 r = raw_export_new(&export, event, on_raw_finished, event);
190 if (r < 0)
191 return log_error_errno(r, "Failed to allocate exporter: %m");
192
193 r = raw_export_start(export, local, fd, arg_compress);
194 if (r < 0)
195 return log_error_errno(r, "Failed to export image: %m");
196
197 r = sd_event_loop(event);
198 if (r < 0)
199 return log_error_errno(r, "Failed to run event loop: %m");
200
201 log_info("Exiting.");
202 return -r;
203}
204
205static int help(int argc, char *argv[], void *userdata) {
206
207 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
208 "Export container or virtual machine images.\n\n"
209 " -h --help Show this help\n"
210 " --version Show package version\n"
211 " --format=FORMAT Select format\n\n"
212 "Commands:\n"
213 " tar NAME [FILE] Export a TAR image\n"
214 " raw NAME [FILE] Export a RAW image\n",
215 program_invocation_short_name);
216
217 return 0;
218}
219
220static int parse_argv(int argc, char *argv[]) {
221
222 enum {
223 ARG_VERSION = 0x100,
224 ARG_FORMAT,
225 };
226
227 static const struct option options[] = {
228 { "help", no_argument, NULL, 'h' },
229 { "version", no_argument, NULL, ARG_VERSION },
230 { "format", required_argument, NULL, ARG_FORMAT },
231 {}
232 };
233
234 int c;
235
236 assert(argc >= 0);
237 assert(argv);
238
239 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
240
241 switch (c) {
242
243 case 'h':
244 return help(0, NULL, NULL);
245
246 case ARG_VERSION:
3f6fd1ba 247 return version();
587fec42
LP
248
249 case ARG_FORMAT:
250 if (streq(optarg, "uncompressed"))
251 arg_compress = IMPORT_COMPRESS_UNCOMPRESSED;
252 else if (streq(optarg, "xz"))
253 arg_compress = IMPORT_COMPRESS_XZ;
254 else if (streq(optarg, "gzip"))
255 arg_compress = IMPORT_COMPRESS_GZIP;
256 else if (streq(optarg, "bzip2"))
257 arg_compress = IMPORT_COMPRESS_BZIP2;
258 else {
259 log_error("Unknown format: %s", optarg);
260 return -EINVAL;
261 }
262 break;
263
264 case '?':
265 return -EINVAL;
266
267 default:
268 assert_not_reached("Unhandled option");
269 }
270
271 return 1;
272}
273
274static int export_main(int argc, char *argv[]) {
275
276 static const Verb verbs[] = {
277 { "help", VERB_ANY, VERB_ANY, 0, help },
278 { "tar", 2, 3, 0, export_tar },
279 { "raw", 2, 3, 0, export_raw },
280 {}
281 };
282
283 return dispatch_verb(argc, argv, verbs, NULL);
284}
285
286int main(int argc, char *argv[]) {
287 int r;
288
289 setlocale(LC_ALL, "");
290 log_parse_environment();
291 log_open();
292
293 r = parse_argv(argc, argv);
294 if (r <= 0)
295 goto finish;
296
ce30c8dc 297 (void) ignore_signals(SIGPIPE, -1);
587fec42
LP
298
299 r = export_main(argc, argv);
300
301finish:
302 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
303}