You Are Here Home > Linux Programming

Linux Programming

Popen+ a Bidirectional Popen Implementation With Ability to Access PID and Kill/Terminate Processes

On Github:
https://github.com/Codingrecipes/Popen+

This is a little popen implementation which will make it easier to kill processes and will give you access to the PID of the process, I don’t know why they didn’t do this in popen but I’m sure they had good reasons because those guys are way too smart…

popen_plus.h:

/*
 ** Author: Hamid Alipour http://codingrecipes.com http://twitter.com/code_head
 ** SQLite style license:
 ** 
 ** 2001 September 15
 **
 ** The author disclaims copyright to this source code.  In place of
 ** a legal notice, here is a blessing:
 **
 **    May you do good and not evil.
 **    May you find forgiveness for yourself and forgive others.
 **    May you share freely, never taking more than you give.
 **/
 
#ifndef POPEN_PLUS_H
#define POPEN_PLUS_H
 
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <paths.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
 
#define READ 0
#define WRITE 1
 
struct popen_plus_process {
    pthread_mutex_t mutex;
    pid_t pid;
    FILE *read_fp;
    FILE *write_fp;
};
 
struct popen_plus_process *popen_plus(const char *command);
int popen_plus_close(struct popen_plus_process *process);
int popen_plus_kill(struct popen_plus_process *process);
int popen_plus_kill_by_id(int process_id);
int popen_plus_terminate(struct popen_plus_process *process);
int popen_plus_terminate_with_id(int process_id);
 
#endif

popen_plus.c:

/*
 ** Author: Hamid Alipour http://codingrecipes.com http://twitter.com/code_head
 ** SQLite style license:
 ** 
 ** 2001 September 15
 **
 ** The author disclaims copyright to this source code.  In place of
 ** a legal notice, here is a blessing:
 **
 **    May you do good and not evil.
 **    May you find forgiveness for yourself and forgive others.
 **    May you share freely, never taking more than you give.
 **/
 
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <paths.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include "popen_plus.h"
 
struct popen_plus_process *popen_plus(const char *command)
{
    int inpipe[2];
    int outpipe[2];
    char *argv[4];
    struct popen_plus_process *process = malloc(sizeof(struct popen_plus_process));
 
    if (!process)
        goto error_out;
 
    if (pipe(inpipe) != 0)
        goto clean_process_out;
 
    if (pipe(outpipe) != 0)
        goto clean_inpipe_out;
 
    process->read_fp = fdopen(outpipe[READ], "r");
    if (!process->read_fp)
        goto clean_outpipe_out;
 
    process->write_fp = fdopen(inpipe[WRITE], "w");
    if (!process->write_fp)
        goto clean_read_fp_out;
 
    if (pthread_mutex_init(&process->mutex, NULL) != 0)
        goto clean_write_fp_out;
 
    process->pid = fork();
    if (process->pid == -1)
        goto clean_mutex_out;
 
    if (process->pid == 0) {
        close(outpipe[READ]);
        close(inpipe[WRITE]);
 
        if (inpipe[READ] != STDIN_FILENO) {
            dup2(inpipe[READ], STDIN_FILENO);
            close(inpipe[READ]);
        }
 
        if (outpipe[WRITE] != STDOUT_FILENO) {
            dup2(outpipe[WRITE], STDOUT_FILENO);
            close(outpipe[WRITE]);
        }
 
        argv[0] = "sh";
        argv[1] = "-c";
        argv[2] = (char *) command;
        argv[3] = NULL;
 
        execv(_PATH_BSHELL, argv);
        exit(127);
    }
 
    close(outpipe[WRITE]);
    close(inpipe[READ]);
 
    return process;
 
clean_mutex_out:
    pthread_mutex_destroy(&process->mutex);
 
clean_write_fp_out:
    fclose(process->write_fp);
 
clean_read_fp_out:
    fclose(process->read_fp);
 
clean_outpipe_out:
    close(outpipe[READ]);
    close(outpipe[WRITE]);
 
clean_inpipe_out:
    close(inpipe[READ]);
    close(inpipe[WRITE]);
 
clean_process_out:
    free(process);
 
error_out:
    return NULL;
}
 
int popen_plus_close(struct popen_plus_process *process)
{
    int pstat;
    pid_t pid;
 
    /**
     * If someone else destrys this mutex, then this call will fail and we know
     * that another thread already cleaned up the process so we can safely return
     * and since we are destroying this mutex bellow then we don't need to unlock
     * it...
     */
    if (pthread_mutex_lock(&process->mutex) != 0)
        return 0;
 
    if (process->pid != -1) {
        do {
            pid = waitpid(process->pid, &pstat, 0);
        } while (pid == -1 && errno == EINTR);
    }
 
    if (process->read_fp)
        fclose(process->read_fp);
 
    if (process->write_fp)
        fclose(process->write_fp);
 
    pthread_mutex_destroy(&process->mutex);
 
    free(process);
 
    return (pid == -1 ? -1 : pstat);
}
 
int popen_plus_kill(struct popen_plus_process *process)
{
    char command[64];
 
    sprintf(command, "kill -9 %d", process->pid);
    system(command);
 
    return 0;
}
 
int popen_plus_kill_by_id(int process_id)
{
    char command[64];
 
    sprintf(command, "kill -9 %d", process_id);
    system(command);
 
    return 0;
}
 
int popen_plus_terminate(struct popen_plus_process *process)
{
    char command[64];
 
    sprintf(command, "kill -TERM %d", process->pid);
    system(command);
 
    return 0;
}
 
int popen_plus_terminate_with_id(int process_id)
{
    char command[64];
 
    sprintf(command, "kill -TERM %d", process_id);
    system(command);
 
    return 0;
}

This code was inspired by many sources and I think I perfected it, although you might find bugs or issues and if you do, please let me know if the comments section.

You can use it like so:

struct popen_plus_process *process = popen_plus("ls -l");
if (!process) {
     /* Failed do something and return or exit */
     return -1;
}
 
int MAX_BUFFER = 256;
char buffer[256];
 
while (!feof(process->read_fp))
     if (fgets(buffer, MAX_BUFFER, process->read_fp) != NULL)
          printf("%s\n", buffer);
 
popen_plus_close(process);

To kill a process:

popen_plus_kill(process);
popen_plus_close(process);

Terminate is similar to kill…

I hope this helps someone!

:)

Popen+ a Bidirectional Popen Implementation With Ability to Access PID and Kill/Terminate Processes