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