2020-06-15 14:18:57 +00:00
|
|
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
|
|
│ │
|
2020-12-28 01:18:44 +00:00
|
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
2020-06-15 14:18:57 +00:00
|
|
|
│ │
|
2020-12-28 01:18:44 +00:00
|
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
2020-06-15 14:18:57 +00:00
|
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
2021-08-26 04:35:58 +00:00
|
|
|
#include "libc/assert.h"
|
2020-08-31 12:17:31 +00:00
|
|
|
#include "libc/bits/bits.h"
|
2021-03-01 07:42:35 +00:00
|
|
|
#include "libc/bits/safemacros.internal.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
#include "libc/calls/calls.h"
|
2021-01-28 03:34:02 +00:00
|
|
|
#include "libc/calls/sigbits.h"
|
2021-01-25 21:08:05 +00:00
|
|
|
#include "libc/calls/struct/flock.h"
|
2022-03-16 20:33:13 +00:00
|
|
|
#include "libc/dce.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
#include "libc/dns/dns.h"
|
2020-12-09 23:04:54 +00:00
|
|
|
#include "libc/fmt/conv.h"
|
2022-03-16 20:33:13 +00:00
|
|
|
#include "libc/intrin/kprintf.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
#include "libc/limits.h"
|
|
|
|
#include "libc/log/check.h"
|
|
|
|
#include "libc/log/log.h"
|
2021-08-07 20:22:35 +00:00
|
|
|
#include "libc/macros.internal.h"
|
2022-05-15 06:17:22 +00:00
|
|
|
#include "libc/mem/mem.h"
|
2021-08-26 04:35:58 +00:00
|
|
|
#include "libc/nexgen32e/crc32.h"
|
2021-03-08 04:23:29 +00:00
|
|
|
#include "libc/runtime/gc.internal.h"
|
2022-03-16 20:33:13 +00:00
|
|
|
#include "libc/runtime/runtime.h"
|
2020-11-25 16:19:00 +00:00
|
|
|
#include "libc/sock/ipclassify.internal.h"
|
2022-03-24 07:05:59 +00:00
|
|
|
#include "libc/sock/sock.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
#include "libc/stdio/stdio.h"
|
|
|
|
#include "libc/sysv/consts/af.h"
|
|
|
|
#include "libc/sysv/consts/ex.h"
|
2022-03-16 20:33:13 +00:00
|
|
|
#include "libc/sysv/consts/f.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
#include "libc/sysv/consts/fileno.h"
|
|
|
|
#include "libc/sysv/consts/ipproto.h"
|
|
|
|
#include "libc/sysv/consts/itimer.h"
|
2021-01-25 21:08:05 +00:00
|
|
|
#include "libc/sysv/consts/lock.h"
|
2021-08-07 20:22:35 +00:00
|
|
|
#include "libc/sysv/consts/map.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
#include "libc/sysv/consts/o.h"
|
2021-08-07 20:22:35 +00:00
|
|
|
#include "libc/sysv/consts/prot.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
#include "libc/sysv/consts/sock.h"
|
|
|
|
#include "libc/time/time.h"
|
|
|
|
#include "libc/x/x.h"
|
2021-08-14 13:17:56 +00:00
|
|
|
#include "net/https/https.h"
|
2021-08-07 20:22:35 +00:00
|
|
|
#include "third_party/mbedtls/ssl.h"
|
2022-05-15 06:17:22 +00:00
|
|
|
#include "third_party/zlib/zlib.h"
|
2021-08-07 20:22:35 +00:00
|
|
|
#include "tool/build/lib/eztls.h"
|
2021-08-14 13:17:56 +00:00
|
|
|
#include "tool/build/lib/psk.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
#include "tool/build/runit.h"
|
|
|
|
|
2022-04-20 16:56:53 +00:00
|
|
|
#define MAX_WAIT_CONNECT_SECONDS 12
|
2022-03-24 07:05:59 +00:00
|
|
|
#define INITIAL_CONNECT_TIMEOUT 100000
|
|
|
|
|
2020-06-15 14:18:57 +00:00
|
|
|
/**
|
|
|
|
* @fileoverview Remote test runner.
|
|
|
|
*
|
2021-08-07 20:22:35 +00:00
|
|
|
* We want to scp .com binaries to remote machines and run them. The
|
|
|
|
* problem is that SSH is the slowest thing imaginable, taking about
|
|
|
|
* 300ms to connect to a host that's merely half a millisecond away.
|
|
|
|
*
|
|
|
|
* This program takes 17ms using elliptic curve diffie hellman exchange
|
|
|
|
* where we favor a 32-byte binary preshared key (~/.runit.psk) instead
|
|
|
|
* of certificates. It's how long it takes to connect, copy the binary,
|
|
|
|
* and run it. The remote daemon is deployed via SSH if it's not there.
|
2020-06-15 14:18:57 +00:00
|
|
|
*
|
|
|
|
* o/default/tool/build/runit.com \
|
|
|
|
* o/default/tool/build/runitd.com \
|
|
|
|
* o/default/test/libc/alg/qsort_test.com \
|
|
|
|
* freebsd.test.:31337:22
|
|
|
|
*
|
2021-08-07 20:22:35 +00:00
|
|
|
* APE binaries are hermetic and embed dependent files within their zip
|
|
|
|
* structure, which is why all we need is this simple test runner tool.
|
2020-06-15 14:18:57 +00:00
|
|
|
* The only thing that needs to be configured is /etc/hosts or Bind, to
|
|
|
|
* assign numbers to the officially reserved canned names. For example:
|
|
|
|
*
|
|
|
|
* 192.168.0.10 windows.test. windows
|
|
|
|
* 192.168.0.11 freebsd.test. freebsd
|
|
|
|
* 192.168.0.12 openbsd.test. openbsd
|
|
|
|
*
|
|
|
|
* Life is easiest if SSH public key authentication is configured too.
|
|
|
|
* It can be tuned as follows in ~/.ssh/config:
|
|
|
|
*
|
|
|
|
* host windows.test.
|
|
|
|
* user testacct
|
|
|
|
* host freebsd.test.
|
|
|
|
* user testacct
|
|
|
|
* host openbsd.test.
|
|
|
|
* user testacct
|
|
|
|
*
|
|
|
|
* Firewalls may need to be configured as well, to allow port tcp:31337
|
|
|
|
* from the local subnet. For example:
|
|
|
|
*
|
|
|
|
* iptables -L -vn
|
|
|
|
* iptables -I INPUT 1 -s 10.0.0.0/8 -p tcp --dport 31337 -j ACCEPT
|
|
|
|
* iptables -I INPUT 1 -s 192.168.0.0/16 -p tcp --dport 31337 -j ACCEPT
|
|
|
|
*
|
2021-08-07 20:22:35 +00:00
|
|
|
* This tool may be used in zero trust environments.
|
2020-06-15 14:18:57 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
static const struct addrinfo kResolvHints = {.ai_family = AF_INET,
|
|
|
|
.ai_socktype = SOCK_STREAM,
|
|
|
|
.ai_protocol = IPPROTO_TCP};
|
|
|
|
|
|
|
|
int g_sock;
|
2020-09-03 12:44:37 +00:00
|
|
|
char *g_prog;
|
|
|
|
char *g_runitd;
|
2020-06-15 14:18:57 +00:00
|
|
|
jmp_buf g_jmpbuf;
|
2020-09-03 12:44:37 +00:00
|
|
|
uint16_t g_sshport;
|
|
|
|
char g_hostname[128];
|
2021-01-25 21:08:05 +00:00
|
|
|
uint16_t g_runitdport;
|
|
|
|
volatile bool alarmed;
|
2022-04-28 16:42:36 +00:00
|
|
|
char g_ssh[PATH_MAX];
|
2021-01-25 21:08:05 +00:00
|
|
|
|
2022-03-16 20:33:13 +00:00
|
|
|
int __sys_execve(const char *, char *const[], char *const[]) hidden;
|
|
|
|
|
2021-01-28 03:34:02 +00:00
|
|
|
static void OnAlarm(int sig) {
|
2021-01-25 21:08:05 +00:00
|
|
|
alarmed = true;
|
|
|
|
}
|
2020-06-15 14:18:57 +00:00
|
|
|
|
|
|
|
forceinline pureconst size_t GreatestTwoDivisor(size_t x) {
|
|
|
|
return x & (~x + 1);
|
|
|
|
}
|
|
|
|
|
2020-12-05 20:20:41 +00:00
|
|
|
wontreturn void ShowUsage(FILE *f, int rc) {
|
2020-06-15 14:18:57 +00:00
|
|
|
fprintf(f, "Usage: %s RUNITD PROGRAM HOSTNAME[:RUNITDPORT[:SSHPORT]]...\n",
|
|
|
|
program_invocation_name);
|
|
|
|
exit(rc);
|
|
|
|
unreachable;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckExists(const char *path) {
|
|
|
|
if (!isregularfile(path)) {
|
|
|
|
fprintf(stderr, "error: %s: not found or irregular\n", path);
|
|
|
|
ShowUsage(stderr, EX_USAGE);
|
|
|
|
unreachable;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Import C++ Standard Template Library
You can now use the hardest fastest and most dangerous language there is
with Cosmopolitan. So far about 75% of LLVM libcxx has been added. A few
breaking changes needed to be made to help this go smoothly.
- Rename nothrow to dontthrow
- Rename nodiscard to dontdiscard
- Add some libm functions, e.g. lgamma, nan, etc.
- Change intmax_t from int128 to int64 like everything else
- Introduce %jjd formatting directive for int128_t
- Introduce strtoi128(), strtou128(), etc.
- Rename bsrmax() to bsr128()
Some of the templates that should be working currently are std::vector,
std::string, std::map, std::set, std::deque, etc.
2022-03-22 12:51:41 +00:00
|
|
|
dontdiscard char *MakeDeployScript(struct addrinfo *remotenic,
|
|
|
|
size_t combytes) {
|
2020-06-15 14:18:57 +00:00
|
|
|
const char *ip4 = (const char *)&remotenic->ai_addr4->sin_addr;
|
2021-02-08 17:19:00 +00:00
|
|
|
return xasprintf("mkdir -p o/ && "
|
|
|
|
"dd bs=%zu count=%zu of=o/runitd.$$.com 2>/dev/null && "
|
|
|
|
"chmod +x o/runitd.$$.com && "
|
|
|
|
"o/runitd.$$.com -rdl%hhu.%hhu.%hhu.%hhu -p %hu && "
|
|
|
|
"rm -f o/runitd.$$.com",
|
2020-06-15 14:18:57 +00:00
|
|
|
GreatestTwoDivisor(combytes),
|
|
|
|
combytes ? combytes / GreatestTwoDivisor(combytes) : 0,
|
|
|
|
ip4[0], ip4[1], ip4[2], ip4[3], g_runitdport);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Upload(int pipe, int fd, struct stat *st) {
|
|
|
|
int64_t i;
|
|
|
|
for (i = 0; i < st->st_size;) {
|
|
|
|
CHECK_GT(splice(fd, &i, pipe, NULL, st->st_size - i, 0), 0);
|
|
|
|
}
|
|
|
|
CHECK_NE(-1, close(fd));
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeployEphemeralRunItDaemonRemotelyViaSsh(struct addrinfo *ai) {
|
2021-01-25 21:08:05 +00:00
|
|
|
int lock;
|
2020-06-15 14:18:57 +00:00
|
|
|
size_t got;
|
2021-01-25 21:08:05 +00:00
|
|
|
char *args[7];
|
2020-06-15 14:18:57 +00:00
|
|
|
struct stat st;
|
|
|
|
char linebuf[32];
|
2021-01-28 03:34:02 +00:00
|
|
|
sigset_t chldmask, savemask;
|
2021-01-25 21:08:05 +00:00
|
|
|
int sshpid, wstatus, binfd, pipefds[2][2];
|
2021-01-28 03:34:02 +00:00
|
|
|
struct sigaction ignore, saveint, savequit;
|
2022-04-20 16:56:53 +00:00
|
|
|
ignore.sa_flags = 0;
|
|
|
|
ignore.sa_handler = SIG_IGN;
|
|
|
|
sigemptyset(&ignore.sa_mask);
|
|
|
|
sigaction(SIGINT, &ignore, &saveint);
|
|
|
|
sigaction(SIGQUIT, &ignore, &savequit);
|
2021-01-25 21:08:05 +00:00
|
|
|
mkdir("o", 0755);
|
|
|
|
CHECK_NE(-1, (lock = open(gc(xasprintf("o/lock.%s", g_hostname)),
|
|
|
|
O_RDWR | O_CREAT, 0644)));
|
2022-03-16 20:33:13 +00:00
|
|
|
CHECK_NE(-1, fcntl(lock, F_SETLKW, &(struct flock){F_WRLCK}));
|
2022-04-20 16:56:53 +00:00
|
|
|
DEBUGF("ssh %s:%hu to spawn %s", g_hostname, g_runitdport, g_runitd);
|
|
|
|
CHECK_NE(-1, (binfd = open(g_runitd, O_RDONLY | O_CLOEXEC)));
|
|
|
|
CHECK_NE(-1, fstat(binfd, &st));
|
|
|
|
args[0] = "ssh";
|
|
|
|
args[1] = "-C";
|
|
|
|
args[2] = "-p";
|
|
|
|
args[3] = gc(xasprintf("%hu", g_sshport));
|
|
|
|
args[4] = g_hostname;
|
|
|
|
args[5] = gc(MakeDeployScript(ai, st.st_size));
|
|
|
|
args[6] = NULL;
|
|
|
|
sigemptyset(&chldmask);
|
|
|
|
sigaddset(&chldmask, SIGCHLD);
|
|
|
|
sigprocmask(SIG_BLOCK, &chldmask, &savemask);
|
|
|
|
CHECK_NE(-1, pipe2(pipefds[0], O_CLOEXEC));
|
|
|
|
CHECK_NE(-1, pipe2(pipefds[1], O_CLOEXEC));
|
|
|
|
CHECK_NE(-1, (sshpid = fork()));
|
|
|
|
if (!sshpid) {
|
|
|
|
sigaction(SIGINT, &(struct sigaction){0}, 0);
|
|
|
|
sigaction(SIGQUIT, &(struct sigaction){0}, 0);
|
|
|
|
sigprocmask(SIG_SETMASK, &savemask, 0);
|
|
|
|
dup2(pipefds[0][0], 0);
|
|
|
|
dup2(pipefds[1][1], 1);
|
|
|
|
execv(g_ssh, args);
|
|
|
|
_exit(127);
|
|
|
|
}
|
|
|
|
close(pipefds[0][0]);
|
|
|
|
close(pipefds[1][1]);
|
|
|
|
Upload(pipefds[0][1], binfd, &st);
|
|
|
|
LOGIFNEG1(close(pipefds[0][1]));
|
|
|
|
CHECK_NE(-1, (got = read(pipefds[1][0], linebuf, sizeof(linebuf))));
|
|
|
|
CHECK_GT(got, 0, "on host %s", g_hostname);
|
|
|
|
linebuf[sizeof(linebuf) - 1] = '\0';
|
|
|
|
if (strncmp(linebuf, "ready ", 6) != 0) {
|
|
|
|
FATALF("expected ready response but got %`'.*s", got, linebuf);
|
|
|
|
} else {
|
|
|
|
DEBUGF("got ready response");
|
|
|
|
}
|
|
|
|
g_runitdport = (uint16_t)atoi(&linebuf[6]);
|
|
|
|
LOGIFNEG1(close(pipefds[1][0]));
|
|
|
|
CHECK_NE(-1, waitpid(sshpid, &wstatus, 0));
|
|
|
|
LOGIFNEG1(sigprocmask(SIG_SETMASK, &savemask, NULL));
|
|
|
|
if (WIFEXITED(wstatus)) {
|
|
|
|
DEBUGF("ssh %s exited with %d", g_hostname, WEXITSTATUS(wstatus));
|
2021-01-25 21:08:05 +00:00
|
|
|
} else {
|
2022-04-20 16:56:53 +00:00
|
|
|
DEBUGF("ssh %s terminated with %s", g_hostname,
|
|
|
|
strsignal(WTERMSIG(wstatus)));
|
2020-06-15 14:18:57 +00:00
|
|
|
}
|
2022-04-20 16:56:53 +00:00
|
|
|
CHECK(WIFEXITED(wstatus) && !WEXITSTATUS(wstatus), "wstatus=%#x", wstatus);
|
|
|
|
LOGIFNEG1(fcntl(lock, F_SETLK, &(struct flock){F_UNLCK}));
|
|
|
|
sigaction(SIGINT, &saveint, 0);
|
|
|
|
sigaction(SIGQUIT, &savequit, 0);
|
|
|
|
close(lock);
|
2020-06-15 14:18:57 +00:00
|
|
|
}
|
|
|
|
|
2021-01-25 21:08:05 +00:00
|
|
|
void Connect(void) {
|
2020-06-15 14:18:57 +00:00
|
|
|
const char *ip4;
|
2021-01-25 21:08:05 +00:00
|
|
|
int rc, err, expo;
|
2022-03-24 07:05:59 +00:00
|
|
|
long double t1, t2;
|
2020-06-15 14:18:57 +00:00
|
|
|
struct addrinfo *ai;
|
|
|
|
if ((rc = getaddrinfo(g_hostname, gc(xasprintf("%hu", g_runitdport)),
|
|
|
|
&kResolvHints, &ai)) != 0) {
|
2021-03-21 03:48:40 +00:00
|
|
|
FATALF("%s:%hu: EAI_%s %m", g_hostname, g_runitdport, gai_strerror(rc));
|
2020-06-15 14:18:57 +00:00
|
|
|
unreachable;
|
|
|
|
}
|
2021-01-25 21:08:05 +00:00
|
|
|
ip4 = (const char *)&ai->ai_addr4->sin_addr;
|
2020-06-15 14:18:57 +00:00
|
|
|
if (ispublicip(ai->ai_family, &ai->ai_addr4->sin_addr)) {
|
|
|
|
FATALF("%s points to %hhu.%hhu.%hhu.%hhu"
|
|
|
|
" which isn't part of a local/private/testing subnet",
|
|
|
|
g_hostname, ip4[0], ip4[1], ip4[2], ip4[3]);
|
|
|
|
unreachable;
|
|
|
|
}
|
2022-03-24 07:05:59 +00:00
|
|
|
DEBUGF("connecting to %d.%d.%d.%d port %d", ip4[0], ip4[1], ip4[2], ip4[3],
|
|
|
|
ntohs(ai->ai_addr4->sin_port));
|
2021-01-25 21:08:05 +00:00
|
|
|
CHECK_NE(-1,
|
|
|
|
(g_sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)));
|
2022-03-24 07:05:59 +00:00
|
|
|
expo = INITIAL_CONNECT_TIMEOUT;
|
|
|
|
t1 = nowl();
|
|
|
|
LOGIFNEG1(sigaction(SIGALRM, &(struct sigaction){.sa_handler = OnAlarm}, 0));
|
2021-01-28 03:34:02 +00:00
|
|
|
Reconnect:
|
|
|
|
DEBUGF("connecting to %s (%hhu.%hhu.%hhu.%hhu) to run %s", g_hostname, ip4[0],
|
|
|
|
ip4[1], ip4[2], ip4[3], g_prog);
|
|
|
|
TryAgain:
|
2022-03-24 07:05:59 +00:00
|
|
|
alarmed = false;
|
|
|
|
LOGIFNEG1(setitimer(
|
|
|
|
ITIMER_REAL,
|
|
|
|
&(const struct itimerval){{0, 0}, {expo / 1000000, expo % 1000000}},
|
|
|
|
NULL));
|
2020-06-15 14:18:57 +00:00
|
|
|
rc = connect(g_sock, ai->ai_addr, ai->ai_addrlen);
|
2021-01-25 21:08:05 +00:00
|
|
|
err = errno;
|
2022-03-24 07:05:59 +00:00
|
|
|
t2 = nowl();
|
2020-06-15 14:18:57 +00:00
|
|
|
if (rc == -1) {
|
2022-03-24 07:05:59 +00:00
|
|
|
if (err == EINTR) {
|
|
|
|
expo *= 1.5;
|
|
|
|
if (t2 > t1 + MAX_WAIT_CONNECT_SECONDS) {
|
|
|
|
FATALF("timeout connecting to %s (%hhu.%hhu.%hhu.%hhu:%d)", g_hostname,
|
|
|
|
ip4[0], ip4[1], ip4[2], ip4[3], ntohs(ai->ai_addr4->sin_port));
|
|
|
|
unreachable;
|
|
|
|
}
|
|
|
|
goto TryAgain;
|
|
|
|
}
|
2021-01-28 03:34:02 +00:00
|
|
|
if (err == ECONNREFUSED || err == EHOSTUNREACH || err == ECONNRESET) {
|
|
|
|
DEBUGF("got %s from %s (%hhu.%hhu.%hhu.%hhu)", strerror(err), g_hostname,
|
|
|
|
ip4[0], ip4[1], ip4[2], ip4[3]);
|
2022-03-24 07:05:59 +00:00
|
|
|
setitimer(ITIMER_REAL, &(const struct itimerval){0}, 0);
|
2020-06-15 14:18:57 +00:00
|
|
|
DeployEphemeralRunItDaemonRemotelyViaSsh(ai);
|
2022-03-24 07:05:59 +00:00
|
|
|
if (t2 > t1 + MAX_WAIT_CONNECT_SECONDS) {
|
|
|
|
FATALF("timeout connecting to %s (%hhu.%hhu.%hhu.%hhu:%d)", g_hostname,
|
|
|
|
ip4[0], ip4[1], ip4[2], ip4[3], ntohs(ai->ai_addr4->sin_port));
|
|
|
|
unreachable;
|
|
|
|
}
|
|
|
|
usleep((expo *= 2));
|
2021-01-28 03:34:02 +00:00
|
|
|
goto Reconnect;
|
2020-06-15 14:18:57 +00:00
|
|
|
} else {
|
2021-01-25 21:08:05 +00:00
|
|
|
FATALF("%s(%s:%hu): %s", "connect", g_hostname, g_runitdport,
|
|
|
|
strerror(err));
|
2020-06-15 14:18:57 +00:00
|
|
|
unreachable;
|
|
|
|
}
|
2021-01-28 03:34:02 +00:00
|
|
|
} else {
|
|
|
|
DEBUGF("connected to %s", g_hostname);
|
2020-06-15 14:18:57 +00:00
|
|
|
}
|
2022-03-24 07:05:59 +00:00
|
|
|
setitimer(ITIMER_REAL, &(const struct itimerval){0}, 0);
|
2020-06-15 14:18:57 +00:00
|
|
|
freeaddrinfo(ai);
|
|
|
|
}
|
|
|
|
|
2022-05-15 06:17:22 +00:00
|
|
|
static void Send(const void *output, size_t outputsize) {
|
|
|
|
int rc, have;
|
|
|
|
static bool once;
|
|
|
|
static z_stream zs;
|
|
|
|
static char zbuf[4096];
|
|
|
|
if (!once) {
|
|
|
|
CHECK_EQ(Z_OK, deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
|
|
|
MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY));
|
|
|
|
once = true;
|
|
|
|
}
|
|
|
|
zs.next_in = output;
|
|
|
|
zs.avail_in = outputsize;
|
|
|
|
do {
|
|
|
|
zs.avail_out = sizeof(zbuf);
|
|
|
|
zs.next_out = (unsigned char *)zbuf;
|
|
|
|
rc = deflate(&zs, Z_SYNC_FLUSH);
|
|
|
|
CHECK_NE(Z_STREAM_ERROR, rc);
|
|
|
|
have = sizeof(zbuf) - zs.avail_out;
|
|
|
|
CHECK_EQ(have, mbedtls_ssl_write(&ezssl, zbuf, have));
|
|
|
|
} while (!zs.avail_out);
|
|
|
|
}
|
|
|
|
|
2020-06-15 14:18:57 +00:00
|
|
|
void SendRequest(void) {
|
|
|
|
int fd;
|
2021-08-07 20:22:35 +00:00
|
|
|
char *p;
|
|
|
|
size_t i;
|
|
|
|
ssize_t rc;
|
2021-08-26 04:35:58 +00:00
|
|
|
uint32_t crc;
|
2020-06-15 14:18:57 +00:00
|
|
|
struct stat st;
|
|
|
|
const char *name;
|
2021-08-26 04:35:58 +00:00
|
|
|
unsigned char *hdr, *q;
|
2020-06-15 14:18:57 +00:00
|
|
|
size_t progsize, namesize, hdrsize;
|
2022-05-15 06:17:22 +00:00
|
|
|
unsigned have;
|
2020-06-15 14:18:57 +00:00
|
|
|
CHECK_NE(-1, (fd = open(g_prog, O_RDONLY)));
|
|
|
|
CHECK_NE(-1, fstat(fd, &st));
|
2021-08-07 20:22:35 +00:00
|
|
|
CHECK_NE(MAP_FAILED, (p = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0)));
|
2020-06-15 14:18:57 +00:00
|
|
|
CHECK_LE((namesize = strlen((name = basename(g_prog)))), PATH_MAX);
|
|
|
|
CHECK_LE((progsize = st.st_size), INT_MAX);
|
2021-08-26 04:35:58 +00:00
|
|
|
CHECK_NOTNULL((hdr = gc(calloc(1, (hdrsize = 17 + namesize)))));
|
|
|
|
crc = crc32_z(0, p, st.st_size);
|
|
|
|
q = hdr;
|
|
|
|
q = WRITE32BE(q, RUNITD_MAGIC);
|
|
|
|
*q++ = kRunitExecute;
|
|
|
|
q = WRITE32BE(q, namesize);
|
|
|
|
q = WRITE32BE(q, progsize);
|
|
|
|
q = WRITE32BE(q, crc);
|
|
|
|
q = mempcpy(q, name, namesize);
|
|
|
|
assert(hdrsize == q - hdr);
|
2022-05-15 06:17:22 +00:00
|
|
|
DEBUGF("running %s on %s", g_prog, g_hostname);
|
|
|
|
Send(hdr, hdrsize);
|
|
|
|
Send(p, progsize);
|
2021-08-09 21:45:52 +00:00
|
|
|
CHECK_EQ(0, EzTlsFlush(&ezbio, 0, 0));
|
2021-08-07 20:22:35 +00:00
|
|
|
CHECK_NE(-1, munmap(p, st.st_size));
|
|
|
|
CHECK_NE(-1, close(fd));
|
2020-06-15 14:18:57 +00:00
|
|
|
}
|
|
|
|
|
2021-08-13 18:18:25 +00:00
|
|
|
bool Recv(unsigned char *p, size_t n) {
|
|
|
|
size_t i, rc;
|
|
|
|
static long backoff;
|
|
|
|
for (i = 0; i < n; i += rc) {
|
|
|
|
do {
|
|
|
|
rc = mbedtls_ssl_read(&ezssl, p + i, n - i);
|
|
|
|
} while (rc == MBEDTLS_ERR_SSL_WANT_READ);
|
|
|
|
if (!rc || rc == MBEDTLS_ERR_NET_CONN_RESET) {
|
|
|
|
usleep((backoff = (backoff + 1000) * 2));
|
|
|
|
return false;
|
|
|
|
} else if (rc < 0) {
|
2021-08-14 13:17:56 +00:00
|
|
|
TlsDie("read response failed", rc);
|
2021-08-13 18:18:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-06-15 14:18:57 +00:00
|
|
|
int ReadResponse(void) {
|
|
|
|
int res;
|
|
|
|
ssize_t rc;
|
|
|
|
size_t n, m;
|
2021-08-07 20:22:35 +00:00
|
|
|
uint32_t size;
|
2021-08-13 18:18:25 +00:00
|
|
|
unsigned char b[512];
|
|
|
|
for (res = -1; res == -1;) {
|
|
|
|
if (!Recv(b, 5)) break;
|
2021-09-28 05:58:51 +00:00
|
|
|
CHECK_EQ(RUNITD_MAGIC, READ32BE(b), "%#.5s", b);
|
2021-08-13 18:18:25 +00:00
|
|
|
switch (b[4]) {
|
|
|
|
case kRunitExit:
|
|
|
|
if (!Recv(b, 1)) break;
|
|
|
|
if ((res = *b)) {
|
|
|
|
WARNF("%s on %s exited with %d", g_prog, g_hostname, res);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kRunitStderr:
|
|
|
|
if (!Recv(b, 4)) break;
|
|
|
|
size = READ32BE(b);
|
|
|
|
for (; size; size -= n) {
|
|
|
|
n = MIN(size, sizeof(b));
|
|
|
|
if (!Recv(b, n)) goto drop;
|
|
|
|
CHECK_EQ(n, write(2, b, n));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "error: received invalid runit command\n");
|
|
|
|
_exit(1);
|
2021-01-25 21:08:05 +00:00
|
|
|
}
|
2020-06-15 14:18:57 +00:00
|
|
|
}
|
|
|
|
drop:
|
2021-08-13 18:18:25 +00:00
|
|
|
close(g_sock);
|
2020-06-15 14:18:57 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2022-03-16 20:33:13 +00:00
|
|
|
static inline bool IsElf(const char *p, size_t n) {
|
|
|
|
return n >= 4 && READ32LE(p) == READ32LE("\177ELF");
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool IsMachO(const char *p, size_t n) {
|
|
|
|
return n >= 4 && READ32LE(p) == 0xFEEDFACEu + 1;
|
|
|
|
}
|
|
|
|
|
2020-06-15 14:18:57 +00:00
|
|
|
int RunOnHost(char *spec) {
|
2021-01-25 21:08:05 +00:00
|
|
|
int rc;
|
2020-06-15 14:18:57 +00:00
|
|
|
char *p;
|
|
|
|
for (p = spec; *p; ++p) {
|
|
|
|
if (*p == ':') *p = ' ';
|
|
|
|
}
|
|
|
|
CHECK_GE(sscanf(spec, "%100s %hu %hu", g_hostname, &g_runitdport, &g_sshport),
|
|
|
|
1);
|
|
|
|
if (!strchr(g_hostname, '.')) strcat(g_hostname, ".test.");
|
2022-03-24 07:05:59 +00:00
|
|
|
DEBUGF("connecting to %s port %d", g_hostname, g_runitdport);
|
2021-01-25 21:08:05 +00:00
|
|
|
do {
|
2022-03-16 20:33:13 +00:00
|
|
|
for (;;) {
|
|
|
|
Connect();
|
|
|
|
EzFd(g_sock);
|
|
|
|
if (!EzHandshake2()) break;
|
|
|
|
WARNF("warning: got connection reset in handshake");
|
|
|
|
close(g_sock);
|
|
|
|
}
|
2021-01-25 21:08:05 +00:00
|
|
|
SendRequest();
|
2021-01-28 03:34:02 +00:00
|
|
|
} while ((rc = ReadResponse()) == -1);
|
2021-01-25 21:08:05 +00:00
|
|
|
return rc;
|
2020-06-15 14:18:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool IsParallelBuild(void) {
|
|
|
|
const char *makeflags;
|
|
|
|
return (makeflags = getenv("MAKEFLAGS")) && strstr(makeflags, "-j");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ShouldRunInParralel(void) {
|
|
|
|
return !IsWindows() && IsParallelBuild();
|
|
|
|
}
|
|
|
|
|
2022-03-16 20:33:13 +00:00
|
|
|
int SpawnSubprocesses(int argc, char *argv[]) {
|
2021-01-28 03:34:02 +00:00
|
|
|
sigset_t chldmask, savemask;
|
|
|
|
int i, rc, ws, pid, *pids, exitcode;
|
|
|
|
struct sigaction ignore, saveint, savequit;
|
2022-03-16 20:33:13 +00:00
|
|
|
char *args[5] = {argv[0], argv[1], argv[2]};
|
|
|
|
argc -= 3;
|
|
|
|
argv += 3;
|
|
|
|
pids = calloc(argc, sizeof(int));
|
2021-01-28 03:34:02 +00:00
|
|
|
ignore.sa_flags = 0;
|
|
|
|
ignore.sa_handler = SIG_IGN;
|
2022-04-20 16:56:53 +00:00
|
|
|
sigemptyset(&ignore.sa_mask);
|
|
|
|
sigaction(SIGINT, &ignore, &saveint);
|
|
|
|
sigaction(SIGQUIT, &ignore, &savequit);
|
|
|
|
sigemptyset(&chldmask);
|
|
|
|
sigaddset(&chldmask, SIGCHLD);
|
|
|
|
sigprocmask(SIG_BLOCK, &chldmask, &savemask);
|
2022-03-16 20:33:13 +00:00
|
|
|
for (i = 0; i < argc; ++i) {
|
|
|
|
args[3] = argv[i];
|
|
|
|
CHECK_NE(-1, (pids[i] = vfork()));
|
2021-01-28 03:34:02 +00:00
|
|
|
if (!pids[i]) {
|
2022-04-20 16:56:53 +00:00
|
|
|
sigaction(SIGINT, &(struct sigaction){0}, 0);
|
|
|
|
sigaction(SIGQUIT, &(struct sigaction){0}, 0);
|
2022-03-16 20:33:13 +00:00
|
|
|
sigprocmask(SIG_SETMASK, &savemask, 0);
|
|
|
|
execve(args[0], args, environ); /* for htop */
|
2022-04-20 16:56:53 +00:00
|
|
|
_Exit(127);
|
2020-06-15 14:18:57 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-28 03:34:02 +00:00
|
|
|
for (exitcode = 0;;) {
|
|
|
|
if ((pid = wait(&ws)) == -1) {
|
|
|
|
if (errno == EINTR) continue;
|
|
|
|
if (errno == ECHILD) break;
|
|
|
|
FATALF("wait failed");
|
2020-06-15 14:18:57 +00:00
|
|
|
}
|
2022-03-16 20:33:13 +00:00
|
|
|
for (i = 0; i < argc; ++i) {
|
2021-01-28 03:34:02 +00:00
|
|
|
if (pids[i] != pid) continue;
|
|
|
|
if (WIFEXITED(ws)) {
|
2022-03-16 20:33:13 +00:00
|
|
|
if (WEXITSTATUS(ws)) {
|
|
|
|
INFOF("%s exited with %d", argv[i], WEXITSTATUS(ws));
|
|
|
|
} else {
|
|
|
|
DEBUGF("%s exited with %d", argv[i], WEXITSTATUS(ws));
|
|
|
|
}
|
2021-01-28 03:34:02 +00:00
|
|
|
if (!exitcode) exitcode = WEXITSTATUS(ws);
|
|
|
|
} else {
|
2022-03-16 20:33:13 +00:00
|
|
|
INFOF("%s terminated with %s", argv[i], strsignal(WTERMSIG(ws)));
|
2021-01-28 03:34:02 +00:00
|
|
|
if (!exitcode) exitcode = 128 + WTERMSIG(ws);
|
|
|
|
}
|
|
|
|
break;
|
2020-06-15 14:18:57 +00:00
|
|
|
}
|
|
|
|
}
|
2022-04-20 16:56:53 +00:00
|
|
|
sigprocmask(SIG_SETMASK, &savemask, 0);
|
|
|
|
sigaction(SIGQUIT, &savequit, 0);
|
|
|
|
sigaction(SIGINT, &saveint, 0);
|
2021-01-28 03:34:02 +00:00
|
|
|
free(pids);
|
2020-06-15 14:18:57 +00:00
|
|
|
return exitcode;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
2021-08-26 04:35:58 +00:00
|
|
|
ShowCrashReports();
|
2022-03-24 07:05:59 +00:00
|
|
|
if (getenv("DEBUG")) {
|
|
|
|
__log_level = kLogDebug;
|
|
|
|
}
|
2020-06-15 14:18:57 +00:00
|
|
|
if (argc > 1 &&
|
|
|
|
(strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
|
|
|
|
ShowUsage(stdout, 0);
|
|
|
|
unreachable;
|
|
|
|
}
|
2022-03-16 20:33:13 +00:00
|
|
|
if (argc < 3) {
|
|
|
|
ShowUsage(stderr, EX_USAGE);
|
|
|
|
unreachable;
|
|
|
|
}
|
2020-06-15 14:18:57 +00:00
|
|
|
CheckExists((g_runitd = argv[1]));
|
|
|
|
CheckExists((g_prog = argv[2]));
|
2022-04-24 16:59:22 +00:00
|
|
|
CHECK_NOTNULL(
|
|
|
|
commandv(firstnonnull(getenv("SSH"), "ssh"), g_ssh, sizeof(g_ssh)));
|
2022-03-16 20:33:13 +00:00
|
|
|
if (argc == 3) {
|
|
|
|
/* hosts list empty */
|
|
|
|
return 0;
|
|
|
|
} else if (argc == 4) {
|
|
|
|
/* single host */
|
|
|
|
SetupPresharedKeySsl(MBEDTLS_SSL_IS_CLIENT, GetRunitPsk());
|
|
|
|
g_sshport = 22;
|
|
|
|
g_runitdport = RUNITD_PORT;
|
|
|
|
return RunOnHost(argv[3]);
|
|
|
|
} else {
|
|
|
|
/* multiple hosts */
|
|
|
|
return SpawnSubprocesses(argc, argv);
|
|
|
|
}
|
2020-06-15 14:18:57 +00:00
|
|
|
}
|