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