Systemd/src/basic/io-util.c
Zbigniew Jędrzejewski-Szmek 11a1589223 tree-wide: drop license boilerplate
Files which are installed as-is (any .service and other unit files, .conf
files, .policy files, etc), are left as is. My assumption is that SPDX
identifiers are not yet that well known, so it's better to retain the
extended header to avoid any doubt.

I also kept any copyright lines. We can probably remove them, but it'd nice to
obtain explicit acks from all involved authors before doing that.
2018-04-06 18:58:55 +02:00

260 lines
6.7 KiB
C

/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
***/
#include <errno.h>
#include <limits.h>
#include <poll.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include "io-util.h"
#include "time-util.h"
int flush_fd(int fd) {
struct pollfd pollfd = {
.fd = fd,
.events = POLLIN,
};
int count = 0;
/* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything
* read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read
* (due to IP packet checksum mismatches), hence this function is only safe to be non-blocking if the fd used
* was set to non-blocking too. */
for (;;) {
char buf[LINE_MAX];
ssize_t l;
int r;
r = poll(&pollfd, 1, 0);
if (r < 0) {
if (errno == EINTR)
continue;
return -errno;
} else if (r == 0)
return count;
l = read(fd, buf, sizeof(buf));
if (l < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN)
return count;
return -errno;
} else if (l == 0)
return count;
count += (int) l;
}
}
ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
uint8_t *p = buf;
ssize_t n = 0;
assert(fd >= 0);
assert(buf);
/* If called with nbytes == 0, let's call read() at least
* once, to validate the operation */
if (nbytes > (size_t) SSIZE_MAX)
return -EINVAL;
do {
ssize_t k;
k = read(fd, p, nbytes);
if (k < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN && do_poll) {
/* We knowingly ignore any return value here,
* and expect that any error/EOF is reported
* via read() */
(void) fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
continue;
}
return n > 0 ? n : -errno;
}
if (k == 0)
return n;
assert((size_t) k <= nbytes);
p += k;
nbytes -= k;
n += k;
} while (nbytes > 0);
return n;
}
int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
ssize_t n;
n = loop_read(fd, buf, nbytes, do_poll);
if (n < 0)
return (int) n;
if ((size_t) n != nbytes)
return -EIO;
return 0;
}
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
const uint8_t *p = buf;
assert(fd >= 0);
assert(buf);
if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
return -EINVAL;
do {
ssize_t k;
k = write(fd, p, nbytes);
if (k < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN && do_poll) {
/* We knowingly ignore any return value here,
* and expect that any error/EOF is reported
* via write() */
(void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
continue;
}
return -errno;
}
if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */
return -EIO;
assert((size_t) k <= nbytes);
p += k;
nbytes -= k;
} while (nbytes > 0);
return 0;
}
int pipe_eof(int fd) {
struct pollfd pollfd = {
.fd = fd,
.events = POLLIN|POLLHUP,
};
int r;
r = poll(&pollfd, 1, 0);
if (r < 0)
return -errno;
if (r == 0)
return 0;
return pollfd.revents & POLLHUP;
}
int fd_wait_for_event(int fd, int event, usec_t t) {
struct pollfd pollfd = {
.fd = fd,
.events = event,
};
struct timespec ts;
int r;
r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL);
if (r < 0)
return -errno;
if (r == 0)
return 0;
return pollfd.revents;
}
static size_t nul_length(const uint8_t *p, size_t sz) {
size_t n = 0;
while (sz > 0) {
if (*p != 0)
break;
n++;
p++;
sz--;
}
return n;
}
ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
const uint8_t *q, *w, *e;
ssize_t l;
q = w = p;
e = q + sz;
while (q < e) {
size_t n;
n = nul_length(q, e - q);
/* If there are more than the specified run length of
* NUL bytes, or if this is the beginning or the end
* of the buffer, then seek instead of write */
if ((n > run_length) ||
(n > 0 && q == p) ||
(n > 0 && q + n >= e)) {
if (q > w) {
l = write(fd, w, q - w);
if (l < 0)
return -errno;
if (l != q -w)
return -EIO;
}
if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
return -errno;
q += n;
w = q;
} else if (n > 0)
q += n;
else
q++;
}
if (q > w) {
l = write(fd, w, q - w);
if (l < 0)
return -errno;
if (l != q - w)
return -EIO;
}
return q - (const uint8_t*) p;
}