You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
260 lines
5.4 KiB
260 lines
5.4 KiB
/*
|
|
* Compilation:
|
|
* gcc -Wall -O3 hypervisor.c -o hypervisor
|
|
*
|
|
* */
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef __linux__
|
|
#include <wait.h>
|
|
#else
|
|
#include <sys/wait.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
|
|
|
|
#define MAXOPT 256
|
|
#define INTERVAL 5
|
|
#define MAXINTERVAL 180
|
|
|
|
#define USAGE() do{ \
|
|
fprintf(stderr, "Usage : %s [--daemon] --exec=\"command arg1 arg2 arg3 ...\"\n", argv[0]); \
|
|
}while(0)
|
|
|
|
static char *cmd, *cmdopt[MAXOPT + 1];
|
|
static int daemonize;
|
|
|
|
static int parseopt(int argc, char *argv[]);
|
|
static int set_nonblock(int fd);
|
|
static int getstatus(char *buf, int size, int status);
|
|
|
|
static int parseopt(int argc, char *argv[])
|
|
{
|
|
int ch, i;
|
|
char *token, *tmpptr, *cmdstr;
|
|
|
|
cmdstr = cmd = NULL;
|
|
daemonize = 0;
|
|
for(i = 0; i < MAXOPT + 1; i++){
|
|
cmdopt[i] = NULL;
|
|
}
|
|
|
|
struct option long_options[] = {
|
|
{"daemon",optional_argument,NULL,'d'},
|
|
{"exec",required_argument,NULL,'e'},
|
|
{0,0,0,0},
|
|
};
|
|
|
|
while((ch=getopt_long(argc, argv, "dec:", long_options, NULL)) != -1) {
|
|
switch(ch)
|
|
{
|
|
case 'e':
|
|
if((cmdstr = strdup(optarg)) == NULL )
|
|
return -1;
|
|
break;
|
|
case 'd':
|
|
daemonize = 1;
|
|
break;
|
|
default:
|
|
USAGE();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if(cmdstr == NULL){
|
|
USAGE();
|
|
return -1;
|
|
}
|
|
|
|
for(i = 0;i < MAXOPT + 1;cmdstr = NULL, i++){
|
|
token = strtok_r(cmdstr, " \t\n", &tmpptr);
|
|
if(token == NULL){
|
|
break;
|
|
} else {
|
|
cmdopt[i] = strdup(token);
|
|
|
|
if(i == 0){
|
|
cmd = strdup(token);
|
|
}
|
|
}
|
|
}
|
|
|
|
if( (cmd == NULL) || (strlen(cmd) == 0) ){
|
|
fprintf(stderr, "Error, cmd should not be empty.\n");
|
|
return -1;
|
|
}
|
|
|
|
if(i == MAXOPT + 1){
|
|
fprintf(stderr, "Argument too long\n");
|
|
return -1;
|
|
}
|
|
|
|
cmdopt[i] = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int set_nonblock(int fd)
|
|
{
|
|
int flags = fcntl(fd, F_GETFL, 0);
|
|
if (flags == -1) {
|
|
return -1;
|
|
}
|
|
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
}
|
|
|
|
static int getstatus(char *buf, int size, int status)
|
|
{
|
|
int n, len;
|
|
|
|
len = size;
|
|
|
|
if(WIFEXITED(status)){
|
|
n = snprintf(buf, len, "- normal termination, exit status = %d\n", WEXITSTATUS(status));
|
|
} else if(WIFSIGNALED(status)) {
|
|
n = snprintf(buf, len, "- abnormal termination, signal number = %d%s",
|
|
WTERMSIG(status),
|
|
#ifdef WCOREDUMP
|
|
WCOREDUMP(status) ? "-> core file generated" : "");
|
|
#else
|
|
"");
|
|
#endif
|
|
} else if(WIFSTOPPED(status)) {
|
|
n = snprintf(buf, len, "child stopped, signal number = %d\n", WSTOPSIG(status));
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
|
|
void go_daemon() {
|
|
int fd;
|
|
|
|
if (fork() != 0) exit(0); /* parent exits */
|
|
setsid(); /* create a new session */
|
|
|
|
/* Every output goes to /dev/null. If Redis is daemonized but
|
|
* * the 'logfile' is set to 'stdout' in the configuration file
|
|
* * it will not log at all. */
|
|
if ((fd = open("/tmp/mongo4bls.output", O_RDWR, 0)) != -1) {
|
|
dup2(fd, STDIN_FILENO);
|
|
dup2(fd, STDOUT_FILENO);
|
|
dup2(fd, STDERR_FILENO);
|
|
if (fd > STDERR_FILENO) close(fd);
|
|
}
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int ssec = INTERVAL, ret, status;
|
|
int first_start = 1;
|
|
int pipefd[2], waited, alive, isdaemon;
|
|
char buf[1024], info[4096];
|
|
pid_t pid;
|
|
time_t now, last = time(NULL);
|
|
|
|
if((ret = parseopt(argc, argv)) < 0 )
|
|
exit(ret);
|
|
|
|
daemonize ? go_daemon() : 0;
|
|
|
|
while(1){
|
|
if(pipe(pipefd) < 0){
|
|
fprintf(stderr, "- make pipe error : %s\n", strerror(errno));
|
|
exit(-1);
|
|
}
|
|
|
|
if( (ret = set_nonblock(pipefd[0])) < 0 ){
|
|
fprintf(stderr, "- set read nonblock error : %s\n", strerror(errno));
|
|
exit(-1);
|
|
}
|
|
|
|
if((pid = fork()) < 0){
|
|
fprintf(stderr, "- call fork() error : %s\n", strerror(errno));
|
|
exit(-1);
|
|
} else if (pid > 0){
|
|
close(pipefd[1]);
|
|
alive = waited = 1;
|
|
isdaemon = 0;
|
|
while(alive){
|
|
if(waited){
|
|
if(pid != waitpid(pid, &status, 0)){
|
|
sleep(INTERVAL);
|
|
continue;
|
|
} else {
|
|
fprintf(stderr, "- child process[%d] terminated .\n",pid);
|
|
if (first_start && (time(NULL)-last)<=5) {
|
|
fprintf(stderr,"- child process killed in %ld seconds , may wrong ! exit !\n",(time(NULL)-last));
|
|
exit(-1);
|
|
} else
|
|
first_start = 0;
|
|
waited = 0;
|
|
}
|
|
}
|
|
|
|
ret = read(pipefd[0], buf, sizeof(buf));
|
|
if(ret < 0){
|
|
if(errno == EAGAIN){
|
|
if(isdaemon == 0){
|
|
fprintf(stderr, "- this daemon process has no output !.\n");
|
|
isdaemon = 1;
|
|
}
|
|
sleep(INTERVAL);
|
|
continue;
|
|
} else {
|
|
fprintf(stderr, "- read pipe error : %s\n", strerror(errno));
|
|
exit(-1);
|
|
}
|
|
} else if(ret == 0) {
|
|
alive = 0;
|
|
close(pipefd[0]);
|
|
fprintf(stderr, "- read zero from pipe of children.\n");
|
|
if(isdaemon == 0){
|
|
getstatus(info, sizeof(info), status);
|
|
fprintf(stderr, "- extra info: %s\n", info);
|
|
} else {
|
|
strcpy(info, "");
|
|
}
|
|
continue;
|
|
} else {
|
|
fprintf(stderr, " - read pipe return: %d bytes\n", ret);
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
fprintf(stderr, "- process: \"%s\" exit, restart it\n", cmd);
|
|
|
|
sleep(ssec);
|
|
|
|
now = time(NULL);
|
|
if(now - last > 3600){
|
|
ssec = INTERVAL;
|
|
last = now;
|
|
} else {
|
|
ssec = (ssec << 1) < MAXINTERVAL ? (ssec << 1) : MAXINTERVAL;
|
|
}
|
|
} else {
|
|
close(pipefd[0]);
|
|
fprintf(stderr, "- execute: \"%s\"\n", cmd);
|
|
if(execvp(cmd, cmdopt) < 0){
|
|
fprintf(stderr, "- execute: \"%s\" error, %s\n", cmd, strerror(errno));
|
|
exit(-1);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|