1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
/*
* rocks/exec.c
*
* Exec handler
*
* Copyright (C) 2001 Victor Zandy
* See COPYING for distribution terms.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "rs.h"
#include "log.h"
/* If non-negative, we are recovering from an exec call */
static int rs_execfd = -1;
int
rs_in_exec()
{
char *s;
if (rs_execfd == -1 && (s = getenv("RS_EXEC_FD"))) {
rs_execfd = atoi(s);
/* don't propagate this variable */
unsetenv("RS_EXEC_FD");
}
return rs_execfd != -1;
}
static void
exec_daemon_sighandler(int sig)
{
rs_log("exec daemon time out");
exit(1);
}
/* Called in native syscall mode in new process.
Feeds rock state to its parent, which has called exec, over FD.
Currently preserves server and client (but not listener) sockets.
Like everything else, does not handle duped sockets. */
static void
exec_daemon(int fd)
{
int i;
struct sigaction sa;
struct itimerval it;
pid_t pid;
rs_t rs;
/* fork (again) to reparent */
pid = fork();
if (0 > pid) {
rs_log("exec daemon failed to start");
exit(1);
}
if (pid) {
rs_log("exec daemon is [%d]", pid);
_exit(0); /* no atexit */
}
memset(&sa, 0, sizeof(sa));
sigfillset(&sa.sa_mask);
sigdelset(&sa.sa_mask, SIGTERM);
sigdelset(&sa.sa_mask, SIGINT);
sa.sa_handler = exec_daemon_sighandler;
rs_rs_sigaction(SIGALRM, &sa);
it.it_value.tv_sec = 60;
it.it_interval.tv_sec = 0;
it.it_value.tv_usec = it.it_interval.tv_usec = 0;
if (0 > setitimer(ITIMER_REAL, &it, NULL))
rs_log("exec daemon failed to set timeout");
for (i = 0; i < RS_MAXFD; i++) {
if ((rs = rs_lookup(i)) && rs->state == RS_ESTABLISHED)
if (0 > rs_save(rs, fd)) {
rs_log("exec daemon failed to xfer state");
_exit(1); /* no atexit */
}
}
close(fd);
rs_log("exec daemon exiting");
_exit(0); /* no atexit */
}
void
rs_restore_exec()
{
rs_t rs;
rs_log("restoring after exec");
while ((rs = rs_restore(rs_execfd))) {
rs_log("exec restored rock %d (now <%p>)", rs->sd, rs);
}
close(rs_execfd);
rs_log("exec restore success");
}
extern char **environ;
static char **
extend_env(char *const envp[], char *buf)
{
char **p;
int len;
int i;
len = 0;
p = (char **)envp;
while (*p++)
len++;
p = (char **) malloc((len+2) * sizeof(char *));
if (!p)
return NULL;
for (i = 0; i < len; i++)
p[i] = envp[i];
p[len] = buf;
p[len+1] = NULL;
return p;
}
int
rs_execve(const char *filename, char *const argv[], char *const envp[])
{
char abuf[512], *ap;
char *const *q;
char buf[32];
int p[2];
pid_t pid;
char **ep;
sigset_t cur, old;
struct itimerval it, oit;
sigemptyset(&cur);
sigaddset(&cur, SIGCHLD);
if (0 > sigprocmask(SIG_BLOCK, &cur, &old)) {
rs_log("Cannot block SIGCHLD");
assert(0);
}
if (0 > pipe(p))
return -1;
pid = fork();
if (0 > pid)
return -1;
if (0 == pid) {
close(p[0]);
exec_daemon(p[1]); /* does not return */
assert(0);
}
ap = abuf;
q = argv;
while (*q && sizeof(abuf) > ap - &abuf[0]) {
ap += snprintf(ap, sizeof(abuf) - (ap - &abuf[0]),
"%s ", *q);
q++;
}
rs_log("exec(%s) -> handled by [%d]", abuf, pid);
waitpid(pid, NULL, 0); /* no problem if we can't reap it;
someone else did for us */
if (0 > sigprocmask(SIG_SETMASK, &old, NULL)) {
rs_log("Cannot unblock SIGCHLD");
assert(0);
}
close(p[1]);
snprintf(buf, sizeof(buf), "RS_EXEC_FD=%d", p[0]);
ep = extend_env(envp, buf);
if (!ep) {
rs_kill9_and_wait(pid);
goto err;
}
/* cancel timer in execed process, lest we're not there to
handle it */
it.it_value.tv_sec = it.it_interval.tv_sec = 0;
it.it_value.tv_usec = it.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &it, &oit);
execve(filename, argv, ep);
setitimer(ITIMER_REAL, &oit, NULL);
/* exec failed */
free(ep); /* allocated by extend_env */
err:
return -1;
}
|