chiark / gitweb /
tree-wide: make ++/-- usage consistent WRT spacing
[elogind.git] / src / basic / io-util.c
1 /***
2   This file is part of systemd.
3
4   Copyright 2010 Lennart Poettering
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <limits.h>
22 #include <poll.h>
23 #include <stdio.h>
24 #include <time.h>
25 #include <unistd.h>
26
27 #include "io-util.h"
28 #include "time-util.h"
29
30 int flush_fd(int fd) {
31         struct pollfd pollfd = {
32                 .fd = fd,
33                 .events = POLLIN,
34         };
35
36         for (;;) {
37                 char buf[LINE_MAX];
38                 ssize_t l;
39                 int r;
40
41                 r = poll(&pollfd, 1, 0);
42                 if (r < 0) {
43                         if (errno == EINTR)
44                                 continue;
45
46                         return -errno;
47
48                 } else if (r == 0)
49                         return 0;
50
51                 l = read(fd, buf, sizeof(buf));
52                 if (l < 0) {
53
54                         if (errno == EINTR)
55                                 continue;
56
57                         if (errno == EAGAIN)
58                                 return 0;
59
60                         return -errno;
61                 } else if (l == 0)
62                         return 0;
63         }
64 }
65
66 ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
67         uint8_t *p = buf;
68         ssize_t n = 0;
69
70         assert(fd >= 0);
71         assert(buf);
72
73         /* If called with nbytes == 0, let's call read() at least
74          * once, to validate the operation */
75
76         if (nbytes > (size_t) SSIZE_MAX)
77                 return -EINVAL;
78
79         do {
80                 ssize_t k;
81
82                 k = read(fd, p, nbytes);
83                 if (k < 0) {
84                         if (errno == EINTR)
85                                 continue;
86
87                         if (errno == EAGAIN && do_poll) {
88
89                                 /* We knowingly ignore any return value here,
90                                  * and expect that any error/EOF is reported
91                                  * via read() */
92
93                                 (void) fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
94                                 continue;
95                         }
96
97                         return n > 0 ? n : -errno;
98                 }
99
100                 if (k == 0)
101                         return n;
102
103                 assert((size_t) k <= nbytes);
104
105                 p += k;
106                 nbytes -= k;
107                 n += k;
108         } while (nbytes > 0);
109
110         return n;
111 }
112
113 int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
114         ssize_t n;
115
116         n = loop_read(fd, buf, nbytes, do_poll);
117         if (n < 0)
118                 return (int) n;
119         if ((size_t) n != nbytes)
120                 return -EIO;
121
122         return 0;
123 }
124
125 int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
126         const uint8_t *p = buf;
127
128         assert(fd >= 0);
129         assert(buf);
130
131         if (nbytes > (size_t) SSIZE_MAX)
132                 return -EINVAL;
133
134         do {
135                 ssize_t k;
136
137                 k = write(fd, p, nbytes);
138                 if (k < 0) {
139                         if (errno == EINTR)
140                                 continue;
141
142                         if (errno == EAGAIN && do_poll) {
143                                 /* We knowingly ignore any return value here,
144                                  * and expect that any error/EOF is reported
145                                  * via write() */
146
147                                 (void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
148                                 continue;
149                         }
150
151                         return -errno;
152                 }
153
154                 if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */
155                         return -EIO;
156
157                 assert((size_t) k <= nbytes);
158
159                 p += k;
160                 nbytes -= k;
161         } while (nbytes > 0);
162
163         return 0;
164 }
165
166 int pipe_eof(int fd) {
167         struct pollfd pollfd = {
168                 .fd = fd,
169                 .events = POLLIN|POLLHUP,
170         };
171
172         int r;
173
174         r = poll(&pollfd, 1, 0);
175         if (r < 0)
176                 return -errno;
177
178         if (r == 0)
179                 return 0;
180
181         return pollfd.revents & POLLHUP;
182 }
183
184 int fd_wait_for_event(int fd, int event, usec_t t) {
185
186         struct pollfd pollfd = {
187                 .fd = fd,
188                 .events = event,
189         };
190
191         struct timespec ts;
192         int r;
193
194         r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL);
195         if (r < 0)
196                 return -errno;
197
198         if (r == 0)
199                 return 0;
200
201         return pollfd.revents;
202 }
203
204 static size_t nul_length(const uint8_t *p, size_t sz) {
205         size_t n = 0;
206
207         while (sz > 0) {
208                 if (*p != 0)
209                         break;
210
211                 n++;
212                 p++;
213                 sz--;
214         }
215
216         return n;
217 }
218
219 ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
220         const uint8_t *q, *w, *e;
221         ssize_t l;
222
223         q = w = p;
224         e = q + sz;
225         while (q < e) {
226                 size_t n;
227
228                 n = nul_length(q, e - q);
229
230                 /* If there are more than the specified run length of
231                  * NUL bytes, or if this is the beginning or the end
232                  * of the buffer, then seek instead of write */
233                 if ((n > run_length) ||
234                     (n > 0 && q == p) ||
235                     (n > 0 && q + n >= e)) {
236                         if (q > w) {
237                                 l = write(fd, w, q - w);
238                                 if (l < 0)
239                                         return -errno;
240                                 if (l != q -w)
241                                         return -EIO;
242                         }
243
244                         if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
245                                 return -errno;
246
247                         q += n;
248                         w = q;
249                 } else if (n > 0)
250                         q += n;
251                 else
252                         q++;
253         }
254
255         if (q > w) {
256                 l = write(fd, w, q - w);
257                 if (l < 0)
258                         return -errno;
259                 if (l != q - w)
260                         return -EIO;
261         }
262
263         return q - (const uint8_t*) p;
264 }