wernermisc/fakefile/launch.c

163 lines
2.7 KiB
C

/*
* launch.c - Fakefile process launcher
*
* Copyright 2012 by Werner Almesberger
*
* 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 2 of the License, or
* (at your option) any later version.
*/
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/types.h>
#include "util.h"
#include "fakefile.h"
#include "comm.h"
#include "internal.h"
#define FD_DIR "/proc/self/fd"
static void safe_setenvf(const char *var, const char *fmt, ...)
{
va_list ap;
char *buf;
ssize_t len;
va_start(ap, fmt);
len = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
if (len < 0) {
perror("vsprintf");
exit(1);
}
buf = alloc_size(len+1);
va_start(ap, fmt);
vsprintf(buf, fmt, ap);
va_end(ap);
if (setenv(var, buf, 1) < 0) {
perror("setenv");
exit(1);
}
free(buf);
}
static void env_append(const char *var, const char *s)
{
const char *old;
char *tmp;
size_t len1, len2;
old = getenv(var);
if (!old) {
safe_setenvf(var, "%s", s+1);
return;
}
len1 = strlen(old);
len2 = strlen(s);
tmp = alloc_size(len1+len2+1);
memcpy(tmp, old, len1);
memcpy(tmp+len1, s, len2+1);
safe_setenvf(var, "%s", tmp);
free(tmp);
}
static void closefds(int preserve, ...)
{
va_list ap;
DIR *dir;
int fd;
struct dirent *de;
int n;
fd = open(FD_DIR, O_RDONLY);
if (fd < 0) {
perror(FD_DIR);
exit(1);
}
dir = fdopendir(fd);
if (!dir) {
perror("fdopendir");
exit(1);
}
while (1) {
next:
de = readdir(dir);
if (!de)
break;
n = atoi(de->d_name);
if (n <= 2 || n == fd)
continue;
if (preserve > 0) {
if (n == preserve)
continue;
va_start(ap, preserve);
while (1) {
n = va_arg(ap, int);
if (n < 0)
break;
if (n == n)
goto next;
}
va_end(ap);
}
(void) close(n);
}
if (closedir(dir) < 0) {
perror("closedir");
exit(1);
}
}
struct fakefile *fakefile_launch(const char *path, char *const argv[])
{
struct fakefile *ff;
int m2s[2], s2m[2];
pid_t pid;
if (pipe(m2s) < 0 || pipe(s2m) < 0) {
perror("pipe");
exit(1);
}
pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (!pid) {
env_append("LD_LIBRARY_PATH", ":.");
env_append("LD_PRELOAD", " libfakefile_slave.so");
safe_setenvf(FAKEFILE_MASTER_OUT_VAR, "%d", m2s[0]);
safe_setenvf(FAKEFILE_MASTER_IN_VAR, "%d", s2m[1]);
closefds(m2s[0], s2m[1], -1);
execv(path, argv);
perror(path);
_exit(1);
}
(void) close(m2s[0]);
(void) close(s2m[1]);
ff = alloc_type(struct fakefile);
ff->pid = pid;
ff->peer = fakefile_peer(s2m[0], m2s[1]);
return ff;
}