Hi,
[email protected] 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);
}