Search code examples
cprocesssignalsterminatekill-process

How can I terminate a process and verify its not running anymore in C?


I want to fully terminate a process on Linux C89.

The flow is: check if its already dead, if not, let it die peacefully using sigterm and wait 10 seconds till it dies. If its still alive - SIGKILL it.

int TerminateProcessIMP(pid_t process_to_kill)
{    
    assert(process_to_kill);
    
    /*---------------------------------------------------------*/
    /*  check if process is already does not exist */
    if (!IsProcessAliveIMP(process_to_kill))
    {
        return (SUCCESS);
    }
    /*---------------------------------------------------------*/
    /*  terminate process_to_kill   */
    if (kill(process_to_kill, SIGTERM))
    {
        fprintf(stderr, "%s\n", strerror(errno));
    }
    
    if (!IsProcessAliveIMP(process_to_kill))
    {
        return (SUCCESS);
    }
    
    /*---------------------------------------------------------*/
    /*  if its still alive, SIGKILL it */
    if (kill(process_to_kill, SIGKILL))
    {
        fprintf(stderr, "%s\n", strerror(errno));
    }
    
    if (!IsProcessAliveIMP(process_to_kill))
    {
        return (SUCCESS);
    }
    /*---------------------------------------------------------*/

    return (FAILURE);
}
/******************************************************************************/
int IsProcessAliveIMP(pid_t process_to_check)
{
    time_t start_time = 0;
    time_t end_time = 0;
    time_t time_to_wait = 10; /* in seconds */
    
    assert(process_to_check);
    
    start_time = time(0);
    end_time = start_time + time_to_wait;
    
    /*  give it time to be terminated because maybe it frees memory meanwhile */
    while (0 != kill(process_to_check, 0) && time(0) < end_time)
    {}
    
    /*  check if it still exists */
    if (0 == kill(process_to_check, 0))
    {
        return (0);
    }
    
    /* the process is still alive */
    return (1);
}

What do you think?

Right now, it does not work and does not terminate the process.

It tries to terminate the process but fails to do that. I can't figure out why.

Thanks.


Solution

  • You're doing unnecessary things.

    To check if a process exists:

    kill(pid, 0);
    

    If the return value is 0, then the process exists, if -1 then you have to check errno. In case of ESRCH:

    The target process or process group does not exist. Note that an existing process might be a zombie, a process that has terminated execution, but has not yet been wait(2)ed for.


    To terminate a process:

    kill(pid, SIGTERM); // signal can be blocked, handled or ignored
    

    or

    kill(pid, SIGKILL); // signal cannot be blocked, handled or ignored
    

    After a kill, you have to wait for the process via:

    waitpid(pid, &status, 0);
    

    If status is not NULL, wait() and waitpid() store status information in the int to which it points. This integer can be inspected with the macros, described in the man page.


    See

    man kill
    man waitpid


    Example:

    int TerminateProcessIMP(pid_t pid)
    {
        //check if process exists
        int res = kill(pid, 0);
        
        if ((res == -1) && (errno != ESRCH)) {
            //error: either EINVAL or EPERM
            //ESRCH: an existing process might be a zombie
            return -1;
        }
        
        if (res == 0) { //process exists
            
            //ask politely to terminate
            
            if (kill(pid, SIGTERM) == -1) {
                //error: unable to send a signal to the process
                return -1;
            }
            
            //let us see if the child complied to our request
            res = waitpid(pid, NULL, WNOHANG | WUNTRACED | WCONTINUED);
            
            if (res == -1) {
                //most likely not our child (errno == ECHILD)
                //but the process could follow our request and still terminate
                //if you want to be sure goto SIGKILL below
                //or return
            } else if (res == 0) {
               //our child, but at this point the child has not terminated yet 
               //(maybe it will never)
               //either continue to wait or goto SIGKILL below
            } else {
               //child complied to our request and terminated in time
               //res contains the id of the child (res == pid)
               return res;
            }
    
            //--- or ---
    
            /* do {
                res = waitpid(pid, NULL, WNOHANG | WUNTRACED | WCONTINUED);
                //because of WNOHANG
                sleep(1);
                //your timeout method goes here
            } while (!res);
    
            if (res == -1) { //same as above }
            if (res  >  0) { return res;     } */
            
        }
        
        //at this point, the process either does not exists (maybe zombie), 
        //is not our child or refused our request (SIGTERM)
        
        //send a SIGKILL signal to the process
        kill(pid, SIGKILL);
        
        //wait for the process to terminate
        res = waitpid(pid, NULL, 0);
        
        /*if (res == -1) {
            //not our child, or process does not exists
        }*/
        
        /*if (res > 0) {
            //child successfully terminated
        }*/
        
        return res;
    }