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);
}

Reply via email to