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