chiark / gitweb /
udev: add network link configuration tool
[elogind.git] / src / udev / net / link-config.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4  This file is part of systemd.
5
6  Copyright (C) 2013 Tom Gundersen <teg@jklm.no>
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "link-config.h"
23
24 #include "util.h"
25 #include "log.h"
26 #include "strv.h"
27 #include "path-util.h"
28 #include "conf-parser.h"
29 #include "conf-files.h"
30
31 struct link_config_ctx {
32         LIST_HEAD(link_config, links);
33
34         char **link_dirs;
35         usec_t *link_dirs_ts_usec;
36 };
37
38 int link_config_ctx_new(link_config_ctx **ret) {
39         link_config_ctx *ctx;
40
41         if (!ret)
42                 return -EINVAL;
43
44         ctx = new0(link_config_ctx, 1);
45         if (!ctx)
46                 return -ENOMEM;
47
48         LIST_HEAD_INIT(ctx->links);
49
50         ctx->link_dirs = strv_new("/etc/net/links",
51                                   "/run/net/links",
52                                   "/usr/lib/net/links",
53                                   NULL);
54         if (!ctx->link_dirs) {
55                 log_error("failed to build link config directory array");
56                 link_config_ctx_free(ctx);
57                 return -ENOMEM;
58         }
59         if (!path_strv_canonicalize_uniq(ctx->link_dirs)) {
60                 log_error("failed to canonicalize link config directories\n");
61                 link_config_ctx_free(ctx);
62                 return -ENOMEM;
63         }
64
65         ctx->link_dirs_ts_usec = calloc(strv_length(ctx->link_dirs), sizeof(usec_t));
66         if(!ctx->link_dirs_ts_usec) {
67                 link_config_ctx_free(ctx);
68                 return -ENOMEM;
69         }
70
71         *ret = ctx;
72         return 0;
73 }
74
75 static void link_configs_free(link_config_ctx *ctx) {
76         link_config *link, *link_next;
77
78         if (!ctx)
79                 return;
80
81         LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
82                 free(link->filename);
83                 free(link->match_path);
84                 free(link->match_driver);
85                 free(link->match_type);
86                 free(link->description);
87
88                 free(link);
89         }
90 }
91
92 void link_config_ctx_free(link_config_ctx *ctx) {
93         if (!ctx)
94                 return;
95
96         strv_free(ctx->link_dirs);
97         free(ctx->link_dirs_ts_usec);
98         link_configs_free(ctx);
99
100         free(ctx);
101
102         return;
103 }
104
105 static int load_link(link_config_ctx *ctx, const char *filename) {
106         link_config *link;
107         FILE *file;
108         int r;
109
110         file = fopen(filename, "re");
111         if (!file) {
112                 if (errno == ENOENT)
113                         return 0;
114                 else
115                         return errno;
116         }
117
118         link = new0(link_config, 1);
119         if (!link) {
120                 r = log_oom();
121                 goto failure;
122         }
123
124         r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", config_item_perf_lookup,
125                          (void*) link_config_gperf_lookup, false, false, link);
126         if (r < 0) {
127                 log_warning("Colud not parse config file %s: %s", filename, strerror(-r));
128                 goto failure;
129         } else
130                 log_info("Parsed configuration file %s", filename);
131
132         link->filename = strdup(filename);
133
134         LIST_PREPEND(links, ctx->links, link);
135
136         return 0;
137
138 failure:
139         free(link);
140         return r;
141 }
142
143 int link_config_load(link_config_ctx *ctx) {
144         int r;
145         char **files, **f;
146
147         link_configs_free(ctx);
148
149         /* update timestamps */
150         paths_check_timestamp(ctx->link_dirs, ctx->link_dirs_ts_usec, true);
151
152         r = conf_files_list_strv(&files, ".link", NULL, (const char **)ctx->link_dirs);
153         if (r < 0) {
154                 log_error("failed to enumerate link files: %s", strerror(-r));
155                 return r;
156         }
157
158         STRV_FOREACH_BACKWARDS(f, files) {
159                 r = load_link(ctx, *f);
160                 if (r < 0)
161                         return r;
162         }
163
164         return 0;
165 }
166
167 bool link_config_should_reload(link_config_ctx *ctx) {
168         return paths_check_timestamp(ctx->link_dirs, ctx->link_dirs_ts_usec, false);
169 }
170
171 static bool match_config(link_config *match, struct udev_device *device) {
172         const char *property;
173
174         if (match->match_mac) {
175                 property = udev_device_get_sysattr_value(device, "address");
176                 if (!property || !streq(match->match_mac, property)) {
177                         log_debug("Device MAC address (%s) did not match MACAddress=%s", property, match->match_mac);
178                         return 0;
179                 }
180         }
181
182         if (match->match_path) {
183                 property = udev_device_get_property_value(device, "ID_PATH");
184                 if (!property || !streq(match->match_path, property)) {
185                         log_debug("Device's persistent path (%s) did not match Path=%s", property, match->match_path);
186                         return 0;
187                 }
188         }
189
190         if (match->match_driver) {
191                 property = udev_device_get_driver(device);
192                 if (!property || !streq(match->match_driver, property)) {
193                         log_debug("Device driver (%s) did not match Driver=%s", property, match->match_driver);
194                         return 0;
195                 }
196         }
197
198         if (match->match_type) {
199                 property = udev_device_get_devtype(device);
200                 if (!property || !streq(match->match_type, property)) {
201                         log_debug("Device type (%s) did not match Type=%s", property, match->match_type);
202                         return 0;
203                 }
204         }
205
206         return 1;
207 }
208
209 int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
210         link_config *link;
211
212         LIST_FOREACH(links, link, ctx->links) {
213                 if (!match_config(link, device)) {
214                         log_info("Config file %s does not apply to device %s", link->filename, udev_device_get_sysname(device));
215                 } else {
216                         log_info("Config file %s applies to device %s", link->filename, udev_device_get_sysname(device));
217                         *ret = link;
218                         return 0;
219                 }
220         }
221
222         return -ENOENT;
223 }
224
225 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device) {
226         const char *name;
227         int r;
228
229         name = udev_device_get_sysname(device);
230         if (!name)
231                 return -EINVAL;
232
233         log_info("Configuring %s", name);
234
235         if (config->description) {
236                 r = udev_device_set_sysattr_value(device, "ifalias", config->description);
237                 if (r < 0)
238                         log_warning("Could not set description of %s to '%s': %s", name, config->description, strerror(-r));
239                 else
240                         log_info("Set link description of %s to '%s'", name, config->description);
241         }
242
243         return 0;
244 }