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