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