blob: 44e6272aa096aa9efb56c47a0bc2cd0fbf5cc174 [file] [log] [blame]
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#if !defined __linux__
#include <libproc.h>
#endif
#include "../proc.h"
#include "../fsatrace.h"
void
procPath(char *fullpath)
{
#if defined __linux__
char exepath[64];
ssize_t ret;
snprintf(exepath, sizeof(exepath), "/proc/%d/exe", getpid());
ret = readlink(exepath, fullpath, PATH_MAX);
assert(ret != -1);
fullpath[ret] = 0;
#else
proc_pidpath(getpid(), fullpath, PATH_MAX);
#endif
}
void
procDumpArgs(unsigned nargs, char *const args[])
{
unsigned i;
for (i = 0; i < nargs; i++)
fprintf(stderr, "argv[%d]=%s\n", i, args[i]);
}
static enum procerr
waitchild(int child, int *rc)
{
enum procerr ret;
if (-1 != waitpid(child, rc, 0)) {
int st = *rc;
if (WIFEXITED(st)) {
ret = ERR_PROC_OK;
*rc = WEXITSTATUS(st);
} else if (WIFSIGNALED(st)) {
ret = ERR_PROC_SIGNALED;
*rc = WTERMSIG(st);
} else if (WIFSTOPPED(st)) {
ret = ERR_PROC_STOPPED;
*rc = WSTOPSIG(st);
} else {
ret = ERR_PROC_UNKNOWN;
}
} else
ret = ERR_PROC_WAIT;
return ret;
}
static void
dump(const char *path, char *p, size_t sz)
{
int fd;
ssize_t r = 0;
if (strcmp(path, "-"))
fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0777);
else
fd = 1;
if (fd >= 0)
r = write(fd, p, sz);
else
fprintf(stderr, "Unable to open output file '%s'", path);
if (r != sz)
fprintf(stderr, "Short write (%ld/%ld)", r, sz);
if (fd != 1)
close(fd);
}
enum procerr
procRun(unsigned nargs, char *const args[], int *rc, char const *out)
{
extern char **environ;
int ret;
int mkfiforet;
int child;
char so[PATH_MAX + 3];
char fullpath[PATH_MAX];
char fifo[PATH_MAX];
procPath(fullpath);
snprintf(so, sizeof(so), "%s.so", fullpath);
setenv("LD_PRELOAD", so, 1);
snprintf(fifo, strlen(out) + 6, "%s.fifo", out);
mkfiforet = mkfifo(fifo, 0666);
// If the $out.fifo is not accessible (e.g. /dev/null.fifo), attempt to
// place fifo in temp dir instead.
if (-1 == mkfiforet && errno == EACCES) {
tmpnam(fifo);
mkfiforet = mkfifo(fifo, 0666);
}
if (-1 == mkfiforet) {
fprintf(stderr, "failed to make fifo %s: %s", fifo,
strerror(errno));
return ERR_PROC_UNKNOWN;
}
if (-1 == setenv(ENV_FIFO_PATH, fifo, 1)) {
fprintf(stderr, "failed to setenv %s=%s: %s\n", ENV_FIFO_PATH,
fifo, strerror(errno));
return ERR_PROC_UNKNOWN;
}
child = fork();
switch (child) {
case -1:
ret = ERR_PROC_FORK;
break;
case 0:
execvp(args[0], args);
_exit(EXIT_FAILURE);
break;
default:
char *buf;
size_t sz;
char c;
const int fd = open(fifo, O_CREAT | O_RDONLY);
if (fd < 0) {
fprintf(stderr,
"failed to open fifo %s for write: %s\n", fifo,
strerror(errno));
return ERR_PROC_UNKNOWN;
}
FILE *memstream = open_memstream(&buf, &sz);
while (read(fd, &c, 1) > 0)
fprintf(memstream, "%c", c);
fclose(memstream);
close(fd);
dump(out, buf, sz);
unlink(fifo);
ret = waitchild(child, rc);
}
return ret;
}