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