chiark / gitweb /
ed145b9ab313230a27c4e2a3f83aaa548a7983f3
[elogind.git] / src / libsystemd-rtnl / sd-rtnl.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 <sys/socket.h>
23 #include <poll.h>
24
25 #include "macro.h"
26 #include "util.h"
27
28 #include "sd-rtnl.h"
29 #include "rtnl-internal.h"
30
31 static int sd_rtnl_new(sd_rtnl **ret) {
32         sd_rtnl *rtnl;
33
34         assert_return(ret, -EINVAL);
35
36         rtnl = new0(sd_rtnl, 1);
37         if (!rtnl)
38                 return -ENOMEM;
39
40         rtnl->n_ref = REFCNT_INIT;
41
42         rtnl->fd = -1;
43
44         rtnl->sockaddr.nl.nl_family = AF_NETLINK;
45
46         rtnl->original_pid = getpid();
47
48         *ret = rtnl;
49         return 0;
50 }
51
52 static bool rtnl_pid_changed(sd_rtnl *rtnl) {
53         assert(rtnl);
54
55         /* We don't support people creating an rtnl connection and
56          * keeping it around over a fork(). Let's complain. */
57
58         return rtnl->original_pid != getpid();
59 }
60
61 int sd_rtnl_open(uint32_t groups, sd_rtnl **ret) {
62         _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
63         socklen_t addrlen;
64         int r;
65
66         r = sd_rtnl_new(&rtnl);
67         if (r < 0)
68                 return r;
69
70         rtnl->fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
71         if (rtnl->fd < 0)
72                 return -errno;
73
74         rtnl->sockaddr.nl.nl_groups = groups;
75
76         addrlen = sizeof(rtnl->sockaddr);
77
78         r = bind(rtnl->fd, &rtnl->sockaddr.sa, addrlen);
79         if (r < 0)
80                 return -errno;
81
82         r = getsockname(rtnl->fd, &rtnl->sockaddr.sa, &addrlen);
83         if (r < 0)
84                 return r;
85
86         *ret = rtnl;
87         rtnl = NULL;
88
89         return 0;
90 }
91
92 sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) {
93         if (rtnl)
94                 assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
95
96         return rtnl;
97 }
98
99 sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
100         if (rtnl && REFCNT_DEC(rtnl->n_ref) <= 0) {
101                 if (rtnl->fd >= 0)
102                         close_nointr_nofail(rtnl->fd);
103                 free(rtnl);
104         }
105
106         return NULL;
107 }
108
109 int sd_rtnl_send_with_reply_and_block(sd_rtnl *nl,
110                 sd_rtnl_message *message,
111                 uint64_t usec,
112                 sd_rtnl_message **ret) {
113         struct pollfd p[1] = {};
114         struct timespec left;
115         usec_t timeout;
116         int r, serial;
117
118         assert_return(nl, -EINVAL);
119         assert_return(!rtnl_pid_changed(nl), -ECHILD);
120         assert_return(message, -EINVAL);
121
122         r = message_seal(nl, message);
123         if (r < 0)
124                 return r;
125
126         serial = message_get_serial(message);
127
128         p[0].fd = nl->fd;
129         p[0].events = POLLOUT;
130
131         if (usec == (uint64_t) -1)
132                 timeout = 0;
133         else if (usec == 0)
134                 timeout = now(CLOCK_MONOTONIC) + RTNL_DEFAULT_TIMEOUT;
135         else
136                 timeout = now(CLOCK_MONOTONIC) + usec;
137
138         for (;;) {
139                 if (timeout) {
140                         usec_t n;
141
142                         n = now(CLOCK_MONOTONIC);
143                         if (n >= timeout)
144                                 return -ETIMEDOUT;
145
146                         timespec_store(&left, timeout - n);
147                 }
148
149                 r = ppoll(p, 1, timeout ? &left : NULL, NULL);
150                 if (r < 0)
151                         return 0;
152
153                 r = socket_write_message(nl, message);
154                 if (r < 0)
155                         return r;
156
157                 if (r > 0) {
158                         break;
159                 }
160         }
161
162         p[0].events = POLLIN;
163
164         for (;;) {
165                 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *reply = NULL;
166
167                 if (timeout) {
168                         usec_t n;
169
170                         n = now(CLOCK_MONOTONIC);
171                         if (n >= timeout)
172                                 return -ETIMEDOUT;
173
174                         timespec_store(&left, timeout - n);
175                 }
176
177                 r = ppoll(p, 1, timeout ? &left : NULL, NULL);
178                 if (r < 0)
179                         return r;
180
181                 r = socket_read_message(nl, &reply);
182                 if (r < 0)
183                         return r;
184
185                 if (r > 0) {
186                         int received_serial = message_get_serial(reply);
187
188                         if (received_serial == serial) {
189                                 r = message_get_errno(reply);
190                                 if (r < 0)
191                                         return r;
192
193                                 if (ret) {
194                                         *ret = reply;
195                                         reply = NULL;
196                                 }
197
198                                 break;;
199                         }
200                 }
201         }
202
203         return 0;
204 }