chiark / gitweb /
networkd: add a basic network daemon
[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_new(&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         udev_monitor_unref(m->udev_monitor);
77         udev_unref(m->udev);
78         sd_event_source_unref(m->udev_event_source);
79         sd_event_unref(m->event);
80         hashmap_free(m->links);
81         strv_free(m->network_dirs);
82         sd_rtnl_unref(m->rtnl);
83
84         free(m);
85 }
86
87 static int manager_process_link(Manager *m, struct udev_device *device) {
88         Link *link;
89         int r;
90
91         if (streq_ptr(udev_device_get_action(device), "remove")) {
92                 uint64_t ifindex;
93
94                 ifindex = udev_device_get_ifindex(device);
95                 link = hashmap_get(m->links, &ifindex);
96                 if (!link)
97                         return 0;
98
99                 link_free(link);
100         } else {
101                 r = link_add(m, device);
102                 if (r < 0) {
103                         log_error("Could not handle link %s: %s",
104                                         udev_device_get_sysname(device),
105                                         strerror(-r));
106                 }
107         }
108
109         return 0;
110 }
111
112 int manager_udev_enumerate_links(Manager *m) {
113         struct udev_list_entry *item = NULL, *first = NULL;
114         struct udev_enumerate *e;
115         int r;
116
117         assert(m);
118
119         e = udev_enumerate_new(m->udev);
120         if (!e) {
121                 r = -ENOMEM;
122                 goto finish;
123         }
124
125         r = udev_enumerate_add_match_subsystem(e, "net");
126         if (r < 0)
127                 goto finish;
128
129         r = udev_enumerate_add_match_tag(e, "systemd-networkd");
130         if (r < 0)
131                 goto finish;
132
133         r = udev_enumerate_scan_devices(e);
134         if (r < 0)
135                 goto finish;
136
137         first = udev_enumerate_get_list_entry(e);
138         udev_list_entry_foreach(item, first) {
139                 struct udev_device *d;
140                 int k;
141
142                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
143                 if (!d) {
144                         r = -ENOMEM;
145                         goto finish;
146                 }
147
148                 k = manager_process_link(m, d);
149                 udev_device_unref(d);
150
151                 if (k < 0)
152                         r = k;
153         }
154
155 finish:
156         if (e)
157                 udev_enumerate_unref(e);
158
159         return r;
160 }
161
162 static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
163         Manager *m = userdata;
164         struct udev_monitor *monitor = m->udev_monitor;
165         struct udev_device *device;
166         int r;
167
168         device = udev_monitor_receive_device(monitor);
169         if (!device)
170                 return -ENOMEM;
171
172         r = manager_process_link(m, device);
173         if (r < 0)
174                 return r;
175
176         udev_device_unref(device);
177
178         return 0;
179 }
180
181 int manager_udev_listen(Manager *m) {
182         int r;
183
184         r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL);
185         if (r < 0) {
186                 log_error("Could not add udev monitor filter: %s", strerror(-r));
187                 return r;
188         }
189
190         r = udev_monitor_filter_add_match_tag(m->udev_monitor, "systemd-networkd");
191         if (r < 0) {
192                 log_error("Could not add udev monitor filter: %s", strerror(-r));
193                 return r;
194         }
195
196         r = udev_monitor_enable_receiving(m->udev_monitor);
197         if (r < 0) {
198                 log_error("Could not enable udev monitor");
199                 return r;
200         }
201
202         r = sd_event_add_io(m->event,
203                         udev_monitor_get_fd(m->udev_monitor),
204                         EPOLLIN, manager_dispatch_link_udev,
205                         m, &m->udev_event_source);
206         if (r < 0)
207                 return r;
208
209         return 0;
210 }