1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2013 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
22 #include "link-config.h"
24 #include "ethtool-util.h"
29 #include "path-util.h"
30 #include "conf-parser.h"
31 #include "conf-files.h"
33 struct link_config_ctx {
34 LIST_HEAD(link_config, links);
39 usec_t *link_dirs_ts_usec;
42 int link_config_ctx_new(link_config_ctx **ret) {
49 ctx = new0(link_config_ctx, 1);
53 r = ethtool_connect(&ctx->ethtool_fd);
55 link_config_ctx_free(ctx);
59 LIST_HEAD_INIT(ctx->links);
61 ctx->link_dirs = strv_new("/etc/net/links",
65 if (!ctx->link_dirs) {
66 log_error("failed to build link config directory array");
67 link_config_ctx_free(ctx);
70 if (!path_strv_canonicalize_uniq(ctx->link_dirs)) {
71 log_error("failed to canonicalize link config directories\n");
72 link_config_ctx_free(ctx);
76 ctx->link_dirs_ts_usec = calloc(strv_length(ctx->link_dirs), sizeof(usec_t));
77 if(!ctx->link_dirs_ts_usec) {
78 link_config_ctx_free(ctx);
86 static void link_configs_free(link_config_ctx *ctx) {
87 link_config *link, *link_next;
92 LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
94 free(link->match_path);
95 free(link->match_driver);
96 free(link->match_type);
97 free(link->description);
103 void link_config_ctx_free(link_config_ctx *ctx) {
107 close_nointr_nofail(ctx->ethtool_fd);
108 strv_free(ctx->link_dirs);
109 free(ctx->link_dirs_ts_usec);
110 link_configs_free(ctx);
117 static int load_link(link_config_ctx *ctx, const char *filename) {
122 file = fopen(filename, "re");
130 link = new0(link_config, 1);
136 r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", config_item_perf_lookup,
137 (void*) link_config_gperf_lookup, false, false, link);
139 log_warning("Colud not parse config file %s: %s", filename, strerror(-r));
142 log_info("Parsed configuration file %s", filename);
144 link->filename = strdup(filename);
146 LIST_PREPEND(links, ctx->links, link);
155 int link_config_load(link_config_ctx *ctx) {
159 link_configs_free(ctx);
161 /* update timestamps */
162 paths_check_timestamp(ctx->link_dirs, ctx->link_dirs_ts_usec, true);
164 r = conf_files_list_strv(&files, ".link", NULL, (const char **)ctx->link_dirs);
166 log_error("failed to enumerate link files: %s", strerror(-r));
170 STRV_FOREACH_BACKWARDS(f, files) {
171 r = load_link(ctx, *f);
179 bool link_config_should_reload(link_config_ctx *ctx) {
180 return paths_check_timestamp(ctx->link_dirs, ctx->link_dirs_ts_usec, false);
183 static bool match_config(link_config *match, struct udev_device *device) {
184 const char *property;
186 if (match->match_mac) {
187 property = udev_device_get_sysattr_value(device, "address");
188 if (!property || !streq(match->match_mac, property)) {
189 log_debug("Device MAC address (%s) did not match MACAddress=%s", property, match->match_mac);
194 if (match->match_path) {
195 property = udev_device_get_property_value(device, "ID_PATH");
196 if (!property || !streq(match->match_path, property)) {
197 log_debug("Device's persistent path (%s) did not match Path=%s", property, match->match_path);
202 if (match->match_driver) {
203 property = udev_device_get_driver(device);
204 if (!property || !streq(match->match_driver, property)) {
205 log_debug("Device driver (%s) did not match Driver=%s", property, match->match_driver);
210 if (match->match_type) {
211 property = udev_device_get_devtype(device);
212 if (!property || !streq(match->match_type, property)) {
213 log_debug("Device type (%s) did not match Type=%s", property, match->match_type);
221 int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
224 LIST_FOREACH(links, link, ctx->links) {
225 if (!match_config(link, device)) {
226 log_info("Config file %s does not apply to device %s", link->filename, udev_device_get_sysname(device));
228 log_info("Config file %s applies to device %s", link->filename, udev_device_get_sysname(device));
237 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device) {
241 name = udev_device_get_sysname(device);
245 log_info("Configuring %s", name);
247 if (config->description) {
248 r = udev_device_set_sysattr_value(device, "ifalias",
249 config->description);
251 log_warning("Could not set description of %s to '%s': %s",
252 name, config->description, strerror(-r));
254 log_info("Set link description of %s to '%s'", name,
255 config->description);
258 if (config->speed || config->duplex) {
259 r = ethtool_set_speed(ctx->ethtool_fd, name,
260 config->speed, config->duplex);
262 log_warning("Could not set speed or duplex of %s to %u Mbytes (%s): %s",
263 name, config->speed, config->duplex, strerror(-r));
265 log_info("Set speed or duplex of %s to %u Mbytes (%s)", name,
266 config->speed, config->duplex);
270 r = ethtool_set_wol(ctx->ethtool_fd, name, config->wol);
272 log_warning("Could not set WakeOnLan of %s to %s: %s",
273 name, config->wol, strerror(-r));
275 log_info("Set WakeOnLan of %s to %s", name, config->wol);