1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "creds-util.h"
10 #include "generator.h"
12 #include "main-func.h"
14 #include "network-generator.h"
15 #include "path-util.h"
16 #include "proc-cmdline.h"
17 #include "recurse-dir.h"
19 #define NETWORK_UNIT_DIRECTORY "/run/systemd/network/"
21 static const char *arg_root
= NULL
;
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
;
31 r
= generator_open_unit_file_full(
36 /* ret_final_path= */ NULL
,
41 network_dump(network
, f
);
43 if (asprintf(&p
, "%s/%s-%s.network",
45 isempty(network
->ifname
) ? "71" : "70",
46 isempty(network
->ifname
) ? "default" : network
->ifname
) < 0)
49 r
= conservative_rename(temp_path
, p
);
51 return log_error_errno(r
, "Failed to rename '%s' to '%s': %m", temp_path
, p
);
53 temp_path
= mfree(temp_path
);
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
;
65 r
= generator_open_unit_file_full(
70 /* ret_final_path= */ NULL
,
75 netdev_dump(netdev
, f
);
77 if (asprintf(&p
, "%s/70-%s.netdev", dest_dir
, netdev
->ifname
) < 0)
80 r
= conservative_rename(temp_path
, p
);
82 return log_error_errno(r
, "Failed to rename '%s' to '%s': %m", temp_path
, p
);
84 temp_path
= mfree(temp_path
);
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
;
96 r
= generator_open_unit_file_full(
101 /* ret_final_path= */ NULL
,
108 if (asprintf(&p
, "%s/%s-%s.link",
110 !isempty(link
->ifname
) ? "70" : !hw_addr_is_null(&link
->mac
) ? "71" : "72",
114 r
= conservative_rename(temp_path
, p
);
116 return log_error_errno(r
, "Failed to rename '%s' to '%s': %m", temp_path
, p
);
118 temp_path
= mfree(temp_path
);
122 static int context_save(Context
*context
) {
128 const char *p
= prefix_roota(arg_root
, NETWORK_UNIT_DIRECTORY
);
130 r
= mkdir_p(p
, 0755);
132 return log_error_errno(r
, "Failed to create directory " NETWORK_UNIT_DIRECTORY
": %m");
134 HASHMAP_FOREACH(network
, context
->networks_by_name
)
135 RET_GATHER(r
, network_save(network
, p
));
137 HASHMAP_FOREACH(netdev
, context
->netdevs_by_name
)
138 RET_GATHER(r
, netdev_save(netdev
, p
));
140 HASHMAP_FOREACH(link
, context
->links_by_filename
)
141 RET_GATHER(r
, link_save(link
, p
));
146 static int pick_up_credentials(void) {
147 _cleanup_close_
int credential_dir_fd
= -EBADF
;
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. */
153 if (credential_dir_fd
< 0)
154 return log_error_errno(credential_dir_fd
, "Failed to open credentials directory: %m");
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
);
159 return log_error_errno(r
, "Failed to enumerate credentials: %m");
161 FOREACH_ARRAY(i
, des
->entries
, des
->n_entries
) {
162 static const struct {
163 const char *credential_prefix
;
164 const char *filename_suffix
;
166 { "network.link.", ".link" },
167 { "network.netdev.", ".netdev" },
168 { "network.network.", ".network" },
171 _cleanup_free_
char *fn
= NULL
;
172 struct dirent
*de
= *i
;
174 if (de
->d_type
!= DT_REG
)
177 FOREACH_ARRAY(t
, table
, ELEMENTSOF(table
)) {
178 const char *e
= startswith(de
->d_name
, t
->credential_prefix
);
181 fn
= strjoin(e
, t
->filename_suffix
);
192 if (!filename_is_valid(fn
)) {
193 log_warning("Passed credential '%s' would result in invalid filename '%s', ignoring.", de
->d_name
, fn
);
197 _cleanup_free_
char *output
= path_join(NETWORK_UNIT_DIRECTORY
, fn
);
202 credential_dir_fd
, de
->d_name
,
208 RET_GATHER(ret
, log_warning_errno(r
, "Failed to copy credential %s → file %s: %m", de
->d_name
, output
));
210 log_info("Installed %s from credential.", output
);
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
);
226 static int parse_argv(int argc
, char *argv
[]) {
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
},
242 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
260 assert_not_reached();
266 static int run(int argc
, char *argv
[]) {
267 _cleanup_(context_clear
) Context context
= {};
274 r
= parse_argv(argc
, argv
);
278 if (optind
>= argc
) {
279 r
= proc_cmdline_parse(parse_cmdline_item
, &context
, 0);
281 return log_warning_errno(r
, "Failed to parse kernel command line: %m");
283 for (int i
= optind
; i
< argc
; i
++) {
284 _cleanup_free_
char *word
= NULL
;
287 word
= strdup(argv
[i
]);
291 value
= strchr(word
, '=');
295 r
= parse_cmdline_item(word
, value
, &context
);
297 return log_warning_errno(r
, "Failed to parse command line \"%s%s%s\": %m",
298 word
, value
? "=" : "", strempty(value
));
302 r
= context_merge_networks(&context
);
304 return log_warning_errno(r
, "Failed to merge multiple command line options: %m");
306 RET_GATHER(ret
, context_save(&context
));
307 RET_GATHER(ret
, pick_up_credentials());
312 DEFINE_MAIN_FUNCTION(run
);