tcoin/popen2.c

136 lines
3.1 KiB
C

#include"popen2.h"
#include<errno.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>
#define CLEANUP_PIPE(pipe) close((pipe)[0]); close((pipe)[1])
//https://github.com/iximiuz/popen2
//https://iximiuz.com/en/posts/how-to-on-processes/
typedef struct files_t files_t;
struct files_chain_t {
files_t files;
pid_t pid;
struct files_chain_t *next;
};
typedef struct files_chain_t files_chain_t;
static files_chain_t *files_chain;
static int _do_popen2(files_chain_t *link, const char *command)
{
int child_in[2];
int child_out[2];
if (0 != pipe(child_in)) {
return -1;
}
if (0 != pipe(child_out)) {
CLEANUP_PIPE(child_in);
return -1;
}
pid_t cpid = link->pid = fork();
if (0 > cpid) {
CLEANUP_PIPE(child_in);
CLEANUP_PIPE(child_out);
return -1;
}
if (0 == cpid) {
if (0 > dup2(child_in[0], 0) || 0 > dup2(child_out[1], 1)) {
_Exit(127);
}
CLEANUP_PIPE(child_in);
CLEANUP_PIPE(child_out);
for (files_chain_t *p = files_chain; p; p = p->next) {
int fd_in = fileno(p->files.in);
if (fd_in != 0) {
close(fd_in);
}
int fd_out = fileno(p->files.out);
if (fd_out != 1) {
close(fd_out);
}
}
execl("/bin/sh", "sh", "-c", command, (char *) NULL);
_Exit(127);
}
close(child_in[0]);
close(child_out[1]);
link->files.in = fdopen(child_in[1], "w");
link->files.out = fdopen(child_out[0], "r");
return 0;
}
/**
* NAME
* popen2 -- bidirectional popen()
*
* DESCRIPTION
* popen2(const char *command) opens two pipes, forks a child process,
* then binds the pipes to its stdin and stdout and execve shell to
* execute given command.
*
* RETURN VALUES:
* On success it returns a pointer to the struct with two fields
* { FILE *in; FILE *out; }. The struct should be released via pclose2()
* call. On failure returns NULL, check errno for more informaion about
* the error.
*/
files_t *popen2(const char *command)
{
files_chain_t *link = (files_chain_t *) malloc(sizeof (files_chain_t));
if (NULL == link) {
return NULL;
}
if (0 > _do_popen2(link, command)) {
free(link);
return NULL;
}
link->next = files_chain;
files_chain = link;
return (files_t *) link;
}
int pclose2(files_t *fp) {
files_chain_t **p = &files_chain;
int found = 0;
while (*p) {
if (*p == (files_chain_t *) fp) {
*p = (*p)->next;
found = 1;
break;
}
p = &(*p)->next;
}
if (!found) {
return -1;
}
if (0 > fclose(fp->in) || 0 > fclose(fp->out)) {
free((files_chain_t *) fp);
return -1;
}
int status = -1;
pid_t wait_pid;
do {
wait_pid = waitpid(((files_chain_t *) fp)->pid, &status, 0);
} while (-1 == wait_pid && EINTR == errno);
free((files_chain_t *) fp);
if (wait_pid == -1) {
return -1;
}
return status;
}