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