diff --git a/build.sh b/build.sh index 48e9af0..fd5d35f 100755 --- a/build.sh +++ b/build.sh @@ -38,3 +38,10 @@ echo "[ BUILD RELEASE ]" run_builder='go build -v' $run_builder -ldflags "-X $info" -o "bin/redis-shake" "./src/redis-shake/main/main.go" echo "build successfully!" + +if [ "Linux" == "$(uname -s)" ];then + # hypervisor + gcc -Wall -O3 scripts/hypervisor.c -o ${output}/hypervisor -lpthread +elif [ "Darwin" == "$(uname -s)" ];then + printf "\\nWARNING !!! MacOS doesn't supply hypervisor\\n" +fi diff --git a/script/hypervisor.c b/script/hypervisor.c new file mode 100644 index 0000000..ca0b605 --- /dev/null +++ b/script/hypervisor.c @@ -0,0 +1,260 @@ +/* + * Compilation: + * gcc -Wall -O3 hypervisor.c -o hypervisor + * + * */ + +#include +#include +#include +#include + +#ifdef __linux__ +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include + + +#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; +} diff --git a/script/start.sh b/script/start.sh new file mode 100755 index 0000000..6767f7b --- /dev/null +++ b/script/start.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +catalog=$(dirname "$0") + +cd "${catalog}"/../ || exit 1 + +if [ $# != 2 ] ; then + echo "USAGE: $0 [conf] [mode]" + exit 0 +fi + +name="redis-shake" + +if [ "Darwin" == "$(uname -s)" ];then + printf "\\nWARNING !!! MacOs doesn't supply to use this script, please use \"./%s -conf=config_file_name\" manual command to run\\n" "$name" + exit 1 +fi + +./bin/hypervisor --daemon --exec="./bin/$name -conf=$1 -type=$2 2>&1 1>> $name.output" 1>>hypervisor.output 2>&1 diff --git a/script/stop.sh b/script/stop.sh new file mode 100755 index 0000000..695ef63 --- /dev/null +++ b/script/stop.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +#kill -9 "$(cat "$1")" +if [ $# != 1 ] ; then + echo "USAGE: $0 [pid filename which by default is 'redis-shake.pid']" + exit 0 +fi +ppid=$(ps -ef | awk '{if ($2=='`cat $1`') print $3}') +[ -z $ppid ] && echo "[Fail] No process number for $(cat "$1")." && exit 1 +if [ $ppid -eq 1 ];then + kill -9 "$(cat "$1")" +else + kill -9 "$ppid" "$(cat "$1")" +fi