/*
* Process spawn functions
* Copyright (C) 2008 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "tvheadend.h"
#include "file.h"
#include "spawn.h"
#if ENABLE_ANDROID
#define WIFCONTINUED(s) ((s) == 0xffff)
#endif
extern char **environ;
pthread_mutex_t spawn_mutex = PTHREAD_MUTEX_INITIALIZER;
static LIST_HEAD(, spawn) spawns;
typedef struct spawn {
LIST_ENTRY(spawn) link;
pid_t pid;
const char *name;
} spawn_t;
/*
* Search PATH for executable
*/
int
find_exec ( const char *name, char *out, size_t len )
{
int ret = 0;
char bin[512];
char *path, *tmp, *tmp2 = NULL;
DIR *dir;
struct dirent *de;
struct stat st;
if (!(path = getenv("PATH"))) return 0;
path = strdup(path);
tmp = strtok_r(path, ":", &tmp2);
while (tmp && !ret) {
if ((dir = opendir(tmp))) {
while ((de = readdir(dir))) {
if (strstr(de->d_name, name) != de->d_name) continue;
snprintf(bin, sizeof(bin), "%s/%s", tmp, de->d_name);
if (lstat(bin, &st)) continue;
if (!S_ISREG(st.st_mode) || !(st.st_mode & S_IEXEC)) continue;
strncpy(out, bin, len);
ret = 1;
break;
}
closedir(dir);
}
tmp = strtok_r(NULL, ":", &tmp2);
}
free(path);
return ret;
}
/**
* Reap one child
*/
int
spawn_reap(char *stxt, size_t stxtlen)
{
pid_t pid;
int status, res;
spawn_t *s;
pid = waitpid(-1, &status, WNOHANG);
if(pid < 1)
return -EAGAIN;
pthread_mutex_lock(&spawn_mutex);
LIST_FOREACH(s, &spawns, link)
if(s->pid == pid)
break;
res = -EIO;
if (WIFEXITED(status)) {
res = WEXITSTATUS(status);
if (stxt)
snprintf(stxt, stxtlen, "exited, status=%d", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
snprintf(stxt, stxtlen, "killed by signal %d, "
"stopped by signal %d",
WTERMSIG(status),
WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
snprintf(stxt, stxtlen, "continued");
} else {
snprintf(stxt, stxtlen, "unknown status");
}
if(s != NULL) {
LIST_REMOVE(s, link);
free((void *)s->name);
free(s);
}
pthread_mutex_unlock(&spawn_mutex);
return res;
}
/**
* The reaper is called once a second to finish of any pending spawns
*/
void
spawn_reaper(void)
{
while (spawn_reap(NULL, 0) != -EAGAIN) ;
}
/**
* Enqueue a spawn on the pending spawn list
*/
static spawn_t *
spawn_enq(const char *name, int pid)
{
spawn_t *s = calloc(1, sizeof(spawn_t));
s->name = strdup(name);
s->pid = pid;
pthread_mutex_lock(&spawn_mutex);
LIST_INSERT_HEAD(&spawns, s, link);
pthread_mutex_unlock(&spawn_mutex);
return s;
}
/**
* Execute the given program and return its output in a malloc()ed buffer
*
* *outp will point to the allocated buffer
* The function will return the size of the buffer
*/
int
spawn_and_store_stdout(const char *prog, char *argv[], char **outp)
{
pid_t p;
int fd[2], f;
char bin[256];
const char *local_argv[2] = { NULL, NULL };
if (*prog != '/' && *prog != '.') {
if (!find_exec(prog, bin, sizeof(bin))) return -1;
prog = bin;
}
if(!argv) argv = (void *)local_argv;
if (!argv[0]) argv[0] = (char*)prog;
pthread_mutex_lock(&fork_lock);
if(pipe(fd) == -1) {
pthread_mutex_unlock(&fork_lock);
return -1;
}
p = fork();
if(p == -1) {
pthread_mutex_unlock(&fork_lock);
syslog(LOG_ERR, "spawn: Unable to fork() for \"%s\" -- %s",
prog, strerror(errno));
return -1;
}
if(p == 0) {
close(0);
close(2);
close(fd[0]);
dup2(fd[1], 1);
close(fd[1]);
f = open("/dev/null", O_RDWR);
if(f == -1) {
syslog(LOG_ERR,
"spawn: pid %d cannot open /dev/null for redirect %s -- %s",
getpid(), prog, strerror(errno));
exit(1);
}
dup2(f, 0);
dup2(f, 2);
close(f);
execve(prog, argv, environ);
syslog(LOG_ERR, "spawn: pid %d cannot execute %s -- %s",
getpid(), prog, strerror(errno));
exit(1);
}
pthread_mutex_unlock(&fork_lock);
spawn_enq(prog, p);
close(fd[1]);
return file_readall(fd[0], outp);
}
/**
* Execute the given program with arguments
*
* *outp will point to the allocated buffer
* The function will return the size of the buffer
*/
int
spawnv(const char *prog, char *argv[])
{
pid_t p;
char bin[256];
const char *local_argv[2] = { NULL, NULL };
if (*prog != '/' && *prog != '.') {
if (!find_exec(prog, bin, sizeof(bin))) return -1;
prog = bin;
}
if(!argv) argv = (void *)local_argv;
if (!argv[0]) argv[0] = (char*)prog;
p = fork();
if(p == -1) {
syslog(LOG_ERR, "spawn: Unable to fork() for \"%s\" -- %s",
prog, strerror(errno));
return -1;
}
if(p == 0) {
close(0);
close(2);
syslog(LOG_INFO, "spawn: Executing \"%s\"", prog);
execve(prog, argv, environ);
syslog(LOG_ERR, "spawn: pid %d cannot execute %s -- %s",
getpid(), prog, strerror(errno));
close(1);
exit(1);
}
spawn_enq(prog, p);
return 0;
}