qLibc
qio.c
Go to the documentation of this file.
1/******************************************************************************
2 * qLibc
3 *
4 * Copyright (c) 2010-2026 Seungyoung Kim.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *****************************************************************************/
28
29/**
30 * @file qio.c I/O handling APIs.
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <stdbool.h>
36#include <stdarg.h>
37#include <string.h>
38#include <unistd.h>
39#include <poll.h>
40#include <errno.h>
41#include "qinternal.h"
42#include "utilities/qio.h"
43
44#define MAX_IOSEND_SIZE (32 * 1024)
45
46/**
47 * Wait until a file descriptor becomes readable.
48 *
49 * @param fd file descriptor
50 * @param timeoutms wait timeout milliseconds. 0 for no wait,
51 * -1 for infinite wait
52 *
53 * @return 1 if readable, 0 on timeout, -1 if an error occurred.
54 *
55 * @note
56 * `timeoutms` can be used to set the maximum wait time for a socket
57 * descriptor.
58 */
59int qio_wait_readable(int fd, int timeoutms) {
60 struct pollfd fds[1];
61
62 fds[0].fd = fd;
63 fds[0].events = POLLIN;
64
65 int pollret = poll(fds, 1, timeoutms);
66 if (pollret == 0) {
67 errno = ETIMEDOUT;
68 return 0;
69 } else if (pollret < 0) {
70 return -1;
71 }
72
73 if (fds[0].revents & POLLIN)
74 return 1;
75 return -1;
76}
77
78/**
79 * Wait until a file descriptor is ready for writing.
80 *
81 * @param fd file descriptor
82 * @param timeoutms wait timeout milliseconds. 0 for no wait,
83 * -1 for infinite wait
84 *
85 * @return 1 if writable, 0 on timeout, -1 if an error occurred.
86 */
87int qio_wait_writable(int fd, int timeoutms) {
88 struct pollfd fds[1];
89
90 fds[0].fd = fd;
91 fds[0].events = POLLOUT;
92
93 int pollret = poll(fds, 1, timeoutms);
94 if (pollret == 0) {
95 errno = ETIMEDOUT;
96 return 0;
97 } else if (pollret < 0) {
98 return -1;
99 }
100
101 if (fds[0].revents & POLLOUT)
102 return 1;
103 return -1;
104}
105
106/**
107 * Read from a file descriptor.
108 *
109 * @param fd file descriptor
110 * @param buf data buffer to write into
111 * @param nbytes number of bytes to read from the file descriptor and write
112 * into `buf`
113 * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
114 * wait
115 *
116 * @return the number of bytes read if successful, 0 on timeout, -1 for error.
117 */
118ssize_t qio_read(int fd, void *buf, size_t nbytes, int timeoutms) {
119 if (nbytes == 0)
120 return 0;
121
122 ssize_t total = 0;
123 while (total < nbytes) {
124 if (timeoutms >= 0 && qio_wait_readable(fd, timeoutms) <= 0)
125 break;
126
127 ssize_t rsize = read(fd, buf + total, nbytes - total);
128 if (rsize <= 0) {
129 if (errno == EAGAIN || errno == EINPROGRESS) {
130 // possible with non-block io
131 usleep(1);
132 continue;
133 }
134 break;
135 }
136 total += rsize;
137 }
138
139 if (total > 0)
140 return total;
141 else if (errno == ETIMEDOUT)
142 return 0;
143 return -1;
144}
145
146/**
147 * Write to a file descriptor.
148 *
149 * @param fd file descriptor
150 * @param buf data buffer to read from
151 * @param nbytes number of bytes to write to the file descriptor from `buf`
152 * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
153 * wait
154 *
155 * @return the number of bytes written if successful, 0 on timeout,
156 * -1 for error.
157 */
158ssize_t qio_write(int fd, const void *buf, size_t nbytes, int timeoutms) {
159 if (nbytes == 0)
160 return 0;
161
162 ssize_t total = 0;
163 while (total < nbytes) {
164 if (timeoutms >= 0 && qio_wait_writable(fd, timeoutms) <= 0)
165 break;
166 ssize_t wsize = write(fd, buf + total, nbytes - total);
167 if (wsize <= 0) {
168 if (errno == EAGAIN || errno == EINPROGRESS) {
169 // possible with non-block io
170 usleep(1);
171 continue;
172 }
173 break;
174 }
175 total += wsize;
176 }
177
178 if (total > 0)
179 return total;
180 else if (errno == ETIMEDOUT)
181 return 0;
182 return -1;
183}
184
185/**
186 * Transfer data between file descriptors.
187 *
188 * @param outfd output file descriptor
189 * @param infd input file descriptor
190 * @param nbytes the number of bytes to copy between file descriptors.
191 * 0 means transfer until end of infd.
192 * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
193 * wait
194 *
195 * @return the number of bytes transferred if successful, 0 on timeout,
196 * -1 for error.
197 */
198off_t qio_send(int outfd, int infd, off_t nbytes, int timeoutms) {
199 if (nbytes == 0)
200 return 0;
201
202 unsigned char buf[MAX_IOSEND_SIZE];
203
204 off_t total = 0; // total size sent
205 while (total < nbytes) {
206 size_t chunksize; // this time sending size
207 if (nbytes - total <= sizeof(buf))
208 chunksize = nbytes - total;
209 else
210 chunksize = sizeof(buf);
211
212 // read
213 ssize_t rsize = qio_read(infd, buf, chunksize, timeoutms);
214 DEBUG("read %zd", rsize);
215 if (rsize <= 0)
216 break;
217
218 // write
219 ssize_t wsize = qio_write(outfd, buf, rsize, timeoutms);
220 DEBUG("write %zd", wsize);
221 if (wsize <= 0)
222 break;
223
224 total += wsize;
225 if (rsize != wsize) {
226 DEBUG("size mismatch. read:%zd, write:%zd", rsize, wsize);
227 break;
228 }
229 }
230
231 if (total > 0)
232 return total;
233 else if (errno == ETIMEDOUT)
234 return 0;
235 return -1;
236}
237
238/**
239 * Read a line from a file descriptor into the buffer until a terminating
240 * newline or EOF. Newline characters (CR, LF) are not stored in the buffer.
241 *
242 * @param fd file descriptor
243 * @param buf data buffer pointer
244 * @param bufsize buffer size
245 * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
246 * wait
247 *
248 * @return the number of bytes read if successful, 0 on timeout, -1 for error.
249 *
250 * @note
251 * The return value is not the length of the stored string.
252 * It is the number of bytes read from the file descriptor, so newline
253 * characters are counted even though they are not stored.
254 */
255ssize_t qio_gets(int fd, char *buf, size_t bufsize, int timeoutms) {
256 if (bufsize <= 1)
257 return -1;
258
259 ssize_t readcnt = 0;
260 char *ptr;
261 for (ptr = buf; readcnt < (bufsize - 1); ptr++) {
262 ssize_t rsize = qio_read(fd, ptr, 1, timeoutms);
263 if (rsize != 1) {
264 if (errno == EAGAIN || errno == EINPROGRESS) {
265 // possible with non-block io
266 usleep(1);
267 continue;
268 }
269 break;
270 }
271
272 readcnt++;
273 if (*ptr == '\r')
274 ptr--;
275 else if (*ptr == '\n')
276 break;
277 }
278
279 *ptr = '\0';
280
281 if (readcnt > 0)
282 return readcnt;
283 else if (errno == ETIMEDOUT)
284 return 0;
285 return -1;
286}
287
288/**
289 * Write a string and a trailing newline to a file descriptor.
290 *
291 * @param fd file descriptor
292 * @param str string pointer
293 * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
294 * wait
295 *
296 * @return the number of bytes written including trailing newline characters
297 * if successful, 0 for timeout and -1 for errors.
298 */
299ssize_t qio_puts(int fd, const char *str, int timeoutms) {
300 size_t strsize = strlen(str);
301 char *newstr = (char *) malloc(strsize + 1 + 1);
302 if (newstr == NULL)
303 return -1;
304 strncpy(newstr, str, strsize);
305 newstr[strsize] = '\n';
306 newstr[strsize + 1] = '\0';
307 ssize_t ret = qio_write(fd, newstr, strsize + 1, timeoutms);
308 free(newstr);
309 return ret;
310}
311
312/**
313 * Write formatted output to a file descriptor.
314 *
315 * @param fd file descriptor
316 * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
317 * wait
318 * @param format format string
319 *
320 * @return the number of bytes written including trailing newline characters
321 * if successful, 0 for timeout and -1 for errors.
322 */
323ssize_t qio_printf(int fd, int timeoutms, const char *format, ...) {
324 char *buf;
325 DYNAMIC_VSPRINTF(buf, format);
326 if (buf == NULL)
327 return -1;
328
329 ssize_t ret = qio_write(fd, buf, strlen(buf), timeoutms);
330 free(buf);
331
332 return ret;
333}
ssize_t qio_write(int fd, const void *buf, size_t nbytes, int timeoutms)
Write to a file descriptor.
Definition qio.c:158
ssize_t qio_puts(int fd, const char *str, int timeoutms)
Write a string and a trailing newline to a file descriptor.
Definition qio.c:299
int qio_wait_readable(int fd, int timeoutms)
Wait until a file descriptor becomes readable.
Definition qio.c:59
ssize_t qio_read(int fd, void *buf, size_t nbytes, int timeoutms)
Read from a file descriptor.
Definition qio.c:118
off_t qio_send(int outfd, int infd, off_t nbytes, int timeoutms)
Transfer data between file descriptors.
Definition qio.c:198
ssize_t qio_gets(int fd, char *buf, size_t bufsize, int timeoutms)
Read a line from a file descriptor into the buffer until a terminating newline or EOF.
Definition qio.c:255
int qio_wait_writable(int fd, int timeoutms)
Wait until a file descriptor is ready for writing.
Definition qio.c:87
ssize_t qio_printf(int fd, int timeoutms, const char *format,...)
Write formatted output to a file descriptor.
Definition qio.c:323