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