diff options
Diffstat (limited to 'launcher.c')
-rw-r--r-- | launcher.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/launcher.c b/launcher.c new file mode 100644 index 00000000..b51d0398 --- /dev/null +++ b/launcher.c @@ -0,0 +1,279 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2012-2013 ZeroTier Networks LLC + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +/* Launcher for Linux/Unix/Mac */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include "launcher.h" + +/* Must match first 16 bytes of EMBEDDED_VERSION_STAMP in Node.cpp */ +static const unsigned char EMBEDDED_VERSION_STAMP_KEY[16] = { 0x6d,0xfe,0xff,0x01,0x90,0xfa,0x89,0x57,0x88,0xa1,0xaa,0xdc,0xdd,0xde,0xb0,0x33 }; + +const unsigned char EMBEDDED_LAUNCHER_VERSION_STAMP[20] = { + 0x96,0xf0,0x00,0x08,0x18,0xff,0xc9,0xde,0xad,0xf0,0x0f,0xbe,0xef,0x30,0xce,0xa1, /* key */ + ZT_LAUNCHER_VERSION_MAJOR, + ZT_LAUNCHER_VERSION_MINOR, + (unsigned char)(((unsigned int)ZT_LAUNCHER_VERSION_REVISION) & 0xff), /* little-endian */ + (unsigned char)((((unsigned int)ZT_LAUNCHER_VERSION_REVISION) >> 8) & 0xff) +}; + +#define ZT_BINARY_NAME "zerotier-one" +#define ZT_BINARY_UPDATE_PREFIX "zerotier-one_update." + +#define ZT_LAUNCHER_PIDFILE "zerotier-launcher.pid" +#define ZT_ONE_PIDFILE "zerotier-one.pid" + +/* Load a file into newly malloc()'ed memory, len set to size */ +static unsigned char *loadFile(const char *path,unsigned long *len) +{ + unsigned char *fbuf = (unsigned char *)0; + FILE *f = fopen(path,"rb"); + if (f) { + if (!fseek(f,0,SEEK_END)) { + long l = ftell(f); + if (l > 0) { + fseek(f,0,SEEK_SET); + fbuf = malloc(l); + if (fbuf) { + if (fread(fbuf,l,1,f) != 1) { + free(fbuf); + fbuf = (unsigned char *)0; + } else *len = (unsigned long)l; + } + } + } + fclose(f); + } + return fbuf; +} + +/* Scans a ZeroTier binary and determines its version from its embedded version code */ +static int findVersion(const unsigned char *bin,unsigned long len,unsigned int *major,unsigned int *minor,unsigned int *revision) +{ + unsigned long i; + + if (len > 20) { + for(i=0;i<(len - 20);++i) { + if ((bin[i] == EMBEDDED_VERSION_STAMP_KEY[0])&&(!memcmp(bin + i,EMBEDDED_VERSION_STAMP_KEY,16))) { + *major = bin[i + 16]; + *minor = bin[i + 17]; + *revision = ((unsigned int)bin[i + 18] & 0xff) | (((unsigned int)bin[i + 19] << 8) & 0xff00); + return 1; + } + } + } + + return 0; +} + +/* Scan for updates and, if found, replace the main binary if possible */ +static int doUpdateBinaryIfNewer() +{ + long pfxLen = strlen(ZT_BINARY_UPDATE_PREFIX); + struct dirent dbuf,*d; + int needUpdate; + unsigned int major = 0,minor = 0,revision = 0; + unsigned int existingMajor = 0,existingMinor = 0,existingRevision = 0; + unsigned long binLen; + unsigned char *bin; + char oldname[1024]; + DIR *dir; + + binLen = 0; + bin = loadFile(ZT_BINARY_NAME,&binLen); + if (!((bin)&&(binLen)&&(findVersion(bin,binLen,&existingMajor,&existingMinor,&existingRevision)))) { + if (bin) + free(bin); + return 0; + } + free(bin); + + dir = opendir("."); + if (!dir) + return 0; + while (!readdir_r(dir,&dbuf,&d)) { + if (!d) break; + if (!strncasecmp(d->d_name,ZT_BINARY_UPDATE_PREFIX,pfxLen)) { + binLen = 0; + unsigned char *bin = loadFile(d->d_name,&binLen); + if ((bin)&&(binLen)&&(findVersion(bin,binLen,&major,&minor,&revision))) { + needUpdate = 0; + if (major > existingMajor) + needUpdate = 1; + else if (major == existingMajor) { + if (minor > existingMinor) + needUpdate = 1; + else if (minor == existingMinor) { + if (revision > existingRevision) + needUpdate = 1; + } + } + free(bin); + if (needUpdate) { + /* fprintf(stderr,"zerotier-launcher: replacing %s with %s\n",ZT_BINARY_NAME,d->d_name); */ + sprintf(oldname,"%s.OLD",ZT_BINARY_NAME); + if (!rename(ZT_BINARY_NAME,oldname)) { + /* fprintf(stderr,"zerotier-launcher: %s -> %s\n",ZT_BINARY_NAME,oldname); */ + if (!rename(d->d_name,ZT_BINARY_NAME)) { + /* fprintf(stderr,"zerotier-launcher: %s -> %s\nzerotier-launcher: delete %s\n",d->d_name,ZT_BINARY_NAME,oldname); */ + chmod(ZT_BINARY_NAME,0755); + unlink(oldname); + return 1; + } + } + break; + } + } + if (bin) + free(bin); + } + } + closedir(dir); + + return 0; +} + +static volatile long childPid = 0; + +static void sigRepeater(int sig) +{ + if (childPid > 0) + kill(childPid,sig); +} + +int main(int argc,char **argv) +{ + const char *zerotierHome = ZT_DEFAULT_HOME; + FILE *pidf; + int status,exitCode; + unsigned long timeStart; + unsigned int numSubTwoSecondRuns; + + /* Pass on certain signals transparently to the subprogram to do with as it will */ + signal(SIGHUP,&sigRepeater); + signal(SIGPIPE,SIG_IGN); + signal(SIGUSR1,&sigRepeater); + signal(SIGUSR2,&sigRepeater); + signal(SIGALRM,SIG_IGN); + signal(SIGURG,SIG_IGN); + signal(SIGTERM,&sigRepeater); + signal(SIGQUIT,&sigRepeater); + + if (argc == 2) + zerotierHome = argv[1]; + + if (chdir(zerotierHome)) { + fprintf(stderr,"%s: fatal error: could not chdir to %s\n",argv[0],zerotierHome); + return ZT_EXEC_RETURN_VALUE_UNRECOVERABLE_ERROR; + } + + pidf = fopen(ZT_LAUNCHER_PIDFILE,"w"); + if (pidf) { + fprintf(pidf,"%d",(int)getpid()); + fclose(pidf); + } + + numSubTwoSecondRuns = 0; + exitCode = ZT_EXEC_RETURN_VALUE_NORMAL_TERMINATION; + +restart_subprogram: + /* We actually do this on every loop, which is fine. It picks up any + * newer versions that are waiting and swaps them out for the current + * running binary. */ + doUpdateBinaryIfNewer(); + + timeStart = time(0); + childPid = fork(); + if (childPid < 0) { + fprintf(stderr,"%s: fatal error: could not fork(): %s\n",argv[0],strerror(errno)); + return ZT_EXEC_RETURN_VALUE_UNRECOVERABLE_ERROR; + } else if (childPid) { + pidf = fopen(ZT_ONE_PIDFILE,"w"); + if (pidf) { + fprintf(pidf,"%d",(int)childPid); + fclose(pidf); + } + + status = ZT_EXEC_RETURN_VALUE_NO_BINARY; +wait_for_subprogram_exit: + if ((long)waitpid(childPid,&status,0) >= 0) { + if (WIFEXITED(status)) { + unlink(ZT_ONE_PIDFILE); + + if ((time(0) - timeStart) < 2) { + /* Terminate abnormally if we appear to be looping in a tight loop + * to avoid fork bombing if one exits abnormally without an abnormal + * exit code. */ + if (++numSubTwoSecondRuns >= 16) { + fprintf(stderr,"%s: fatal error: program exiting immediately in infinite loop\n",argv[0]); + return ZT_EXEC_RETURN_VALUE_UNRECOVERABLE_ERROR; + } + } + + switch(WEXITSTATUS(status)) { + case ZT_EXEC_RETURN_VALUE_NORMAL_TERMINATION: + exitCode = ZT_EXEC_RETURN_VALUE_NORMAL_TERMINATION; + goto exit_launcher; + case ZT_EXEC_RETURN_VALUE_NO_BINARY: + fprintf(stderr,"%s: fatal error: binary zerotier-one not found at %s\n",argv[0],zerotierHome); + exitCode = ZT_EXEC_RETURN_VALUE_UNRECOVERABLE_ERROR; + goto exit_launcher; + case ZT_EXEC_RETURN_VALUE_TERMINATED_FOR_UPGRADE: + case ZT_EXEC_RETURN_VALUE_PLEASE_RESTART: + goto restart_subprogram; + default: + exitCode = status; + goto exit_launcher; + } + } + } else if (errno != EINTR) { + fprintf(stderr,"%s: fatal error: waitpid() failed: %s\n",argv[0],strerror(errno)); + exitCode = ZT_EXEC_RETURN_VALUE_UNRECOVERABLE_ERROR; + goto exit_launcher; + } else { + goto wait_for_subprogram_exit; + } + } else { + execl(ZT_BINARY_NAME,ZT_BINARY_NAME,zerotierHome,(char *)0); + exit(ZT_EXEC_RETURN_VALUE_NO_BINARY); /* only reached if execl succeeds */ + } + +exit_launcher: + unlink(ZT_LAUNCHER_PIDFILE); + return exitCode; +} |