[libvirt] [PATCH 1/2] Add virFork() function to utils.

Laine Stump laine at laine.org
Thu Feb 18 15:06:02 UTC 2010


On 02/18/2010 07:50 AM, Daniel P. Berrange wrote:
> On Wed, Feb 17, 2010 at 02:29:27PM -0500, Laine Stump wrote:
>    
>> virFork() contains bookkeeping that must be done any time a process
>> forks. Currently this includes:
>>
>> 1) Call virLogLock() prior to fork() and virLogUnlock() just after,
>>     to avoid a deadlock if some other thread happens to hold that lock
>>     during the fork.
>>
>> 2) Reset the logging hooks and send all child process log messages to
>>     stderr.
>>
>> 3) Block all signals prior to fork(), then either a) reset the signal
>>     mask for the parent process, or b) clear the signal mask for the
>>     child process.
>>
>> util.c, util.h: add virFork() function, based on what is currently
>>                  done in __virExec().
>> ---
>>   src/util/util.c |  115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   src/util/util.h |    1 +
>>   2 files changed, 116 insertions(+), 0 deletions(-)
>>
>> diff --git a/src/util/util.c b/src/util/util.c
>> index cdab300..f508f6b 100644
>> --- a/src/util/util.c
>> +++ b/src/util/util.c
>> @@ -296,6 +296,121 @@ static int virClearCapabilities(void)
>>   }
>>   #endif
>>
>> +
>> +/* virFork() - fork a new process while avoiding various race/deadlock conditions
>> +
>> +   @pid - a pointer to a pid_t that will receive the return value from
>> +          fork()
>> +
>> +   on return from virFork(), if *pid<  0, the fork failed and there is
>> +   no new process. Otherwise, just like fork(), if *pid == 0, it is the
>> +   child process returning, and if *pid>  0, it is the parent.
>> +
>> +   Even if *pid>= 0, if the return value from virFork() is<  0, it
>> +   indicates a failure that occurred in the parent or child process
>> +   after the fork. In this case, the child process should call
>> +   _exit(1) after doing any additional error reporting.
>> +
>> + */
>> +int virFork(pid_t *pid) {
>> +    sigset_t oldmask, newmask;
>> +    struct sigaction sig_action;
>> +    int saved_errno, ret = -1;
>> +
>> +    *pid = -1;
>> +
>> +    /*
>> +     * Need to block signals now, so that child process can safely
>> +     * kill off caller's signal handlers without a race.
>> +     */
>> +    sigfillset(&newmask);
>> +    if (pthread_sigmask(SIG_SETMASK,&newmask,&oldmask) != 0) {
>> +        saved_errno = errno;
>> +        virReportSystemError(errno,
>> +                             "%s", _("cannot block signals"));
>> +        goto cleanup;
>> +    }
>> +
>> +    /* Ensure we hold the logging lock, to protect child processes
>> +     * from deadlocking on another thread's inherited mutex state */
>> +    virLogLock();
>> +
>> +    *pid = fork();
>> +    saved_errno = errno; /* save for caller */
>> +
>> +    /* Unlock for both parent and child process */
>> +    virLogUnlock();
>> +
>> +    if (*pid<  0) {
>> +        virReportSystemError(saved_errno,
>> +                             "%s", _("cannot fork child process"));
>> +        goto cleanup;
>> +    }
>>      
> Tiny bug here, in that we forget to unblock the parent's signals in
> this error path.
>    

Yow! That actually seems potentially catastrophic to me - if fork() ever 
fails, libvirtd would ignore all signals until it was kill -9'd.

My only defense is that I was replicating existing behavior ;-)

Fixed patch coming up.




More information about the libvir-list mailing list