mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-22 22:32:22 +00:00
280 lines
8.1 KiB
C
280 lines
8.1 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|