urbit/vere/main.c
Joe Bryan 6c36594f6f Merge branch 'master' into libh2o
* master: (124 commits)
  use compile-time assertion to check for unsigned char
  Don't build libuv in travis if it is now submoduled.
  Rebuild the scripts without PKG_CONFIG_PATH hacks.
  Remove manual PKG_CONFIG_PATH configuration.
  Enable fallback libuv bundle
  Bundle libuv.
  adds a cell check for the sample of the +roll gate
  Add missing flags fixing the markdown parser bug #951
  Revert "Merge pull request #952 from eglaysher/revert-meson"
  Revert "Merge pull request #949 from eglaysher/meson-build-clean"
  Revert "The -C flag was added for exactly this case."
  The -C flag was added for exactly this case.
  Take ownership of the submodule repositories.
  Revert "Revert "Merge pull request #941 from frodwith/runtime-overflows""
  Add legacy meson instructions to README
  Fix new meson version check
  Fix legacy meson version detection.
  Universal meson build.
  change overflow check to short
  do the don trick for jam
  ...
2018-04-09 19:49:40 -07:00

655 lines
16 KiB
C

/* v/main.c
**
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <setjmp.h>
#include <signal.h>
#include <gmp.h>
#include <stdint.h>
#include <limits.h>
#include <uv.h>
#include <sigsegv.h>
#include <curses.h>
#include <termios.h>
#include <term.h>
#include <dirent.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include "h2o.h"
#define U3_GLOBAL
#define C3_GLOBAL
#include "all.h"
#include "vere/vere.h"
/* Require unsigned char
*/
STATIC_ASSERT(( 0 == CHAR_MIN && UCHAR_MAX == CHAR_MAX ), "unsigned char required");
/* _main_readw(): parse a word from a string.
*/
static u3_noun
_main_readw(const c3_c* str_c, c3_w max_w, c3_w* out_w)
{
c3_c* end_c;
c3_w par_w = strtoul(str_c, &end_c, 0);
if ( *str_c != '\0' && *end_c == '\0' && par_w < max_w ) {
*out_w = par_w;
return c3y;
}
else return c3n;
}
static c3_c hostbuf[2048]; // kill me
/* _main_presig(): prefix optional sig.
*/
c3_c*
_main_presig(c3_c* txt_c)
{
c3_c* new_c = malloc(2 + strlen(txt_c));
if ( '~' == *txt_c ) {
strcpy(new_c, txt_c);
} else {
new_c[0] = '~';
strcpy(new_c + 1, txt_c);
}
return new_c;
}
/* _main_getopt(): extract option map from command line.
*/
static u3_noun
_main_getopt(c3_i argc, c3_c** argv)
{
c3_i ch_i;
c3_w arg_w;
u3_Host.ops_u.abo = c3n;
u3_Host.ops_u.bat = c3n;
u3_Host.ops_u.dem = c3n;
u3_Host.ops_u.dry = c3n;
u3_Host.ops_u.fak = c3n;
u3_Host.ops_u.fog = c3n;
u3_Host.ops_u.gab = c3n;
u3_Host.ops_u.git = c3n;
u3_Host.ops_u.net = c3n;
u3_Host.ops_u.mem = c3n;
u3_Host.ops_u.nuu = c3n;
u3_Host.ops_u.pro = c3n;
u3_Host.ops_u.qui = c3n;
u3_Host.ops_u.rep = c3n;
u3_Host.ops_u.tex = c3n;
u3_Host.ops_u.veb = c3n;
u3_Host.ops_u.kno_w = DefaultKernel;
while ( (ch_i=getopt(argc, argv,"G:B:A:H:I:w:u:t:f:k:l:n:p:r:NabcdgqsvxFMPDXR")) != -1 ) {
switch ( ch_i ) {
case 'M': {
u3_Host.ops_u.mem = c3y;
break;
}
case 'B': {
u3_Host.ops_u.pil_c = strdup(optarg);
break;
}
case 'G': {
u3_Host.ops_u.gen_c = strdup(optarg);
break;
}
case 'A': {
u3_Host.ops_u.arv_c = strdup(optarg);
break;
}
case 'H': {
u3_Host.ops_u.dns_c = strdup(optarg);
break;
}
case 'I': {
u3_Host.ops_u.imp_c = _main_presig(optarg);
break;
}
case 'w': {
u3_Host.ops_u.who_c = _main_presig(optarg);
u3_Host.ops_u.nuu = c3y;
break;
}
case 'u': {
u3_Host.ops_u.url_c = strdup(optarg);
break;
}
case 't': {
u3_Host.ops_u.tic_c = _main_presig(optarg);
break;
}
case 'x': {
u3_Host.ops_u.tex = c3y;
break;
}
case 'X': {
u3_Host.ops_u.fog = c3y;
break;
}
case 'f': {
if ( c3n == _main_readw(optarg, 100, &u3_Host.ops_u.fuz_w) ) {
return c3n;
}
break;
}
case 'k': {
if ( c3n == _main_readw(optarg, 256, &u3_Host.ops_u.kno_w) ) {
return c3n;
}
break;
}
case 'l': {
if ( c3n == _main_readw(optarg, 65536, &arg_w) ) {
return c3n;
} else u3_Host.ops_u.rop_s = arg_w;
break;
}
case 'n': {
u3_Host.ops_u.nam_c = strdup(optarg);
break;
}
case 'p': {
if ( c3n == _main_readw(optarg, 65536, &arg_w) ) {
return c3n;
} else u3_Host.ops_u.por_s = arg_w;
break;
}
case 'r': {
u3_Host.ops_u.raf_c = strdup(optarg);
break;
}
case 'R': {
u3_Host.ops_u.rep = c3y;
return c3y;
}
case 'N': { u3_Host.ops_u.net = c3y; break; }
case 'F': { u3_Host.ops_u.fak = c3y; break; }
case 'a': { u3_Host.ops_u.abo = c3y; break; }
case 'b': { u3_Host.ops_u.bat = c3y; break; }
case 'c': { u3_Host.ops_u.nuu = c3y; break; }
case 'd': { u3_Host.ops_u.dem = c3y; break; }
case 'g': { u3_Host.ops_u.gab = c3y; break; }
case 'P': { u3_Host.ops_u.pro = c3y; break; }
case 'D': { u3_Host.ops_u.dry = c3y; break; }
case 'q': { u3_Host.ops_u.qui = c3y; break; }
case 'v': { u3_Host.ops_u.veb = c3y; break; }
case 's': { u3_Host.ops_u.git = c3y; break; }
case '?': default: {
return c3n;
}
}
}
if ( u3_Host.ops_u.fak == c3n && u3_Host.ops_u.net == c3y ) {
fprintf(stderr, "-N only makes sense with -F\n");
return c3n;
} else if ( u3_Host.ops_u.fak == c3n && u3_Host.ops_u.net == c3n ) {
u3_Host.ops_u.net = c3y; /* remote networking is always on in real mode. */
}
if ( u3_Host.ops_u.arv_c != 0 && ( u3_Host.ops_u.imp_c == 0 ||
u3_Host.ops_u.nuu == c3n ) ) {
fprintf(stderr, "-A only makes sense when creating a new galaxy\n");
return c3n;
}
if ( u3_Host.ops_u.imp_c != 0 &&
u3_Host.ops_u.arv_c == 0 &&
u3_Host.ops_u.nuu == c3y ) {
fprintf(stderr, "can't create a new galaxy without specifying "
"the initial sync path with -A\n");
return c3n;
}
if ( u3_Host.ops_u.gen_c != 0 && ( u3_Host.ops_u.imp_c == 0 ||
u3_Host.ops_u.nuu == c3n ) ) {
fprintf(stderr, "-G only makes sense when creating a new galaxy\n");
return c3n;
}
if ( u3_Host.ops_u.tic_c != 0 && ( u3_Host.ops_u.imp_c != 0 ||
u3_Host.ops_u.nuu == c3n ) ) {
fprintf(stderr, "-t only makes sense when creating a new non-galaxy\n");
return c3n;
}
if ( u3_Host.ops_u.rop_s == 0 && u3_Host.ops_u.raf_c != 0 ) {
fprintf(stderr, "The -r flag requires -l.\n");
return c3n;
}
if ( u3_Host.ops_u.tic_c == 0 && u3_Host.ops_u.who_c != 0 ) {
c3_c tic_c[29];
printf("your ticket: ~");
scanf("%28s",tic_c);
u3_Host.ops_u.tic_c = _main_presig(tic_c);
}
if ( c3y == u3_Host.ops_u.bat ) {
u3_Host.ops_u.dem = c3y;
u3_Host.ops_u.nuu = c3y;
}
if ( u3_Host.ops_u.nuu != c3y && u3_Host.ops_u.pil_c != 0) {
fprintf(stderr, "-B only makes sense when bootstrapping a new instance\n");
return c3n;
}
if ( u3_Host.ops_u.nuu != c3y && u3_Host.ops_u.url_c != 0 ) {
fprintf(stderr, "-u only makes sense when bootstrapping a new instance\n");
return c3n;
} else if ( u3_Host.ops_u.nuu == c3y
&& u3_Host.ops_u.url_c == 0
&& u3_Host.ops_u.git == c3n ) {
u3_Host.ops_u.url_c = "https://bootstrap.urbit.org/latest.pill";
} else if ( u3_Host.ops_u.nuu == c3y
&& u3_Host.ops_u.url_c == 0
&& u3_Host.ops_u.arv_c == 0 ) {
fprintf(stderr, "-s only makes sense with -A\n");
return c3n;
}
if ( u3_Host.ops_u.dns_c == 0 ) {
u3_Host.ops_u.dns_c = "urbit.org";
}
if ( u3_Host.ops_u.pil_c != 0 ) {
struct stat s;
if ( stat(u3_Host.ops_u.pil_c, &s) != 0 ) {
fprintf(stderr, "pill %s not found\n", u3_Host.ops_u.pil_c);
return c3n;
}
}
if ( u3_Host.ops_u.nam_c == 0 ) {
u3_Host.ops_u.nam_c = getenv("HOSTNAME");
if ( u3_Host.ops_u.nam_c == 0 ) {
c3_w len_w = sysconf(_SC_HOST_NAME_MAX) + 1;
u3_Host.ops_u.nam_c = hostbuf;
if ( 0 != gethostname(u3_Host.ops_u.nam_c, len_w) ) {
perror("gethostname");
exit(1);
}
}
}
if ( argc != (optind + 1) && u3_Host.ops_u.who_c != 0 ) {
u3_Host.dir_c = strdup(1 + u3_Host.ops_u.who_c);
}
if ( argc != (optind + 1) ) {
return u3_Host.dir_c ? c3y : c3n;
} else {
{
c3_c* ash_c;
if ( (ash_c = strrchr(argv[optind], '/')) && (ash_c[1] == 0) ) {
*ash_c = 0;
}
}
u3_Host.dir_c = strdup(argv[optind]);
return c3y;
}
}
/* u3_ve_usage(): print usage and exit.
*/
static void
u3_ve_usage(c3_i argc, c3_c** argv)
{
c3_c *use_c[] = {
"Urbit: a personal server operating function\n",
"https://urbit.org\n",
"Version " URBIT_VERSION "\n",
"\n",
"Usage: %s [options...] ship_name\n",
"where ship_name is a @p phonetic representation of an urbit address\n",
"without the leading '~', and options is some subset of the following:\n",
"\n",
"-A dir Use dir for initial galaxy sync\n",
"-b Batch create\n",
"-B pill Bootstrap from this pill\n",
"-c pier Create a new urbit in pier/\n",
"-d Daemon mode\n",
"-D Recompute from events\n",
"-F Fake keys; also disables networking\n",
"-f Fuzz testing\n",
"-g Set GC flag\n",
"-H domain Set ames bootstrap domain (default urbit.org)\n",
"-I galaxy Start as ~galaxy\n",
"-k stage Start at Hoon kernel version stage\n",
"-l port Initial peer port\n",
"-M Memory madness\n",
"-n host Set unix hostname\n",
"-N Enable networking in fake mode (-F)\n",
"-p ames_port Set the HTTP port to bind to\n",
"-P Profiling\n",
"-q Quiet\n",
"-r host Initial peer address\n",
"-R Report urbit build info\n",
"-s Pill URL from arvo git hash\n",
"-t ticket Use ~ticket automatically\n",
"-u url URL from which to download pill\n",
"-v Verbose\n",
"-w name Immediately upgrade to ~name\n",
"-x Exit immediately\n",
"-Xwtf Skip last event\n",
"\n",
"Development Usage:\n",
" To create a development ship, use a fakezod:\n",
" %s -FI zod -A /path/to/arvo/folder -B /path/to/pill -c zod\n",
"\n",
" For more information about developing on urbit, see:\n",
" https://github.com/urbit/urbit/blob/master/CONTRIBUTING.md\n",
"\n",
"Simple Usage: \n",
" %s -c <mycomet> to create a comet (anonymous urbit)\n",
" %s -w <myplanet> -t <myticket> if you have a ticket\n",
" %s <myplanet or mycomet> to restart an existing urbit\n",
0
};
c3_i i;
for ( i=0; use_c[i]; i++ ) {
fprintf(stderr, use_c[i], argv[0]);
}
exit(1);
}
#if 0
/* u3_ve_panic(): panic and exit.
*/
static void
u3_ve_panic(c3_i argc, c3_c** argv)
{
fprintf(stderr, "%s: gross system failure\n", argv[0]);
exit(1);
}
#endif
/* u3_ve_sysopt(): apply option map to system state.
*/
static void
u3_ve_sysopt()
{
u3_Local = strdup(u3_Host.dir_c);
}
#if 0
static jmp_buf Signal_buf;
#ifndef SIGSTKSZ
# define SIGSTKSZ 16384
#endif
static uint8_t Sigstk[SIGSTKSZ];
volatile enum { sig_none, sig_overflow, sig_interrupt } Sigcause;
static void _main_cont(void *arg1, void *arg2, void *arg3)
{
(void)(arg1);
(void)(arg2);
(void)(arg3);
siglongjmp(Signal_buf, 1);
}
static void
overflow_handler(int emergency, stackoverflow_context_t scp)
{
if ( 1 == emergency ) {
write(2, "stack emergency\n", strlen("stack emergency" + 2));
exit(1);
} else {
Sigcause = sig_overflow;
sigsegv_leave_handler(_main_cont, NULL, NULL, NULL);
}
}
// Install signal handlers and set buffers.
//
// Note that we use the sigmask-restoring variant. Essentially, when
// we get a signal, we force the system back into the just-booted state.
// If anything goes wrong during boot (above), it's curtains.
{
if ( 0 != sigsetjmp(Signal_buf, 1) ) {
switch ( Sigcause ) {
case sig_overflow: printf("[stack overflow]\r\n"); break;
case sig_interrupt: printf("[interrupt]\r\n"); break;
default: printf("[signal error!]\r\n"); break;
}
Sigcause = sig_none;
signal(SIGINT, SIG_DFL);
stackoverflow_deinstall_handler();
// Print the trace, do a GC, etc.
//
// This is half-assed at present, so we exit.
//
u3_lo_sway(0, u3k(u3_wire_tax(u3_Wire)));
u3_lo_bail(u3A);
exit(1);
}
if ( -1 == stackoverflow_install_handler
(overflow_handler, Sigstk, SIGSTKSZ) )
{
fprintf(stderr, "overflow_handler: install failed\n");
exit(1);
}
signal(SIGINT, interrupt_handler);
signal(SIGIO, SIG_IGN);
}
static void
interrupt_handler(int x)
{
Sigcause = sig_interrupt;
longjmp(Signal_buf, 1);
}
#endif
#define GRAB
static void
report(void)
{
printf("---------\nLibraries\n---------\n");
printf("gmp: %s\n", gmp_version);
printf("sigsegv: %d.%d\n", (libsigsegv_version >> 8) & 0xff, libsigsegv_version & 0xff);
printf("openssl: %s\n", SSLeay_version(SSLEAY_VERSION));
printf("curses: %s\n", curses_version());
printf("libuv: %s\n", uv_version_string());
printf("libh2o: %d.%d.%d\n", H2O_LIBRARY_VERSION_MAJOR, H2O_LIBRARY_VERSION_MINOR, H2O_LIBRARY_VERSION_PATCH);
}
void
_stop_exit(c3_i int_i)
{
fprintf(stderr, "\r\n[received keyboard stop signal, exiting]\r\n");
u3_lo_bail();
}
c3_i
main(c3_i argc,
c3_c** argv)
{
// Parse options.
//
if ( c3n == _main_getopt(argc, argv) ) {
u3_ve_usage(argc, argv);
return 1;
}
if ( c3y == u3_Host.ops_u.rep ) {
report();
return 0;
}
if ( c3y == u3_Host.ops_u.nuu ) {
struct stat s;
if ( !stat(u3_Host.dir_c, &s) ) {
fprintf(stderr, "tried to create, but %s already exists\n", u3_Host.dir_c);
fprintf(stderr, "normal usage: %s %s\n", argv[0], u3_Host.dir_c);
exit(1);
}
} else {
struct stat s;
if ( -1 == stat(u3_Host.dir_c, &s) ) {
fprintf(stderr, "%s: urbit not found\n", u3_Host.dir_c);
u3_ve_usage(argc, argv);
}
}
#if 0
if ( 0 == getuid() ) {
chroot(u3_Host.dir_c);
u3_Host.dir_c = "/";
}
#endif
u3_ve_sysopt();
// Block profiling signal, which should be delievered to exactly one thread.
//
if ( _(u3_Host.ops_u.pro) ) {
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGPROF);
if ( 0 != pthread_sigmask(SIG_BLOCK, &set, NULL) ) {
perror("pthread_sigmask");
exit(1);
}
}
// Handle SIGTSTP as if it was SIGTERM.
//
signal(SIGTSTP, _stop_exit);
printf("~\n");
// printf("welcome.\n");
printf("urbit %s\n", URBIT_VERSION);
printf("urbit: home is %s\n", u3_Host.dir_c);
// printf("vere: hostname is %s\n", u3_Host.ops_u.nam_c);
if ( c3y == u3_Host.ops_u.dem && c3n == u3_Host.ops_u.bat ) {
printf("urbit: running as daemon\n");
}
// Seed prng. Don't panic -- just for fuzz testing.
//
srand(getpid());
// Instantiate process globals.
{
/* Boot the image and checkpoint. Set flags.
*/
{
/* Set pier directory.
*/
u3C.dir_c = u3_Host.dir_c;
/* Set GC flag.
*/
if ( _(u3_Host.ops_u.gab) ) {
u3C.wag_w |= u3o_debug_ram;
}
/* Set profile flag.
*/
if ( _(u3_Host.ops_u.pro) ) {
u3C.wag_w |= u3o_debug_cpu;
}
/* Set verbose flag.
*/
if ( _(u3_Host.ops_u.veb) ) {
u3C.wag_w |= u3o_verbose;
}
/* Set quiet flag.
*/
if ( _(u3_Host.ops_u.qui) ) {
u3C.wag_w |= u3o_quiet;
}
/* Set dry-run flag.
*/
if ( _(u3_Host.ops_u.dry) ) {
u3C.wag_w |= u3o_dryrun;
}
}
u3m_boot(u3_Host.ops_u.nuu,
u3_Host.ops_u.gab,
u3_Host.dir_c,
u3_Host.ops_u.pil_c,
u3_Host.ops_u.url_c,
u3_Host.ops_u.arv_c);
/* Start Arvo.
*/
#if 1
{
struct timeval tim_tv;
u3_noun now;
gettimeofday(&tim_tv, 0);
now = u3_time_in_tv(&tim_tv);
u3v_start(now);
}
#endif
#if 0
/* Initial checkpoint.
*/
if ( _(u3_Host.ops_u.nuu) ) {
printf("about to save.\r\n");
u3e_save();
printf("saved.\r\n");
}
#endif
}
SSL_library_init();
SSL_load_error_strings();
{
c3_i rad;
c3_y buf[4096];
// RAND_status, at least on OS X, never returns true.
// 4096 bytes should be enough entropy for anyone, right?
rad = open("/dev/urandom", O_RDONLY);
if ( 4096 != read(rad, &buf, 4096) ) {
perror("rand-seed");
exit(1);
}
RAND_seed(buf, 4096);
close(rad);
}
// u3e_grab("main", u3_none);
//
u3_lo_loop();
return 0;
}