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