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.
279 lines
8.1 KiB
279 lines
8.1 KiB
/* |
|
* 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; |
|
}
|
|
|