chiark / gitweb /
networkd: avoid segfault
[elogind.git] / src / network / networkd-manager.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 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 "path-util.h"
23 #include "networkd.h"
24 #include "libudev-private.h"
25
26 int manager_new(Manager **ret) {
27         _cleanup_manager_free_ Manager *m = NULL;
28         int r;
29
30         m = new0(Manager, 1);
31         if (!m)
32                 return -ENOMEM;
33
34         r = sd_event_default(&m->event);
35         if (r < 0)
36                 return r;
37
38         r = sd_rtnl_open(0, &m->rtnl);
39         if (r < 0)
40                 return r;
41
42         m->udev = udev_new();
43         if (!m->udev)
44                 return -ENOMEM;
45
46         m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
47         if (!m->udev_monitor)
48                 return -ENOMEM;
49
50         m->links = hashmap_new(uint64_hash_func, uint64_compare_func);
51         if (!m->links)
52                 return -ENOMEM;
53
54         LIST_HEAD_INIT(m->networks);
55
56         m->network_dirs = strv_new("/etc/systemd/network/",
57                         "/run/systemd/network/",
58                         "/usr/lib/systemd/network",
59 #ifdef HAVE_SPLIT_USER
60                         "/lib/systemd/network",
61 #endif
62                         NULL);
63         if (!m->network_dirs)
64                 return -ENOMEM;
65
66         if (!path_strv_canonicalize_uniq(m->network_dirs))
67                 return -ENOMEM;
68
69         *ret = m;
70         m = NULL;
71
72         return 0;
73 }
74
75 void manager_free(Manager *m) {
76         Network *network;
77         Link *link;
78
79         udev_monitor_unref(m->udev_monitor);
80         udev_unref(m->udev);
81         sd_event_source_unref(m->udev_event_source);
82         sd_event_unref(m->event);
83
84         while ((network = m->networks))
85                 network_free(network);
86
87         while ((link = hashmap_first(m->links)))
88                 link_free(link);
89         hashmap_free(m->links);
90
91         strv_free(m->network_dirs);
92         sd_rtnl_unref(m->rtnl);
93
94         free(m);
95 }
96
97 static int manager_process_link(Manager *m, struct udev_device *device) {
98         Link *link;
99         int r;
100
101         if (streq_ptr(udev_device_get_action(device), "remove")) {
102                 uint64_t ifindex;
103
104                 log_debug("Link removed: %s", udev_device_get_sysname(device));
105
106                 ifindex = udev_device_get_ifindex(device);
107                 link = hashmap_get(m->links, &ifindex);
108                 if (!link)
109                         return 0;
110
111                 link_free(link);
112         } else {
113                 log_debug("New link: %s", udev_device_get_sysname(device));
114
115                 r = link_add(m, device);
116                 if (r < 0) {
117                         log_error("Could not handle link %s: %s",
118                                         udev_device_get_sysname(device),
119                                         strerror(-r));
120                 }
121         }
122
123         return 0;
124 }
125
126 int manager_udev_enumerate_links(Manager *m) {
127         struct udev_list_entry *item = NULL, *first = NULL;
128         struct udev_enumerate *e;
129         int r;
130
131         assert(m);
132
133         e = udev_enumerate_new(m->udev);
134         if (!e) {
135                 r = -ENOMEM;
136                 goto finish;
137         }
138
139         r = udev_enumerate_add_match_subsystem(e, "net");
140         if (r < 0)
141                 goto finish;
142
143         r = udev_enumerate_scan_devices(e);
144         if (r < 0)
145                 goto finish;
146
147         first = udev_enumerate_get_list_entry(e);
148         udev_list_entry_foreach(item, first) {
149                 struct udev_device *d;
150                 int k;
151
152                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
153                 if (!d) {
154                         r = -ENOMEM;
155                         goto finish;
156                 }
157
158                 k = manager_process_link(m, d);
159                 udev_device_unref(d);
160
161                 if (k < 0)
162                         r = k;
163         }
164
165 finish:
166         if (e)
167                 udev_enumerate_unref(e);
168
169         return r;
170 }
171
172 static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
173         Manager *m = userdata;
174         struct udev_monitor *monitor = m->udev_monitor;
175         struct udev_device *device;
176         int r;
177
178         device = udev_monitor_receive_device(monitor);
179         if (!device)
180                 return -ENOMEM;
181
182         r = manager_process_link(m, device);
183         if (r < 0)
184                 return r;
185
186         udev_device_unref(device);
187
188         return 0;
189 }
190
191 int manager_udev_listen(Manager *m) {
192         int r;
193
194         r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL);
195         if (r < 0) {
196                 log_error("Could not add udev monitor filter: %s", strerror(-r));
197                 return r;
198         }
199
200         r = udev_monitor_enable_receiving(m->udev_monitor);
201         if (r < 0) {
202                 log_error("Could not enable udev monitor");
203                 return r;
204         }
205
206         r = sd_event_add_io(m->event,
207                         udev_monitor_get_fd(m->udev_monitor),
208                         EPOLLIN, manager_dispatch_link_udev,
209                         m, &m->udev_event_source);
210         if (r < 0)
211                 return r;
212
213         return 0;
214 }
215
216 int manager_rtnl_listen(Manager *m) {
217         int r;
218
219         r = sd_rtnl_attach_event(m->rtnl, m->event, 0);
220         if (r < 0)
221                 return r;
222
223         return 0;
224 }