X-Git-Url: https://ruderich.org/simon/gitweb/?a=blobdiff_plain;f=src%2Fwall-notify.c;h=13e7829926f04708708e084d1dd147e895f864ff;hb=637459052b01632ce2c4e9e36c93509f65fb4788;hp=c502bce0862e1c4a2379116b2cf3a6150be6361e;hpb=aaaaf11639fafcd1c926cfcccd282182551f3382;p=wall-notify%2Fwall-notify.git diff --git a/src/wall-notify.c b/src/wall-notify.c index c502bce..13e7829 100644 --- a/src/wall-notify.c +++ b/src/wall-notify.c @@ -29,9 +29,11 @@ #include #include #include +#include #include #include #include +#include #include #ifdef USE_UTEMPTER @@ -59,9 +61,6 @@ static void setup_signals(void) { sigaction(SIGQUIT, &action, NULL); sigaction(SIGUSR1, &action, NULL); sigaction(SIGUSR2, &action, NULL); - - /* Collect zombies automatically without having to call waitpid(2). */ - signal(SIGCHLD, SIG_IGN); } static int open_tty(void) { @@ -161,6 +160,31 @@ static int logout(int ptm) { #endif } +static int wait_for_write(int fd, int timeout) { + fd_set rfds; + struct timeval tv; + int result; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + tv.tv_sec = timeout; + tv.tv_usec = 0; + + result = select(fd + 1, &rfds, NULL, NULL, &tv); + if (result < 0) { + perror("select"); + return 0; + } + if (result == 0) { + /* Timeout. */ + return 0; + } + + /* Got more data to read. */ + return 1; +} + static void pass_buffer_to_program(const char *buffer, size_t length, char **argv) { int fds[2]; FILE *fh; @@ -199,9 +223,21 @@ static void pass_buffer_to_program(const char *buffer, size_t length, char **arg } close(fds[0]); - execvp(argv[0], argv); - perror("execvp"); - exit(EXIT_FAILURE); + /* Double fork so `wall-notify` doesn't have to wait for the + * notification process to terminate. We can't just use + * signal(SIGCHLD, SIG_IGN); because utempter on at least FreeBSD + * doesn't work if SIGCHLD is ignored. */ + pid = fork(); + if (pid < 0) { + perror("fork"); + exit(EXIT_FAILURE); + } else if (pid == 0) { + execvp(argv[0], argv); + perror("execvp"); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); } /* father */ @@ -213,13 +249,33 @@ static void pass_buffer_to_program(const char *buffer, size_t length, char **arg out: close(fds[0]); /* read side */ fclose(fh); + + if (waitpid(pid, NULL, 0) < 0) { + perror("waitpid"); + } } static void handle_wall(int fd, char **argv) { char buffer[4096]; ssize_t r; + assert(SSIZE_MAX <= SIZE_MAX); while ((r = read(fd, buffer, sizeof(buffer))) > 0) { - assert(SSIZE_MAX <= SIZE_MAX); + size_t space; + ssize_t r2; + + /* To prevent partial messages (sometimes it takes multiple reads to + * get the complete message) wait for a short time to get the rest of + * the message. */ + space = sizeof(buffer) - (size_t)r; + while (space > 0 && wait_for_write(fd, 1 /* second */)) { + r2 = read(fd, buffer + r, space); + if (r2 <= 0) { + break; + } + r += r2; + space -= (size_t)r2; + } + pass_buffer_to_program(buffer, (size_t)r, argv); } }