[PATCH RESEND 1/5] virCommand: Actually acquire pidfile instead of just writing it

Marc-André Lureau marcandre.lureau at gmail.com
Mon Mar 23 16:51:50 UTC 2020


Hi


On Mon, Mar 23, 2020 at 5:14 PM Michal Privoznik <mprivozn at redhat.com> wrote:
>
> Our virCommand module allows us to set a pidfile for commands we
> want to spawn. The caller constructs the string of pidfile path
> and then uses virCommandSetPidFile() to tell the module to write
> the pidfile once the command is ran. This usually works, but has
> two flaws:
>
> 1) the child process does not hold the pidfile open & locked.
> Therefore, the caller (or anybody else) can't use our fancy
> virPidFileForceCleanupPath() function to kill the command
> afterwards. Also, for everybody else on the system it's
> needlessly harder to check if the pid from the pidfile is still
> alive or not.
>
> 2) if the caller ever makes a mistake and passes the same pidfile
> path for two different commands, the start of the second command
> will overwrite the pidfile even though the first command might
> still be running.
>
> Signed-off-by: Michal Privoznik <mprivozn at redhat.com>

Reviewed-by: Marc-André Lureau <marcandre.lureau at redhat.com>


> ---
>  docs/internals/command.html.in |  4 ++-
>  src/util/vircommand.c          | 56 +++++++++++++++++++++++++++++-----
>  tests/commanddata/test4.log    |  1 +
>  3 files changed, 52 insertions(+), 9 deletions(-)
>
> diff --git a/docs/internals/command.html.in b/docs/internals/command.html.in
> index 8a9061e70f..823d89cc71 100644
> --- a/docs/internals/command.html.in
> +++ b/docs/internals/command.html.in
> @@ -226,7 +226,9 @@ virCommandSetPidFile(cmd, "/var/run/dnsmasq.pid");
>
>      <p>
>        This PID file is guaranteed to be written before
> -      the intermediate process exits.
> +      the intermediate process exits. Moreover, the daemonized
> +      process will inherit the FD of the opened and locked PID
> +      file.
>      </p>
>
>      <h3><a id="privs">Reducing command privileges</a></h3>
> diff --git a/src/util/vircommand.c b/src/util/vircommand.c
> index 9ffa0cf49b..77078d09fb 100644
> --- a/src/util/vircommand.c
> +++ b/src/util/vircommand.c
> @@ -612,6 +612,7 @@ virExec(virCommandPtr cmd)
>      int null = -1;
>      int pipeout[2] = {-1, -1};
>      int pipeerr[2] = {-1, -1};
> +    int pipesync[2] = {-1, -1};
>      int childin = cmd->infd;
>      int childout = -1;
>      int childerr = -1;
> @@ -745,9 +746,15 @@ virExec(virCommandPtr cmd)
>      /* Initialize full logging for a while */
>      virLogSetFromEnv();
>
> +    if (cmd->pidfile &&
> +        virPipe(pipesync) < 0)
> +        goto fork_error;
> +
>      /* Daemonize as late as possible, so the parent process can detect
>       * the above errors with wait* */
>      if (cmd->flags & VIR_EXEC_DAEMON) {
> +        char c;
> +
>          if (setsid() < 0) {
>              virReportSystemError(errno,
>                                   "%s", _("cannot become session leader"));
> @@ -768,12 +775,13 @@ virExec(virCommandPtr cmd)
>          }
>
>          if (pid > 0) {
> -            if (cmd->pidfile && (virPidFileWritePath(cmd->pidfile, pid) < 0)) {
> -                if (virProcessKillPainfully(pid, true) >= 0)
> -                    virReportSystemError(errno,
> -                                         _("could not write pidfile %s for %d"),
> -                                         cmd->pidfile, pid);
> -                goto fork_error;
> +            /* The parent expect us to have written the pid file before
> +             * exiting. Wait here for the child to write it and signal us. */
> +            if (cmd->pidfile &&
> +                saferead(pipesync[0], &c, sizeof(c)) != sizeof(c)) {
> +                virReportSystemError(errno, "%s",
> +                                     _("Unable to wait for child process"));
> +                _exit(EXIT_FAILURE);
>              }
>              _exit(EXIT_SUCCESS);
>          }
> @@ -788,6 +796,37 @@ virExec(virCommandPtr cmd)
>      if (cmd->setMaxCore &&
>          virProcessSetMaxCoreSize(0, cmd->maxCore) < 0)
>          goto fork_error;
> +    if (cmd->pidfile) {
> +        VIR_AUTOCLOSE pidfilefd = -1;
> +        int newpidfilefd = -1;
> +        char c;
> +
> +        pidfilefd = virPidFileAcquirePath(cmd->pidfile, false, getpid());
> +        if (pidfilefd < 0)
> +            goto fork_error;
> +        if (virSetInherit(pidfilefd, true) < 0) {
> +            virReportSystemError(errno, "%s",
> +                                 _("Cannot disable close-on-exec flag"));
> +            goto fork_error;
> +        }
> +
> +        c = '1';
> +        if (safewrite(pipesync[1], &c, sizeof(c)) != sizeof(c)) {
> +            virReportSystemError(errno, "%s", _("Unable to notify child process"));
> +            goto fork_error;
> +        }
> +        VIR_FORCE_CLOSE(pipesync[0]);
> +        VIR_FORCE_CLOSE(pipesync[1]);
> +
> +        /* This is here only to move the pidfilefd
> +         * to the lowest possible number. */
> +        if ((newpidfilefd = dup(pidfilefd)) < 0) {
> +            virReportSystemError(errno, "%s", _("Unable to dup FD"));
> +            goto fork_error;
> +        }
> +
> +        /* newpidfilefd is intentionally leaked. */
> +    }
>
>      if (cmd->hook) {
>          VIR_DEBUG("Run hook %p %p", cmd->hook, cmd->opaque);
> @@ -1080,8 +1119,9 @@ virCommandPassFDGetFDIndex(virCommandPtr cmd, int fd)
>   * @cmd: the command to modify
>   * @pidfile: filename to use
>   *
> - * Save the child PID in a pidfile.  The pidfile will be populated
> - * before the exec of the child.
> + * Save the child PID in a pidfile. The pidfile will be populated before the
> + * exec of the child and the child will inherit opened and locked FD to the
> + * pidfile.
>   */
>  void
>  virCommandSetPidFile(virCommandPtr cmd, const char *pidfile)
> diff --git a/tests/commanddata/test4.log b/tests/commanddata/test4.log
> index f170be204e..5820f28307 100644
> --- a/tests/commanddata/test4.log
> +++ b/tests/commanddata/test4.log
> @@ -9,6 +9,7 @@ ENV:USER=test
>  FD:0
>  FD:1
>  FD:2
> +FD:3
>  DAEMON:yes
>  CWD:/
>  UMASK:0022
> --
> 2.24.1
>


--
Marc-André Lureau





More information about the libvir-list mailing list