#include <stdio.h>
#include <unistd.h>
+#include "errno-util.h"
#include "io-util.h"
+#include "iovec-util.h"
#include "string-util.h"
#include "time-util.h"
assert(fd >= 0);
- /* If called with nbytes == 0, let's call read() at least
- * once, to validate the operation */
+ /* If called with nbytes == 0, let's call read() at least once, to validate the operation */
if (nbytes > (size_t) SSIZE_MAX)
return -EINVAL;
return 0;
}
-int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
- const uint8_t *p = ASSERT_PTR(buf);
+int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout) {
+ const uint8_t *p;
+ usec_t end;
+ int r;
assert(fd >= 0);
+ assert(buf || nbytes == 0);
+
+ if (nbytes == 0) {
+ static const dummy_t dummy[0];
+ assert_cc(sizeof(dummy) == 0);
+ p = (const void*) dummy; /* Some valid pointer, in case NULL was specified */
+ } else {
+ if (nbytes == SIZE_MAX)
+ nbytes = strlen(buf);
+ else if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
+ return -EINVAL;
+
+ p = buf;
+ }
- if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
- return -EINVAL;
+ /* When timeout is 0 or USEC_INFINITY this is not used. But we initialize it to a sensible value. */
+ end = timestamp_is_set(timeout) ? usec_add(now(CLOCK_MONOTONIC), timeout) : USEC_INFINITY;
do {
ssize_t k;
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() */
+ if (errno != EAGAIN || timeout == 0)
+ return -errno;
- (void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
- continue;
+ usec_t wait_for;
+
+ if (timeout == USEC_INFINITY)
+ wait_for = USEC_INFINITY;
+ else {
+ usec_t t = now(CLOCK_MONOTONIC);
+ if (t >= end)
+ return -ETIME;
+
+ wait_for = usec_sub_unsigned(end, t);
}
- return -errno;
+ r = fd_wait_for_event(fd, POLLOUT, wait_for);
+ if (timeout == USEC_INFINITY || ERRNO_IS_NEG_TRANSIENT(r))
+ /* If timeout == USEC_INFINITY we knowingly ignore any return value
+ * here, and expect that any error/EOF is reported via write() */
+ continue;
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ETIME;
}
if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */
assert(fds || nfds == 0);
+ /* This is a wrapper around ppoll() that does primarily two things:
+ *
+ * ✅ Takes a usec_t instead of a struct timespec
+ *
+ * ✅ Guarantees that if an invalid fd is specified we return EBADF (i.e. converts POLLNVAL to
+ * EBADF). This is done because EBADF is a programming error usually, and hence should bubble up
+ * as error, and not be eaten up as non-error POLLNVAL event.
+ *
+ * ⚠️ ⚠️ ⚠️ Note that this function does not add any special handling for EINTR. Don't forget
+ * poll()/ppoll() will return with EINTR on any received signal always, there is no automatic
+ * restarting via SA_RESTART available. Thus, typically you want to handle EINTR not as an error,
+ * but just as reason to restart things, under the assumption you use a more appropriate mechanism
+ * to handle signals, such as signalfd() or signal handlers. ⚠️ ⚠️ ⚠️
+ */
+
if (nfds == 0)
return 0;
};
int r;
+ /* ⚠️ ⚠️ ⚠️ Keep in mind you almost certainly want to handle -EINTR gracefully in the caller, see
+ * ppoll_usec() above! ⚠️ ⚠️ ⚠️ */
+
r = ppoll_usec(&pollfd, 1, timeout);
if (r <= 0)
return r;
return -EIO;
}
- if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
+ if (lseek(fd, n, SEEK_CUR) < 0)
return -errno;
q += n;
return q - (const uint8_t*) p;
}
-
-char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value) {
- char *x;
-
- x = strjoin(field, value);
- if (x)
- iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x);
- return x;
-}
-
-char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value) {
- char *x;
-
- x = set_iovec_string_field(iovec, n_iovec, field, value);
- free(value);
- return x;
-}
-
-struct iovec_wrapper *iovw_new(void) {
- return malloc0(sizeof(struct iovec_wrapper));
-}
-
-void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors) {
- if (free_vectors)
- for (size_t i = 0; i < iovw->count; i++)
- free(iovw->iovec[i].iov_base);
-
- iovw->iovec = mfree(iovw->iovec);
- iovw->count = 0;
-}
-
-struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw) {
- iovw_free_contents(iovw, true);
-
- return mfree(iovw);
-}
-
-struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw) {
- iovw_free_contents(iovw, false);
-
- return mfree(iovw);
-}
-
-int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) {
- if (iovw->count >= IOV_MAX)
- return -E2BIG;
-
- if (!GREEDY_REALLOC(iovw->iovec, iovw->count + 1))
- return -ENOMEM;
-
- iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len);
- return 0;
-}
-
-int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value) {
- _cleanup_free_ char *x = NULL;
- int r;
-
- x = strjoin(field, value);
- if (!x)
- return -ENOMEM;
-
- r = iovw_put(iovw, x, strlen(x));
- if (r >= 0)
- TAKE_PTR(x);
-
- return r;
-}
-
-int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value) {
- _cleanup_free_ _unused_ char *free_ptr = value;
-
- return iovw_put_string_field(iovw, field, value);
-}
-
-void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) {
- for (size_t i = 0; i < iovw->count; i++)
- iovw->iovec[i].iov_base = (char *)iovw->iovec[i].iov_base - old + new;
-}
-
-size_t iovw_size(struct iovec_wrapper *iovw) {
- size_t n = 0;
-
- for (size_t i = 0; i < iovw->count; i++)
- n += iovw->iovec[i].iov_len;
-
- return n;
-}