mirror of
git://projects.qi-hardware.com/wernermisc.git
synced 2024-11-25 11:04:59 +02:00
163 lines
2.7 KiB
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;
|
|
}
|