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