summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed T. Crigler <crigler@users.sourceforge.net>2004-11-03 20:00:18 +0000
committerNed T. Crigler <crigler@users.sourceforge.net>2004-11-03 20:00:18 +0000
commit76b04f84f0b681b9d9d93fd83f392ef0651b8832 (patch)
treef0826bfc5757a34904ac5c8d34fd1f6742ce6359
parent05a6e1678ee2a39a3a962f0f693d1d43cabef60c (diff)
Use non-blocking mode a little better by repeatedly trying to send data out to
the clients until at least one write worked and no error (other than EAGAIN) occurred when writing to any of the clients. This way, complete data loss can be avoided since at least one client must get all of the data. Also make sure that stdin/stdout/stderr point to /dev/null instead of just closing them, to avoid confusion.
-rw-r--r--master.c194
1 files changed, 131 insertions, 63 deletions
diff --git a/master.c b/master.c
index 776812b..1f08a6b 100644
--- a/master.c
+++ b/master.c
@@ -82,6 +82,28 @@ die(int sig)
exit(1);
}
+/* Sets a file descriptor to non-blocking mode. */
+static int
+setnonblocking(int fd)
+{
+ int flags;
+
+#if defined(O_NONBLOCK)
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
+ return -1;
+ return 0;
+#elif defined(FIONBIO)
+ flags = 1;
+ if (ioctl(fd, FIONBIO, &flags) < 0)
+ return -1;
+ return 0;
+#else
+#warning Do not know how to set non-blocking mode.
+ return 0;
+#endif
+}
+
/* Initialize the pty structure. */
static int
init_pty(char **argv)
@@ -113,6 +135,35 @@ init_pty(char **argv)
return 0;
}
+/* Send a signal to the slave side of a pseudo-terminal. */
+static void
+killpty(struct pty *pty, int sig)
+{
+ pid_t pgrp = -1;
+
+#ifdef TIOCSIGNAL
+ if (ioctl(pty->fd, TIOCSIGNAL, sig) >= 0)
+ return;
+#endif
+#ifdef TIOCSIG
+ if (ioctl(pty->fd, TIOCSIG, sig) >= 0)
+ return;
+#endif
+#ifdef TIOCGPGRP
+#ifdef BROKEN_MASTER
+ if (ioctl(pty->slave, TIOCGPGRP, &pgrp) >= 0 && pgrp != -1 &&
+ kill(-pgrp, sig) >= 0)
+ return;
+#endif
+ if (ioctl(pty->fd, TIOCGPGRP, &pgrp) >= 0 && pgrp != -1 &&
+ kill(-pgrp, sig) >= 0)
+ return;
+#endif
+
+ /* Fallback using the child's pid. */
+ kill(-pty->pid, sig);
+}
+
/* Creates a new unix domain socket. */
static int
create_socket(char *name)
@@ -135,6 +186,11 @@ create_socket(char *name)
close(s);
return -1;
}
+ if (setnonblocking(s) < 0)
+ {
+ close(s);
+ return -1;
+ }
/* chmod it to prevent any suprises */
if (chmod(name, 0600) < 0)
{
@@ -144,36 +200,16 @@ create_socket(char *name)
return s;
}
-/* Sets a file descriptor to non-blocking mode. */
-static int
-setnonblocking(int fd)
-{
- int flags;
-
-#if defined(O_NONBLOCK)
- flags = fcntl(fd, F_GETFL);
- if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
- return -1;
- return 0;
-#elif defined(FIONBIO)
- flags = 1;
- if (ioctl(fd, FIONBIO, &flags) < 0)
- return -1;
- return 0;
-#else
-#warning Do not know how to set non-blocking mode.
- return 0;
-#endif
-}
-
/* Process activity on the pty - Input and terminal changes are sent out to
** the attached clients. If the pty goes away, we die. */
static void
-pty_activity()
+pty_activity(int s)
{
unsigned char buf[BUFSIZE];
- struct client *p;
int len;
+ struct client *p;
+ fd_set readfds, writefds;
+ int highest_fd, nclients;
/* Read the pty activity */
len = read(the_pty.fd, buf, sizeof(buf));
@@ -192,12 +228,60 @@ pty_activity()
exit(1);
#endif
- /* Send it out to the clients. */
- for (p = clients; p; p = p->next)
+top:
+ /*
+ ** Wait until at least one client is writable. Also wait on the control
+ ** socket in case a new client tries to connect.
+ */
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_SET(s, &readfds);
+ highest_fd = s;
+ for (p = clients, nclients = 0; p; p = p->next)
+ {
+ if (!p->attached)
+ continue;
+ FD_SET(p->fd, &writefds);
+ if (p->fd > highest_fd)
+ highest_fd = p->fd;
+ nclients++;
+ }
+ if (nclients == 0)
+ return;
+ if (select(highest_fd + 1, &readfds, &writefds, NULL, NULL) < 0)
+ return;
+
+ /* Send the data out to the clients. */
+ for (p = clients, nclients = 0; p; p = p->next)
{
- if (p->attached)
- write(p->fd, buf, len);
+ int written;
+
+ if (!FD_ISSET(p->fd, &writefds))
+ continue;
+
+ written = 0;
+ while (written < len)
+ {
+ int n = write(p->fd, buf + written, len - written);
+
+ if (n > 0)
+ {
+ written += n;
+ continue;
+ }
+ else if (n < 0 && errno == EINTR)
+ continue;
+ else if (n < 0 && errno != EAGAIN)
+ nclients = -1;
+ break;
+ }
+ if (nclients != -1 && written == len)
+ nclients++;
}
+
+ /* Try again if nothing happened. */
+ if (!FD_ISSET(s, &readfds) && nclients == 0)
+ goto top;
}
/* Process activity on the control socket */
@@ -209,8 +293,13 @@ control_activity(int s)
/* Accept the new client and link it in. */
fd = accept(s, NULL, NULL);
- if (fd < 0 || setnonblocking(fd) < 0)
+ if (fd < 0)
+ return;
+ else if (setnonblocking(fd) < 0)
+ {
+ close(fd);
return;
+ }
/* Link it in. */
p = malloc(sizeof(struct client));
@@ -223,35 +312,6 @@ control_activity(int s)
*(p->pprev) = p;
}
-/* Send a signal to the slave side of a pseudo-terminal. */
-static void
-killpty(struct pty *pty, int sig)
-{
- pid_t pgrp = -1;
-
-#ifdef TIOCSIGNAL
- if (ioctl(pty->fd, TIOCSIGNAL, sig) >= 0)
- return;
-#endif
-#ifdef TIOCSIG
- if (ioctl(pty->fd, TIOCSIG, sig) >= 0)
- return;
-#endif
-#ifdef TIOCGPGRP
-#ifdef BROKEN_MASTER
- if (ioctl(pty->slave, TIOCGPGRP, &pgrp) >= 0 && pgrp != -1 &&
- kill(-pgrp, sig) >= 0)
- return;
-#endif
- if (ioctl(pty->fd, TIOCGPGRP, &pgrp) >= 0 && pgrp != -1 &&
- kill(-pgrp, sig) >= 0)
- return;
-#endif
-
- /* Fallback using the child's pid. */
- kill(-pty->pid, sig);
-}
-
/* Process activity from a client. */
static void
client_activity(struct client *p)
@@ -261,6 +321,9 @@ client_activity(struct client *p)
/* Read the activity. */
len = read(p->fd, &pkt, sizeof(struct packet));
+ if (len < 0 && (errno == EAGAIN || errno == EINTR))
+ return;
+
/* Close the client on an error. */
if (len <= 0)
{
@@ -333,6 +396,7 @@ master_process(int s, char **argv)
struct client *p, *next;
fd_set readfds;
int highest_fd;
+ int nullfd;
/* Okay, disassociate ourselves from the original terminal, as we
** don't care what happens to it. */
@@ -355,10 +419,14 @@ master_process(int s, char **argv)
signal(SIGTERM, die);
signal(SIGCHLD, die);
- /* Close the original terminal. We are now a daemon. */
- fclose(stdin);
- fclose(stdout);
- fclose(stderr);
+ /* Make sure stdin/stdout/stderr point to /dev/null. We are now a
+ ** daemon. */
+ nullfd = open("/dev/null", O_RDWR);
+ dup2(nullfd, 0);
+ dup2(nullfd, 1);
+ dup2(nullfd, 2);
+ if (nullfd > 2)
+ close(nullfd);
/* Set a trap to unlink the socket when we die. */
atexit(unlink_socket);
@@ -390,7 +458,7 @@ master_process(int s, char **argv)
}
/* pty activity? */
if (FD_ISSET(the_pty.fd, &readfds))
- pty_activity(&the_pty);
+ pty_activity(s);
/* New client? */
if (FD_ISSET(s, &readfds))
control_activity(s);