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