chiark / gitweb /
service: if we don't know the main pid of a service, we cannot accept any notificatio...
[elogind.git] / src / core / loopback-setup.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
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 <errno.h>
23 #include <sys/socket.h>
24 #include <net/if.h>
25 #include <asm/types.h>
26 #include <netinet/in.h>
27 #include <linux/rtnetlink.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31
32 #include "util.h"
33 #include "macro.h"
34 #include "loopback-setup.h"
35 #include "socket-util.h"
36 #include "sd-rtnl.h"
37 #include "rtnl-util.h"
38
39 static int pipe_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
40         int *counter = userdata;
41         int r;
42
43         (*counter) --;
44
45         r = sd_rtnl_message_get_errno(m);
46
47         return r == -EEXIST ? 0 : r;
48 }
49
50 static int add_addresses(sd_rtnl *rtnl, int if_loopback, struct in_addr *ipv4_address, int *counter) {
51         _cleanup_rtnl_message_unref_ sd_rtnl_message *ipv4 = NULL, *ipv6 = NULL;
52         int r;
53
54         r = sd_rtnl_message_new_addr(RTM_NEWADDR, if_loopback, AF_INET, &ipv4);
55         if (r < 0)
56                 return r;
57
58         r = sd_rtnl_message_addr_set_prefixlen(ipv4, 8);
59         if (r < 0)
60                 return r;
61
62         r = sd_rtnl_message_addr_set_flags(ipv4, IFA_F_PERMANENT);
63         if (r < 0)
64                 return r;
65
66         r = sd_rtnl_message_addr_set_scope(ipv4, RT_SCOPE_HOST);
67         if (r < 0)
68                 return r;
69
70         r = sd_rtnl_message_append_in_addr(ipv4, IFA_LOCAL, ipv4_address);
71         if (r < 0)
72                 return r;
73
74         r = sd_rtnl_call_async(rtnl, ipv4, &pipe_handler, counter, 0, NULL);
75         if (r < 0)
76                 return r;
77
78         (*counter) ++;
79
80         if (!socket_ipv6_is_supported())
81                 return 0;
82
83         r = sd_rtnl_message_new_addr(RTM_NEWADDR, if_loopback, AF_INET6, &ipv6);
84         if (r < 0)
85                 return r;
86
87         r = sd_rtnl_message_addr_set_prefixlen(ipv6, 128);
88         if (r < 0)
89                 return r;
90
91         r = sd_rtnl_message_addr_set_flags(ipv6, IFA_F_PERMANENT);
92         if (r < 0)
93                 return r;
94
95         r = sd_rtnl_message_addr_set_scope(ipv6, RT_SCOPE_HOST);
96         if (r < 0)
97                 return r;
98
99         r = sd_rtnl_message_append_in6_addr(ipv6, IFA_LOCAL, &in6addr_loopback);
100         if (r < 0)
101                 return r;
102
103         r = sd_rtnl_call_async(rtnl, ipv6, &pipe_handler, counter, 0, NULL);
104         if (r < 0)
105                 return r;
106
107         (*counter) ++;
108
109         return 0;
110 }
111
112 static int start_interface(sd_rtnl *rtnl, int if_loopback, struct in_addr *ipv4_address, int *counter) {
113         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
114         int r;
115
116         r = sd_rtnl_message_new_link(RTM_SETLINK, if_loopback, &req);
117         if (r < 0)
118                 return r;
119
120         r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP);
121         if (r < 0)
122                 return r;
123
124         r = sd_rtnl_call_async(rtnl, req, &pipe_handler, counter, 0, NULL);
125         if (r < 0)
126                 return r;
127
128         (*counter) ++;
129
130         return 0;
131 }
132
133 static int check_loopback(void) {
134         int r;
135         _cleanup_close_ int fd = -1;
136         union {
137                 struct sockaddr sa;
138                 struct sockaddr_in in;
139         } sa = {
140                 .in.sin_family = AF_INET,
141                 .in.sin_addr.s_addr = INADDR_LOOPBACK,
142         };
143
144         /* If we failed to set up the loop back device, check whether
145          * it might already be set up */
146
147         fd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
148         if (fd < 0)
149                 return -errno;
150
151         if (bind(fd, &sa.sa, sizeof(sa.in)) >= 0)
152                 r = 1;
153         else
154                 r = errno == EADDRNOTAVAIL ? 0 : -errno;
155
156         return r;
157 }
158
159 int loopback_setup(void) {
160         _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
161         int r, if_loopback, counter = 0;
162         bool eperm = false;
163         struct in_addr ipv4_address;
164
165         errno = 0;
166         if_loopback = (int) if_nametoindex("lo");
167         if (if_loopback <= 0)
168                 return errno ? -errno : -ENODEV;
169
170         ipv4_address.s_addr = htonl(INADDR_LOOPBACK);
171
172         r = sd_rtnl_open(0, &rtnl);
173         if (r < 0)
174                 return r;
175
176         r = add_addresses(rtnl, if_loopback, &ipv4_address, &counter);
177         if (r < 0)
178                 return r;
179
180         r = start_interface(rtnl, if_loopback, &ipv4_address, &counter);
181         if (r < 0)
182                 return r;
183
184         while (counter > 0) {
185                 r = sd_rtnl_wait(rtnl, 0);
186                 if (r < 0)
187                         return r;
188
189                 r = sd_rtnl_process(rtnl, 0);
190                 if (r < 0) {
191                         if (r == -EPERM)
192                                 eperm = true;
193                         else {
194                                 log_warning("Failed to configure loopback device: %s", strerror(-r));
195                                 return r;
196                         }
197                 }
198         }
199
200         if (eperm && check_loopback() < 0) {
201                 log_warning("Failed to configure loopback device: %s", strerror(EPERM));
202                 return -EPERM;
203         }
204
205         return 0;
206 }