So all the series looks quite good, and I must confess I'm impatient to
merge it so that we turn the page of the wrapper, and also because being
able to use nbproc in foreground during development can be nice.

But I have two comments first :

On Mon, May 29, 2017 at 05:42:09PM +0200, William Lallemand wrote:
> +void mworker_pipe_handler(int fd)
> +{
> +     char c;
> +
> +     if (read(fd, &c, 1) > -1) {
> +             fd_delete(fd);
> +             deinit();
> +             exit(EXIT_FAILURE);
> +     } else {
> +             /* should never happened */
> +             fd_delete(fd);
> +     }
> +
> +     return;
> +}

The test above is dangerous, it assumes that there's zero valid reason
for read() to return -1 but in fact there are eventhough they are corner
cases.

First, a good rule of thumb to consider is that a poller is not rocket
science and that it always relies on the callee to check for the reality
of event readiness. We have one well-known example in Linux in association
with UDP datagram reception. It's possible for poll() to say "there's
something to read" and when read()/recv() try to read, the packet checksum
is computed on the fly during the user_copy(), found to be bad, the packet
is destroyed and read() returns EAGAIN. Of course it's not what you have
above, but it's to say that whenever you have a poller, EAGAIN must be
tested to be completely safe.

The second (more likely) case is a signal causing read() to return -1 EINTR.
And in fact in the master worker, it could theorically happen like this :

    killall -SOMESIG haproxy

The master, not intercepting this signal, would die, closing the pipe.
The worker would be woken up on the detection of this closure, and while
trying to perform the read() would get the signal in turn, causing the
read() to return EINTR and to stop polling on this fd instead of exiting.

A much safer way to deal with this situation is to loop on EINTR and
return without doing anything on EAGAIN, approximately like this :

    while (read(fd) == -1) {
       if (errno == EINTR)
          continue;
       if (errno == EAGAIN) {
          fd_cant_recv(fd);
          return;
       }
       /* otherwise probably die ? */
       break;
    }

    deinit();
    exit();

> @@ -2478,6 +2508,28 @@ int main(int argc, char **argv)
>                               exit(0);
>               }
>  
> +             if (global.mode & MODE_MWORKER) {
> +                     if ((getenv(REEXEC_FLAG) == NULL)) {
> +                             char *msg = NULL;
> +                             /* master pipe to ensure the master is still 
> alive  */
> +                             ret = pipe(mworker_pipe);
> +                             if (ret < 0) {
> +                                     Warning("[%s.main()] Cannot create 
> master pipe.\n", argv[0]);
> +                             } else {
> +                                     memprintf(&msg, "%d", mworker_pipe[0]);
> +                                     setenv("MWORKER_PIPE_RD", msg, 1);
> +                                     memprintf(&msg, "%d", mworker_pipe[1]);
> +                                     setenv("MWORKER_PIPE_WR", msg, 1);

Regarding the environment variable names, it's preferable to prepend
"HAPROXY" in front of them as we've been doing for all other ones to
avoid namespace conflicts with anything used in other environments (I've
ween places where you had to run "env|grep" to find your variable). I've
seen another one called "WAIT_ONLY" in one of the first patches and
which should equally be renamed.

Otherwise the series looks quite good, it would be nice to get some
feedback especially from systemd hostages^Wusers (my comments above
will not affect their experience unless they're really unlucky).

Thanks!
Willy

Reply via email to