#!/usr/bin/env node 'use strict'; const constants = require('./src/constants.js'), fs = require('node:fs'), ldapServer = require('./src/ldapserver.js'), net = require('node:net'), oidcServer = require('./src/oidcserver.js'), paths = require('./src/paths.js'), proxyAuth = require('./src/proxyauth.js'), safe = require('safetydance'), server = require('./src/server.js'), directoryServer = require('./src/directoryserver.js'); let logFd; async function setupLogging() { if (constants.TEST) return; logFd = fs.openSync(paths.BOX_LOG_FILE, 'a'); // we used to write using a stream before but it caches internally and there is no way to flush it when things crash process.stdout.write = process.stderr.write = function (...args) { const callback = typeof args[args.length-1] === 'function' ? args.pop() : function () {}; // callback is required for fs.write fs.write.apply(fs, [logFd, ...args, callback]); }; } // happy eyeballs workaround. when there is no ipv6, nodejs timesout prematurely since the default for ipv4 is just 250ms // https://github.com/nodejs/node/issues/54359 async function setupNetworking() { net.setDefaultAutoSelectFamilyAttemptTimeout(2500); } // this is also used as the 'uncaughtException' handler which can only have synchronous functions function exitSync(status) { const ts = new Date().toISOString(); if (status.message) fs.write(logFd, `${ts} ${status.message}\n`, function () {}); const msg = status.error.stack.replace(/\n/g, `\n${ts} `); // prefix each line with ts if (status.error) fs.write(logFd, `${ts} ${msg}\n`, function () {}); fs.fsyncSync(logFd); fs.closeSync(logFd); process.exit(status.code); } async function startServers() { await setupLogging(); await setupNetworking(); await server.start(); // do this first since it also inits the database await proxyAuth.start(); await ldapServer.start(); const conf = await directoryServer.getConfig(); if (conf.enabled) await directoryServer.start(); } async function main() { const [error] = await safe(startServers()); if (error) return exitSync({ error, code: 1, message: 'Error starting servers' }); // require this here so that logging handler is already setup const debug = require('debug')('box:box'); process.on('SIGHUP', async function () { debug('Received SIGHUP. Re-reading configs.'); const conf = await directoryServer.getConfig(); if (conf.enabled) await directoryServer.checkCertificate(); }); process.on('SIGINT', async function () { debug('Received SIGINT. Shutting down.'); await proxyAuth.stop(); await server.stop(); await directoryServer.stop(); await ldapServer.stop(); await oidcServer.stop(); setTimeout(() => { debug('Shutdown complete'); process.exit(); }, 2000); // need to wait for the task processes to die }); process.on('SIGTERM', async function () { debug('Received SIGTERM. Shutting down.'); await proxyAuth.stop(); await server.stop(); await directoryServer.stop(); await ldapServer.stop(); await oidcServer.stop(); setTimeout(() => { debug('Shutdown complete'); process.exit(); }, 2000); // need to wait for the task processes to die }); process.on('uncaughtException', (error) => exitSync({ error, code: 1, message: 'From uncaughtException handler.' })); } main();