Hi, yelni...@tutamail.com writes:
> After spending a bit of time investigating I am a bit lost. I haven't > found anything that is for me obviously wrong, but have not yet fully > understood how things fit together. > > The only thing I have so far is that the flags of the wrong socket are > the same as the original socket after the "select" as if something is > not being updated correctly/in time. But why is always reproducible > then? I am struggling to understand what could be different between > the first and second "accept". Keep in mind that on the first time, the socket is passed to the newly-created guix-daemon process by shepherd; the second time, guix-daemon is already running and calling accept(2) by itself. Anyway, I tried to simulate socket activation with the two attached programs: the first one creates an O_NONBLOCK socket, selects on it, removes the O_NONBLOCK flag, then execs the second program, which accepts on the socket. It works like a charm. So I wonder if we’re missing something here specific to guix-daemon. Ludo’.
#define _GNU_SOURCE 1 #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/select.h> #include <string.h> #include <fcntl.h> #include <error.h> static void terminate (const char *message) { perror (message); exit (EXIT_FAILURE); } static void child (const struct sockaddr *addr, socklen_t len) { int sock = socket (PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); if (sock < 0) terminate ("socket"); int err = connect (sock, addr, len); if (err) terminate ("connect"); static const char buf[] = "hello, world!\n"; ssize_t written = write (sock, buf, sizeof buf); if (written < 0) terminate ("write"); exit (EXIT_SUCCESS); } int main () { int err; int sock = socket (PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); if (sock < 0) terminate ("socket"); struct sockaddr_un addr; addr.sun_family = AF_LOCAL; strcpy (addr.sun_path, "/tmp/sock"); unlink ("/tmp/sock"); err = bind (sock, &addr, sizeof addr); if (err) terminate ("bind"); err = listen (sock, 1); if (err) terminate ("listen"); if (fork () == 0) child ((struct sockaddr *) &addr, sizeof addr); fd_set read_fds; FD_ZERO (&read_fds); FD_SET (sock, &read_fds); struct timeval timeout = { .tv_sec = 100, .tv_usec = 0 }; err = select (FD_SETSIZE, &read_fds, NULL, NULL, &timeout); if (err < 0) terminate ("select"); int flags = fcntl (sock, F_GETFL); err = fcntl (sock, F_SETFL, flags & ~O_NONBLOCK); if (err) terminate ("F_SETFL"); if (fork () == 0) { char buf[100]; sprintf (buf, "%d", sock); err = execl ("./accept", "./accept", buf, NULL); if (err) terminate ("exec"); } return EXIT_SUCCESS; }
#define _GNU_SOURCE 1 #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/select.h> #include <string.h> #include <fcntl.h> #include <error.h> static void terminate (const char *message) { perror (message); exit (EXIT_FAILURE); } int main (int argc, char *argv[]) { int err; int sock = atoi (argv[1]); printf ("accepting on fd %d (flags: %d)\n", sock, fcntl (sock, F_GETFL) & (O_NONBLOCK | O_RDONLY | O_CLOEXEC)); struct sockaddr_storage peer; socklen_t len; int connection = accept (sock, (struct sockaddr *) &peer, &len); if (connection < 0) terminate ("accept"); char buf[1024]; err = read (connection, buf, sizeof buf); if (err < 0) terminate ("read"); printf ("read '%s'\n", buf); }