the following one did not, a crash could result when adding an
Authentication-Results: header.
+JH/14 On GNU/Hurd, retry EINTR returns from I/O on pipes, which seem to occur
+ frequently.
+
Exim version 4.93
-----------------
mytypes.h \
sha_ver.h \
structs.h \
- os.h
+ os.h \
+ osfunctions.h
PHDRS = ../config.h \
../dbfunctions.h \
../dbstuff.h \
../macros.h \
../mytypes.h \
../structs.h \
- ../os.h
+ ../os.h \
+ ../osfunctions.h
.SUFFIXES: .o .c
.c.o:; @echo "$(CC) $*.c"
}
#endif /* OS_LOAD_AVERAGE */
+
+ssize_t
+os_pipe_read(int fd, void * buf, size_t count)
+{
+for (int rc, retries = 10; retries > 0; retries--)
+ {
+ if ((rc = read(fd, buf, count) >= 0) break;
+ if (rc != -1 || errno != EINTR) break;
+ }
+return rc;
+}
+
+
+ssize_t
+os_pipe_write(int fd, void * buf, size_t count)
+{
+for (int rc, retries = 10; retries > 0; retries--)
+ {
+ if ((rc = write(fd, buf, count) >= 0) break;
+ if (rc != -1 || errno != EINTR) break;
+ }
+return rc;
+}
+
+ssize_t
+os_pipe_writev(int fd, const struct iovec * iov, int iovcnt
+{
+for (int rc, retries = 10; retries > 0; retries--)
+ {
+ if ((rc = writev(fd, iov, iovcnt) >= 0) break;
+ if (rc != -1 || errno != EINTR) break;
+ }
+return rc;
+}
+
/* End of os.c-GNU */
#define ICONV_ARG2_TYPE const char **
/* setgroups(0, NULL) succeeds, and drops the gid group
-as well as any supplementary groups*/
+as well as any supplementary groups */
#define OS_SETGROUPS_ZERO_DROPS_ALL
+/* reads and writes on pipes frequently return EINTR. We provide
+a routine to retry that */
+#define OS_PIPE_RW_EINTR
+
/* End */
uschar *s;
int ret;
- if( (ret = write(pfd[pipe_write], &addr2->transport_return, sizeof(int))) != sizeof(int)
- || (ret = write(pfd[pipe_write], &transport_count, sizeof(transport_count))) != sizeof(transport_count)
- || (ret = write(pfd[pipe_write], &addr2->flags, sizeof(addr2->flags))) != sizeof(addr2->flags)
- || (ret = write(pfd[pipe_write], &addr2->basic_errno, sizeof(int))) != sizeof(int)
- || (ret = write(pfd[pipe_write], &addr2->more_errno, sizeof(int))) != sizeof(int)
- || (ret = write(pfd[pipe_write], &addr2->delivery_usec, sizeof(int))) != sizeof(int)
- || (ret = write(pfd[pipe_write], &addr2->special_action, sizeof(int))) != sizeof(int)
- || (ret = write(pfd[pipe_write], &addr2->transport,
+ if( (ret = os_pipe_write(pfd[pipe_write], &addr2->transport_return, sizeof(int))) != sizeof(int)
+ || (ret = os_pipe_write(pfd[pipe_write], &transport_count, sizeof(transport_count))) != sizeof(transport_count)
+ || (ret = os_pipe_write(pfd[pipe_write], &addr2->flags, sizeof(addr2->flags))) != sizeof(addr2->flags)
+ || (ret = os_pipe_write(pfd[pipe_write], &addr2->basic_errno, sizeof(int))) != sizeof(int)
+ || (ret = os_pipe_write(pfd[pipe_write], &addr2->more_errno, sizeof(int))) != sizeof(int)
+ || (ret = os_pipe_write(pfd[pipe_write], &addr2->delivery_usec, sizeof(int))) != sizeof(int)
+ || (ret = os_pipe_write(pfd[pipe_write], &addr2->special_action, sizeof(int))) != sizeof(int)
+ || (ret = os_pipe_write(pfd[pipe_write], &addr2->transport,
sizeof(transport_instance *))) != sizeof(transport_instance *)
/* For a file delivery, pass back the local part, in case the original
logging. */
|| (testflag(addr2, af_file)
- && ( (ret = write(pfd[pipe_write], &local_part_length, sizeof(int))) != sizeof(int)
- || (ret = write(pfd[pipe_write], addr2->local_part, local_part_length)) != local_part_length
+ && ( (ret = os_pipe_write(pfd[pipe_write], &local_part_length, sizeof(int))) != sizeof(int)
+ || (ret = os_pipe_write(pfd[pipe_write], addr2->local_part, local_part_length)) != local_part_length
)
)
)
for (i = 0, s = addr2->message; i < 2; i++, s = addr2->user_message)
{
int message_length = s ? Ustrlen(s) + 1 : 0;
- if( (ret = write(pfd[pipe_write], &message_length, sizeof(int))) != sizeof(int)
- || message_length > 0 && (ret = write(pfd[pipe_write], s, message_length)) != message_length
+ if( (ret = os_pipe_write(pfd[pipe_write], &message_length, sizeof(int))) != sizeof(int)
+ || message_length > 0 && (ret = os_pipe_write(pfd[pipe_write], s, message_length)) != message_length
)
log_write(0, LOG_MAIN|LOG_PANIC, "Failed writing transport results to pipe: %s",
ret == -1 ? strerror(errno) : "short write");
for (addr2 = addr; addr2; addr2 = addr2->next)
{
- if ((len = read(pfd[pipe_read], &status, sizeof(int))) > 0)
+ if ((len = os_pipe_read(pfd[pipe_read], &status, sizeof(int))) > 0)
{
int i;
uschar **sptr;
addr2->transport_return = status;
- len = read(pfd[pipe_read], &transport_count,
+ len = os_pipe_read(pfd[pipe_read], &transport_count,
sizeof(transport_count));
- len = read(pfd[pipe_read], &addr2->flags, sizeof(addr2->flags));
- len = read(pfd[pipe_read], &addr2->basic_errno, sizeof(int));
- len = read(pfd[pipe_read], &addr2->more_errno, sizeof(int));
- len = read(pfd[pipe_read], &addr2->delivery_usec, sizeof(int));
- len = read(pfd[pipe_read], &addr2->special_action, sizeof(int));
- len = read(pfd[pipe_read], &addr2->transport,
+ len = os_pipe_read(pfd[pipe_read], &addr2->flags, sizeof(addr2->flags));
+ len = os_pipe_read(pfd[pipe_read], &addr2->basic_errno, sizeof(int));
+ len = os_pipe_read(pfd[pipe_read], &addr2->more_errno, sizeof(int));
+ len = os_pipe_read(pfd[pipe_read], &addr2->delivery_usec, sizeof(int));
+ len = os_pipe_read(pfd[pipe_read], &addr2->special_action, sizeof(int));
+ len = os_pipe_read(pfd[pipe_read], &addr2->transport,
sizeof(transport_instance *));
if (testflag(addr2, af_file))
{
int llen;
- if ( read(pfd[pipe_read], &llen, sizeof(int)) != sizeof(int)
+ if ( os_pipe_read(pfd[pipe_read], &llen, sizeof(int)) != sizeof(int)
|| llen > 64*4 /* limit from rfc 5821, times I18N factor */
)
{
}
/* sanity-checked llen so disable the Coverity error */
/* coverity[tainted_data] */
- if (read(pfd[pipe_read], big_buffer, llen) != llen)
+ if (os_pipe_read(pfd[pipe_read], big_buffer, llen) != llen)
{
log_write(0, LOG_MAIN|LOG_PANIC, "bad local_part read"
" from delivery subprocess");
for (i = 0, sptr = &addr2->message; i < 2; i++, sptr = &addr2->user_message)
{
int message_length;
- len = read(pfd[pipe_read], &message_length, sizeof(int));
+ len = os_pipe_read(pfd[pipe_read], &message_length, sizeof(int));
if (message_length > 0)
{
- len = read(pfd[pipe_read], big_buffer, message_length);
+ len = os_pipe_read(pfd[pipe_read], big_buffer, message_length);
big_buffer[big_buffer_size-1] = '\0'; /* guard byte */
if (len > 0) *sptr = string_copy(big_buffer);
}
DEBUG(D_deliver) debug_printf("header write id:%c,subid:%c,size:%ld,final:%s\n",
id, subid, (long)size, pipe_header);
-for (int retries = 10; retries > 0; retries--)
- {
- if ((ret = writev(fd, iov, 2)) == total_len) return;
- if (ret != -1 || errno != EINTR) break;
- }
-log_write(0, LOG_MAIN|LOG_PANIC_DIE,
- "Failed writing transport result to pipe (%ld of %ld bytes): %s",
- (long)ret, (long)total_len, ret == -1 ? strerror(errno) : "short write");
+if ((ret = os_pipe_writev(fd, iov, 2)) != total_len)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "Failed writing transport result to pipe (%ld of %ld bytes): %s",
+ (long)ret, (long)total_len, ret == -1 ? strerror(errno) : "short write");
}
/*************************************************
*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2016 */
+/* Copyright (c) The Exim Maintainers 2019 */
/* See the file NOTICE for conditions of use and distribution. */
/* Prototypes for os-specific functions. For utilities, we don't need the one
predicting constness is awkward. */
#ifndef os_getloadavg
-extern int os_getloadavg(void);
+ extern int os_getloadavg(void);
#endif
#ifndef os_restarting_signal
-extern void os_restarting_signal(int, void (*)(int));
+ extern void os_restarting_signal(int, void (*)(int));
#endif
#ifndef os_non_restarting_signal
-extern void os_non_restarting_signal(int, void (*)(int));
+ extern void os_non_restarting_signal(int, void (*)(int));
#endif
#ifndef os_strexit
-extern const char *os_strexit(int); /* char to match os_strsignal */
+ extern const char *os_strexit(int); /* char to match os_strsignal */
#endif
#ifndef os_strsignal
-extern const char *os_strsignal(int); /* char to match strsignal in some OS */
+ extern const char *os_strsignal(int); /* char to match strsignal in some OS */
#endif
#ifndef os_unsetenv
-extern int os_unsetenv(const uschar *);
+ extern int os_unsetenv(const uschar *);
#endif
#ifndef os_getcwd
-extern uschar *os_getcwd(uschar *, size_t);
+ extern uschar *os_getcwd(uschar *, size_t);
+#endif
+
+#ifdef OS_PIPE_RW_EINTR
+ extern ssize_t os_pipe_read(int fd, void * buf, size_t count);
+ extern ssize_t os_pipe_write(int fd, void * buf, size_t count);
+ extern ssize_t os_pipe_writev(int fd, const struct iovec * iov, int iovcnt);
+#else
+# define os_pipe_read(fd, buf, count) read(fd, buf, count)
+# define os_pipe_write(fd, buf, count) write(fd, buf, count)
+# define os_pipe_writev(fd, buf, count) writev(fd, buf, count)
#endif
/* End of osfunctions.h */
the mere fact that read() unblocks is enough. */
set_process_info("running queue: waiting for children of %d", pid);
- if ((status = read(pfd[pipe_read], buffer, sizeof(buffer))) != 0)
- log_write(0, LOG_MAIN|LOG_PANIC, status > 0 ?
- "queue run: unexpected data on pipe" : "queue run: error on pipe: %s",
+ if ((status = os_pipe_read(pfd[pipe_read], buffer, sizeof(buffer))) != 0)
+ log_write(0, LOG_MAIN|LOG_PANIC, status > 0
+ ? "queue run: unexpected data on pipe"
+ : "queue run: error on pipe read: %s",
strerror(errno));
(void)close(pfd[pipe_read]);
set_process_info("running queue");
static int
rda_write_string(int fd, const uschar *s)
{
-int len = (s == NULL)? 0 : Ustrlen(s) + 1;
-return ( write(fd, &len, sizeof(int)) != sizeof(int)
- || (s != NULL && write(fd, s, len) != len)
+int len = s ? Ustrlen(s) + 1 : 0;
+return ( os_pipe_write(fd, &len, sizeof(int)) != sizeof(int)
+ || s && write(fd, s, len) != len
)
? -1 : 0;
}
{
int len;
-if (read(fd, &len, sizeof(int)) != sizeof(int)) return FALSE;
+if (os_pipe_read(fd, &len, sizeof(int)) != sizeof(int)) return FALSE;
if (len == 0)
*sp = NULL;
else
/* We know we have enough memory so disable the error on "len" */
/* coverity[tainted_data] */
/* We trust the data source, so untainted */
- if (read(fd, *sp = store_get(len, FALSE), len) != len) return FALSE;
+ if (os_pipe_read(fd, *sp = store_get(len, FALSE), len) != len) return FALSE;
return TRUE;
}
/* Pass back whether it was a filter, and the return code and any overall
error text via the pipe. */
- if ( write(fd, filtertype, sizeof(int)) != sizeof(int)
- || write(fd, &yield, sizeof(int)) != sizeof(int)
+ if ( os_pipe_write(fd, filtertype, sizeof(int)) != sizeof(int)
+ || os_pipe_write(fd, &yield, sizeof(int)) != sizeof(int)
|| rda_write_string(fd, *error) != 0
)
goto bad;
int i = 0;
for (header_line * h = header_list; h != waslast->next; i++, h = h->next)
if ( h->type == htype_old
- && write(fd, &i, sizeof(i)) != sizeof(i)
+ && os_pipe_write(fd, &i, sizeof(i)) != sizeof(i)
)
goto bad;
i = -1;
- if (write(fd, &i, sizeof(i)) != sizeof(i))
+ if (os_pipe_write(fd, &i, sizeof(i)) != sizeof(i))
goto bad;
while (waslast != header_last)
waslast = waslast->next;
if (waslast->type != htype_old)
if ( rda_write_string(fd, waslast->text) != 0
- || write(fd, &(waslast->type), sizeof(waslast->type))
+ || os_pipe_write(fd, &(waslast->type), sizeof(waslast->type))
!= sizeof(waslast->type)
)
goto bad;
/* Write the contents of the $n variables */
- if (write(fd, filter_n, sizeof(filter_n)) != sizeof(filter_n))
+ if (os_pipe_write(fd, filter_n, sizeof(filter_n)) != sizeof(filter_n))
goto bad;
/* If the result was DELIVERED or NOTDELIVERED, we pass back the generated
int ig_err = addr->prop.ignore_error ? 1 : 0;
if ( rda_write_string(fd, addr->address) != 0
- || write(fd, &addr->mode, sizeof(addr->mode)) != sizeof(addr->mode)
- || write(fd, &addr->flags, sizeof(addr->flags)) != sizeof(addr->flags)
+ || os_pipe_write(fd, &addr->mode, sizeof(addr->mode)) != sizeof(addr->mode)
+ || os_pipe_write(fd, &addr->flags, sizeof(addr->flags)) != sizeof(addr->flags)
|| rda_write_string(fd, addr->prop.errors_address) != 0
- || write(fd, &ig_err, sizeof(ig_err)) != sizeof(ig_err)
+ || os_pipe_write(fd, &ig_err, sizeof(ig_err)) != sizeof(ig_err)
)
goto bad;
if (!addr->reply)
{
- if (write(fd, &reply_options, sizeof(int)) != sizeof(int)) /* 0 means no reply */
+ if (os_pipe_write(fd, &reply_options, sizeof(int)) != sizeof(int)) /* 0 means no reply */
goto bad;
}
else
reply_options |= REPLY_EXISTS;
if (addr->reply->file_expand) reply_options |= REPLY_EXPAND;
if (addr->reply->return_message) reply_options |= REPLY_RETURN;
- if ( write(fd, &reply_options, sizeof(int)) != sizeof(int)
- || write(fd, &(addr->reply->expand_forbid), sizeof(int))
+ if ( os_pipe_write(fd, &reply_options, sizeof(int)) != sizeof(int)
+ || os_pipe_write(fd, &(addr->reply->expand_forbid), sizeof(int))
!= sizeof(int)
- || write(fd, &(addr->reply->once_repeat), sizeof(time_t))
+ || os_pipe_write(fd, &(addr->reply->once_repeat), sizeof(time_t))
!= sizeof(time_t)
|| rda_write_string(fd, addr->reply->to) != 0
|| rda_write_string(fd, addr->reply->cc) != 0
/* Read initial data, including yield and contents of *error */
fd = pfd[pipe_read];
-if (read(fd, filtertype, sizeof(int)) != sizeof(int) ||
- read(fd, &yield, sizeof(int)) != sizeof(int) ||
+if (os_pipe_read(fd, filtertype, sizeof(int)) != sizeof(int) ||
+ os_pipe_read(fd, &yield, sizeof(int)) != sizeof(int) ||
!rda_read_string(fd, error)) goto DISASTER;
/* Read the contents of any syntax error blocks if we have a pointer */
for (;;)
{
int n;
- if (read(fd, &n, sizeof(int)) != sizeof(int)) goto DISASTER;
+ if (os_pipe_read(fd, &n, sizeof(int)) != sizeof(int)) goto DISASTER;
if (n < 0) break;
while (hn < n)
{
int type;
if (!rda_read_string(fd, &s)) goto DISASTER;
if (!s) break;
- if (read(fd, &type, sizeof(type)) != sizeof(type)) goto DISASTER;
+ if (os_pipe_read(fd, &type, sizeof(type)) != sizeof(type)) goto DISASTER;
header_add(type, "%s", s);
}
}
/* Read the values of the $n variables */
-if (read(fd, filter_n, sizeof(filter_n)) != sizeof(filter_n)) goto DISASTER;
+if (os_pipe_read(fd, filter_n, sizeof(filter_n)) != sizeof(filter_n)) goto DISASTER;
/* If the yield is DELIVERED, NOTDELIVERED, FAIL, or FREEZE there may follow
addresses and data to go with them. Keep them in the same order in the
/* Next comes the mode and the flags fields */
- if ( read(fd, &addr->mode, sizeof(addr->mode)) != sizeof(addr->mode)
- || read(fd, &addr->flags, sizeof(addr->flags)) != sizeof(addr->flags)
+ if ( os_pipe_read(fd, &addr->mode, sizeof(addr->mode)) != sizeof(addr->mode)
+ || os_pipe_read(fd, &addr->flags, sizeof(addr->flags)) != sizeof(addr->flags)
|| !rda_read_string(fd, &addr->prop.errors_address)
- || read(fd, &i, sizeof(i)) != sizeof(i)
+ || os_pipe_read(fd, &i, sizeof(i)) != sizeof(i)
)
goto DISASTER;
addr->prop.ignore_error = (i != 0);
/* Then an int containing reply options; zero => no reply data. */
- if (read(fd, &reply_options, sizeof(int)) != sizeof(int)) goto DISASTER;
+ if (os_pipe_read(fd, &reply_options, sizeof(int)) != sizeof(int)) goto DISASTER;
if ((reply_options & REPLY_EXISTS) != 0)
{
addr->reply = store_get(sizeof(reply_item), FALSE);
addr->reply->file_expand = (reply_options & REPLY_EXPAND) != 0;
addr->reply->return_message = (reply_options & REPLY_RETURN) != 0;
- if (read(fd,&(addr->reply->expand_forbid),sizeof(int)) !=
+ if (os_pipe_read(fd,&(addr->reply->expand_forbid),sizeof(int)) !=
sizeof(int) ||
- read(fd,&(addr->reply->once_repeat),sizeof(time_t)) !=
+ os_pipe_read(fd,&(addr->reply->once_repeat),sizeof(time_t)) !=
sizeof(time_t) ||
!rda_read_string(fd, &(addr->reply->to)) ||
!rda_read_string(fd, &(addr->reply->cc)) ||
rc = internal_transport_write_message(tctx, size_limit);
save_errno = errno;
- if ( write(pfd[pipe_write], (void *)&rc, sizeof(BOOL))
- != sizeof(BOOL)
- || write(pfd[pipe_write], (void *)&save_errno, sizeof(int))
- != sizeof(int)
- || write(pfd[pipe_write], (void *)&tctx->addr->more_errno, sizeof(int))
- != sizeof(int)
- || write(pfd[pipe_write], (void *)&tctx->addr->delivery_usec, sizeof(int))
- != sizeof(int)
- )
- rc = FALSE; /* compiler quietening */
+ errno = 0;
+ for (int retries = 10;;)
+ {
+ if ( os_pipe_write(pfd[pipe_write], (void *)&rc, sizeof(BOOL))
+ != sizeof(BOOL)
+ || os_pipe_write(pfd[pipe_write], (void *)&save_errno, sizeof(int))
+ != sizeof(int)
+ || os_pipe_write(pfd[pipe_write], (void *)&tctx->addr->more_errno, sizeof(int))
+ != sizeof(int)
+ || os_pipe_write(pfd[pipe_write], (void *)&tctx->addr->delivery_usec, sizeof(int))
+ != sizeof(int)
+ )
+ if (errno == EINTR && --retries > 0)
+ continue;
+ else
+ rc = FALSE; /* compiler quietening */
+ break;
+ }
exim_underbar_exit(0);
}
save_errno = errno;
if (rc == 0)
{
BOOL ok;
- if (read(pfd[pipe_read], (void *)&ok, sizeof(BOOL)) != sizeof(BOOL))
+ if (os_pipe_read(pfd[pipe_read], (void *)&ok, sizeof(BOOL)) != sizeof(BOOL))
{
DEBUG(D_transport)
debug_printf("pipe read from writing process: %s\n", strerror(errno));
}
else if (!ok)
{
- int dummy = read(pfd[pipe_read], (void *)&save_errno, sizeof(int));
- dummy = read(pfd[pipe_read], (void *)&tctx->addr->more_errno, sizeof(int));
- dummy = read(pfd[pipe_read], (void *)&tctx->addr->delivery_usec, sizeof(int));
+ int dummy = os_pipe_read(pfd[pipe_read], (void *)&save_errno, sizeof(int));
+ dummy = os_pipe_read(pfd[pipe_read], (void *)&tctx->addr->more_errno, sizeof(int));
+ dummy = os_pipe_read(pfd[pipe_read], (void *)&tctx->addr->delivery_usec, sizeof(int));
dummy = dummy; /* compiler quietening */
yield = FALSE;
}