kern/64313: FreeBSD (OpenBSD) pthread implicit set/unset O_NONBLOCK
flag
Lars Köller
lkoeller at koellers.net
Mon Mar 15 12:40:22 PST 2004
>Number: 64313
>Category: kern
>Synopsis: FreeBSD (OpenBSD) pthread implicit set/unset O_NONBLOCK flag
>Confidential: no
>Severity: critical
>Priority: high
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Mon Mar 15 12:40:21 PST 2004
>Closed-Date:
>Last-Modified:
>Originator: Lars Köller
>Release: FreeBSD 4.9-RELEASE-p1 i386
>Organization:
Computing Center, University of Bielefeld, Germany
>Environment:
System: FreeBSD odie.koellers.net 4.9-RELEASE-p1 FreeBSD 4.9-RELEASE-p1 #3: Sun Jan 25 19:07:53 CET 2004 root at odie.koellers.net:/usr/src/sys/compile/ODIE i386
>Description:
/*
* Work around a bug in OpenBSD & FreeBSD userspace pthreads
* implementations.
*
* The pthreads implementation under the hood sets O_NONBLOCK
* implicitly on all fds. This setting is not visible to the user
* application but is relied upon by the pthreads library to prevent
* blocking syscalls in one thread from halting all threads in the
* process. When a process exit()s or exec()s, the implicit
* O_NONBLOCK flags are removed from all fds, EVEN THOSE IT INHERITED.
* If another process is still using the inherited fds, there will
* soon be trouble.
*
* apcupsd is bitten by this issue after fork()ing a child process to
* run apccontrol.
*
* select() is conveniently immune to the O_NONBLOCK issue so we use
* that to make sure the following read() will not block.
*/
>How-To-Repeat:
Happens sometimes in the threaded apcupsd when it calls the apccontrol script.
>Fix:
Don't know, I only notice the problem in the threaded apcups port which
I'm the maintainer for.
I receive the attached patch for the apcupsd port from
Gary Bajaj <b04 at interbaun.com>
Originally it was written by Adam Kropelin:
###################################################
##### Attached to make the problem more clear #####
##### See description below #####
###################################################
--- ./src/apcnis.c Fri Jul 18 05:32:19 2003
+++ ./apcupsd-3.10.11-debug3/src/apcnis.c Fri Feb 6 21:19:14 2004
@@ -197,7 +197,6 @@
int newsockfd, sockfd, childpid;
struct sockaddr_in cli_addr; /* client's address */
struct sockaddr_in serv_addr; /* our address */
- socklen_t clilen;
int tlog;
int turnon = 1;
struct s_arg *arg;
@@ -269,11 +268,7 @@
/*
* Wait for a connection from a client process.
*/
- clilen = sizeof(cli_addr);
- for (tlog=0; (newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen)) < 0; tlog -= 5*60 ) {
- if (errno == EINTR) {
- continue;
- }
+ for (tlog=0; (newsockfd = net_accept(sockfd, &cli_addr)) < 0; tlog -= 5*60 ) {
if (tlog <= 0) {
tlog = 60*60;
log_event(ups, LOG_ERR, "apcserver: accept error. ERR=%s",
--- ./src/lib/apclibnis.c Sat Aug 3 18:49:45 2002
+++ ./apcupsd-3.10.11-debug3/src/lib/apclibnis.c
Fri Feb 6 21:38:58 2004
@@ -71,12 +71,50 @@
static int read_nbytes(int fd, char *ptr, int nbytes)
{
- int nleft, nread;
-
+ int nleft, nread, rc;
+
+#if defined HAVE_PTHREADS && (defined HAVE_OPENBSD_OS || defined HAVE_FREEBSD_OS)
+ fd_set fds;
+#endif
+
nleft = nbytes;
- errno = 0;
+
while (nleft > 0) {
+
do {
+
+#if defined HAVE_PTHREADS && (defined HAVE_OPENBSD_OS || defined HAVE_FREEBSD_OS)
+ /*
+ * Work around a bug in OpenBSD & FreeBSD userspace pthreads
+ * implementations.
+ *
+ * The pthreads implementation under the hood sets O_NONBLOCK
+ * implicitly on all fds. This setting is not visible to the user
+ * application but is relied upon by the pthreads library to prevent
+ * blocking syscalls in one thread from halting all threads in the
+ * process. When a process exit()s or exec()s, the implicit
+ * O_NONBLOCK flags are removed from all fds, EVEN THOSE IT INHERITED.
+ * If another process is still using the inherited fds, there will
+ * soon be trouble.
+ *
+ * apcupsd is bitten by this issue after fork()ing a child process to
+ * run apccontrol.
+ *
+ * select() is conveniently immune to the O_NONBLOCK issue so we use
+ * that to make sure the following read() will not block.
+ */
+ do {
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ rc = select(fd+1, &fds, NULL, NULL, NULL);
+ } while (rc == -1 && (errno == EINTR || errno == EAGAIN));
+ if (rc < 0)
+ {
+ net_errno = errno;
+ return(-1); /* error */
+ }
+#endif
+
nread = read(fd, ptr, nleft);
} while (nread == -1 && (errno == EINTR || errno == EAGAIN));
if (nread <= 0) {
@@ -100,6 +138,15 @@
nleft = nbytes;
while (nleft > 0) {
+#if defined HAVE_PTHREADS && (defined HAVE_OPENBSD_OS || defined HAVE_FREEBSD_OS)
+ /*
+ * Work around a bug in OpenBSD & FreeBSD userspace pthreads
+ * implementations. Rationale is the same as described above.
+ * This seemingly-pointless fcntl() call causes the pthreads
+ * library to reapply the O_NONBLOCK flag appropriately.
+ */
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL));
+#endif
nwritten = write(fd, ptr, nleft);
if (nwritten <= 0) {
net_errno = errno;
@@ -225,6 +272,13 @@
return -1;
}
/* connect to server */
+#if defined HAVE_PTHREADS && (defined HAVE_OPENBSD_OS || defined HAVE_FREEBSD_OS)
+ /*
+ * Work around a bug in OpenBSD & FreeBSD userspace pthreads
+ * implementations. Rationale is the same as described above.
+ */
+ fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL));
+#endif
if (connect(sockfd, (struct sockaddr *) &tcp_serv_addr, sizeof(tcp_serv_addr)) < 0) {
sprintf(net_errbuf, "tcp_open: cannot connect to server %s on port %d.\n\
ERR=%s\n", host, port, strerror(errno));
@@ -243,6 +297,50 @@
close(sockfd);
}
+/*
+ * Accept a TCP connection.
+ * Returns -1 on error.
+ * Returns file descriptor of new connection otherwise.
+ */
+int net_accept(int fd, struct sockaddr_in *cli_addr)
+{
+ socklen_t clilen = sizeof(*cli_addr);
+ int newfd, rc;
+
+#if defined HAVE_PTHREADS && (defined HAVE_OPENBSD_OS || defined HAVE_FREEBSD_OS)
+ fd_set fds;
+#endif
+
+ do {
+
+#if defined HAVE_PTHREADS && (defined HAVE_OPENBSD_OS || defined HAVE_FREEBSD_OS)
+ /*
+ * Work around a bug in OpenBSD & FreeBSD userspace pthreads
+ * implementations. Rationale is the same as described above.
+ */
+ do {
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ rc = select(fd+1, &fds, NULL, NULL, NULL);
+ } while (rc == -1 && (errno == EINTR || errno == EAGAIN));
+ if (rc < 0)
+ {
+ net_errno = errno;
+ return(-1); /* error */
+ }
+#endif
+
+ newfd = accept(fd, (struct sockaddr*)cli_addr, &clilen);
+ } while (newfd == -1 && (errno == EINTR || errno == EAGAIN));
+
+ if (newfd < 0)
+ {
+ net_errno = errno;
+ return(-1); /* error */
+ }
+
+ return newfd;
+}
int upserror, syserrno;
--- ./include/apc_nis.h Tue May 28 09:34:24 2002
+++ ./apcupsd-3.10.11-debug3/include/apc_nis.h
Fri Feb 6 21:19:14 2004
@@ -40,4 +40,7 @@
/* Close the network connection */
void net_close(int sockfd);
+/* Wait for and accept a new TCP connection */
+int net_accept(int fd, struct sockaddr_in *cli_addr);
+
extern int upserror, syserrno;
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list