mirror of https://github.com/login000/tcoin
136 lines
3.1 KiB
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;
|
|
}
|
|
|