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