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