]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/generator/main.c
network-generator: rename NETWORKD_UNIT_... -> NETWORK_UNIT_...
[thirdparty/systemd.git] / src / network / generator / main.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <getopt.h>
4
5 #include "build.h"
6 #include "copy.h"
7 #include "creds-util.h"
8 #include "fd-util.h"
9 #include "fs-util.h"
10 #include "generator.h"
11 #include "macro.h"
12 #include "main-func.h"
13 #include "mkdir.h"
14 #include "network-generator.h"
15 #include "path-util.h"
16 #include "proc-cmdline.h"
17 #include "recurse-dir.h"
18
19 #define NETWORK_UNIT_DIRECTORY "/run/systemd/network/"
20
21 static const char *arg_root = NULL;
22
23 static int network_save(Network *network, const char *dest_dir) {
24 _cleanup_(unlink_and_freep) char *temp_path = NULL;
25 _cleanup_fclose_ FILE *f = NULL;
26 _cleanup_free_ char *p = NULL;
27 int r;
28
29 assert(network);
30
31 r = generator_open_unit_file_full(
32 dest_dir,
33 /* source= */ NULL,
34 /* name= */ NULL,
35 &f,
36 /* ret_final_path= */ NULL,
37 &temp_path);
38 if (r < 0)
39 return r;
40
41 network_dump(network, f);
42
43 if (asprintf(&p, "%s/%s-%s.network",
44 dest_dir,
45 isempty(network->ifname) ? "71" : "70",
46 isempty(network->ifname) ? "default" : network->ifname) < 0)
47 return log_oom();
48
49 r = conservative_rename(temp_path, p);
50 if (r < 0)
51 return log_error_errno(r, "Failed to rename '%s' to '%s': %m", temp_path, p);
52
53 temp_path = mfree(temp_path);
54 return 0;
55 }
56
57 static int netdev_save(NetDev *netdev, const char *dest_dir) {
58 _cleanup_(unlink_and_freep) char *temp_path = NULL;
59 _cleanup_fclose_ FILE *f = NULL;
60 _cleanup_free_ char *p = NULL;
61 int r;
62
63 assert(netdev);
64
65 r = generator_open_unit_file_full(
66 dest_dir,
67 /* source= */ NULL,
68 /* name= */ NULL,
69 &f,
70 /* ret_final_path= */ NULL,
71 &temp_path);
72 if (r < 0)
73 return r;
74
75 netdev_dump(netdev, f);
76
77 if (asprintf(&p, "%s/70-%s.netdev", dest_dir, netdev->ifname) < 0)
78 return log_oom();
79
80 r = conservative_rename(temp_path, p);
81 if (r < 0)
82 return log_error_errno(r, "Failed to rename '%s' to '%s': %m", temp_path, p);
83
84 temp_path = mfree(temp_path);
85 return 0;
86 }
87
88 static int link_save(Link *link, const char *dest_dir) {
89 _cleanup_(unlink_and_freep) char *temp_path = NULL;
90 _cleanup_fclose_ FILE *f = NULL;
91 _cleanup_free_ char *p = NULL;
92 int r;
93
94 assert(link);
95
96 r = generator_open_unit_file_full(
97 dest_dir,
98 /* source= */ NULL,
99 /* name= */ NULL,
100 &f,
101 /* ret_final_path= */ NULL,
102 &temp_path);
103 if (r < 0)
104 return r;
105
106 link_dump(link, f);
107
108 if (asprintf(&p, "%s/%s-%s.link",
109 dest_dir,
110 !isempty(link->ifname) ? "70" : !hw_addr_is_null(&link->mac) ? "71" : "72",
111 link->filename) < 0)
112 return log_oom();
113
114 r = conservative_rename(temp_path, p);
115 if (r < 0)
116 return log_error_errno(r, "Failed to rename '%s' to '%s': %m", temp_path, p);
117
118 temp_path = mfree(temp_path);
119 return 0;
120 }
121
122 static int context_save(Context *context) {
123 Network *network;
124 NetDev *netdev;
125 Link *link;
126 int r;
127
128 const char *p = prefix_roota(arg_root, NETWORK_UNIT_DIRECTORY);
129
130 r = mkdir_p(p, 0755);
131 if (r < 0)
132 return log_error_errno(r, "Failed to create directory " NETWORK_UNIT_DIRECTORY ": %m");
133
134 HASHMAP_FOREACH(network, context->networks_by_name)
135 RET_GATHER(r, network_save(network, p));
136
137 HASHMAP_FOREACH(netdev, context->netdevs_by_name)
138 RET_GATHER(r, netdev_save(netdev, p));
139
140 HASHMAP_FOREACH(link, context->links_by_filename)
141 RET_GATHER(r, link_save(link, p));
142
143 return r;
144 }
145
146 static int pick_up_credentials(void) {
147 _cleanup_close_ int credential_dir_fd = -EBADF;
148 int r, ret = 0;
149
150 credential_dir_fd = open_credentials_dir();
151 if (IN_SET(credential_dir_fd, -ENXIO, -ENOENT)) /* Credential env var not set, or dir doesn't exist. */
152 return 0;
153 if (credential_dir_fd < 0)
154 return log_error_errno(credential_dir_fd, "Failed to open credentials directory: %m");
155
156 _cleanup_free_ DirectoryEntries *des = NULL;
157 r = readdir_all(credential_dir_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des);
158 if (r < 0)
159 return log_error_errno(r, "Failed to enumerate credentials: %m");
160
161 FOREACH_ARRAY(i, des->entries, des->n_entries) {
162 static const struct {
163 const char *credential_prefix;
164 const char *filename_suffix;
165 } table[] = {
166 { "network.link.", ".link" },
167 { "network.netdev.", ".netdev" },
168 { "network.network.", ".network" },
169 };
170
171 _cleanup_free_ char *fn = NULL;
172 struct dirent *de = *i;
173
174 if (de->d_type != DT_REG)
175 continue;
176
177 FOREACH_ARRAY(t, table, ELEMENTSOF(table)) {
178 const char *e = startswith(de->d_name, t->credential_prefix);
179
180 if (e) {
181 fn = strjoin(e, t->filename_suffix);
182 if (!fn)
183 return log_oom();
184
185 break;
186 }
187 }
188
189 if (!fn)
190 continue;
191
192 if (!filename_is_valid(fn)) {
193 log_warning("Passed credential '%s' would result in invalid filename '%s', ignoring.", de->d_name, fn);
194 continue;
195 }
196
197 _cleanup_free_ char *output = path_join(NETWORK_UNIT_DIRECTORY, fn);
198 if (!output)
199 return log_oom();
200
201 r = copy_file_at(
202 credential_dir_fd, de->d_name,
203 AT_FDCWD, output,
204 /* open_flags= */ 0,
205 0644,
206 /* flags= */ 0);
207 if (r < 0)
208 RET_GATHER(ret, log_warning_errno(r, "Failed to copy credential %s → file %s: %m", de->d_name, output));
209 else
210 log_info("Installed %s from credential.", output);
211 }
212
213 return ret;
214 }
215
216 static int help(void) {
217 printf("%s [OPTIONS...] [-- KERNEL_CMDLINE]\n"
218 " -h --help Show this help\n"
219 " --version Show package version\n"
220 " --root=PATH Operate on an alternate filesystem root\n",
221 program_invocation_short_name);
222
223 return 0;
224 }
225
226 static int parse_argv(int argc, char *argv[]) {
227 enum {
228 ARG_VERSION = 0x100,
229 ARG_ROOT,
230 };
231 static const struct option options[] = {
232 { "help", no_argument, NULL, 'h' },
233 { "version", no_argument, NULL, ARG_VERSION },
234 { "root", required_argument, NULL, ARG_ROOT },
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();
248
249 case ARG_VERSION:
250 return version();
251
252 case ARG_ROOT:
253 arg_root = optarg;
254 break;
255
256 case '?':
257 return -EINVAL;
258
259 default:
260 assert_not_reached();
261 }
262
263 return 1;
264 }
265
266 static int run(int argc, char *argv[]) {
267 _cleanup_(context_clear) Context context = {};
268 int r, ret = 0;
269
270 log_setup();
271
272 umask(0022);
273
274 r = parse_argv(argc, argv);
275 if (r <= 0)
276 return r;
277
278 if (optind >= argc) {
279 r = proc_cmdline_parse(parse_cmdline_item, &context, 0);
280 if (r < 0)
281 return log_warning_errno(r, "Failed to parse kernel command line: %m");
282 } else {
283 for (int i = optind; i < argc; i++) {
284 _cleanup_free_ char *word = NULL;
285 char *value;
286
287 word = strdup(argv[i]);
288 if (!word)
289 return log_oom();
290
291 value = strchr(word, '=');
292 if (value)
293 *(value++) = 0;
294
295 r = parse_cmdline_item(word, value, &context);
296 if (r < 0)
297 return log_warning_errno(r, "Failed to parse command line \"%s%s%s\": %m",
298 word, value ? "=" : "", strempty(value));
299 }
300 }
301
302 r = context_merge_networks(&context);
303 if (r < 0)
304 return log_warning_errno(r, "Failed to merge multiple command line options: %m");
305
306 RET_GATHER(ret, context_save(&context));
307 RET_GATHER(ret, pick_up_credentials());
308
309 return ret;
310 }
311
312 DEFINE_MAIN_FUNCTION(run);