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