chiark / gitweb /
rtnl: complain if used after fork
[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         int r;
64
65         r = sd_rtnl_new(&rtnl);
66         if (r < 0)
67                 return r;
68
69         rtnl->fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
70         if (rtnl->fd < 0)
71                 return -errno;
72
73         rtnl->sockaddr.nl.nl_groups = groups;
74
75         r = bind(rtnl->fd, &rtnl->sockaddr.sa, sizeof(rtnl->sockaddr));
76         if (r < 0)
77                 return -errno;
78
79         *ret = rtnl;
80         rtnl = NULL;
81
82         return 0;
83 }
84
85 sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) {
86         if (rtnl)
87                 assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
88
89         return rtnl;
90 }
91
92 sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
93         if (rtnl && REFCNT_DEC(rtnl->n_ref) <= 0) {
94                 if (rtnl->fd >= 0)
95                         close_nointr_nofail(rtnl->fd);
96                 free(rtnl);
97         }
98
99         return NULL;
100 }
101
102 int sd_rtnl_send_with_reply_and_block(sd_rtnl *nl,
103                 sd_rtnl_message *message,
104                 uint64_t usec,
105                 sd_rtnl_message **ret) {
106         struct pollfd p[1] = {};
107         struct timespec left;
108         usec_t timeout;
109         int r, serial;
110
111         assert_return(nl, -EINVAL);
112         assert_return(!rtnl_pid_changed(nl), -ECHILD);
113         assert_return(message, -EINVAL);
114
115         r = message_seal(nl, message);
116         if (r < 0)
117                 return r;
118
119         serial = message_get_serial(message);
120
121         p[0].fd = nl->fd;
122         p[0].events = POLLOUT;
123
124         if (usec == (uint64_t) -1)
125                 timeout = 0;
126         else if (usec == 0)
127                 timeout = now(CLOCK_MONOTONIC) + RTNL_DEFAULT_TIMEOUT;
128         else
129                 timeout = now(CLOCK_MONOTONIC) + usec;
130
131         for (;;) {
132                 if (timeout) {
133                         usec_t n;
134
135                         n = now(CLOCK_MONOTONIC);
136                         if (n >= timeout)
137                                 return -ETIMEDOUT;
138
139                         timespec_store(&left, timeout - n);
140                 }
141
142                 r = ppoll(p, 1, timeout ? &left : NULL, NULL);
143                 if (r < 0)
144                         return 0;
145
146                 r = socket_write_message(nl, message);
147                 if (r < 0)
148                         return r;
149
150                 if (r > 0) {
151                         break;
152                 }
153         }
154
155         p[0].events = POLLIN;
156
157         for (;;) {
158                 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *reply = NULL;
159
160                 if (timeout) {
161                         usec_t n;
162
163                         n = now(CLOCK_MONOTONIC);
164                         if (n >= timeout)
165                                 return -ETIMEDOUT;
166
167                         timespec_store(&left, timeout - n);
168                 }
169
170                 r = ppoll(p, 1, timeout ? &left : NULL, NULL);
171                 if (r < 0)
172                         return r;
173
174                 r = socket_read_message(nl, &reply);
175                 if (r < 0)
176                         return r;
177
178                 if (r > 0) {
179                         int received_serial = message_get_serial(reply);
180
181                         if (received_serial == serial) {
182                                 r = message_get_errno(reply);
183                                 if (r < 0)
184                                         return r;
185
186                                 if (ret) {
187                                         *ret = reply;
188                                         reply = NULL;
189                                 }
190
191                                 break;;
192                         }
193                 }
194         }
195
196         return 0;
197 }