Using systemd's `Delegate=` option, it is possible to get it to let you manage your own cgroup subtree, therefore allowing you to set options and other fun stuff. This commit adds support for doing so and configuring the resource limits in config.toml. For later: The cgroup created has to be a threaded one. Iin theory, we can actually wait for the QEMU process to handshake qmp, grab the vCPU threads, and only limit those. For now, just limiting the entire QEMU process works, though and is the least complicated. NOTE: Windows support should still work, even if you have resource limits configured. If you do, it should only warn and complain, but still function.
115 lines
3.1 KiB
TypeScript
115 lines
3.1 KiB
TypeScript
import * as toml from 'toml';
|
|
import IConfig from './IConfig.js';
|
|
import * as fs from 'fs';
|
|
import CollabVMServer from './CollabVMServer.js';
|
|
|
|
import { QemuVmDefinition } from '@computernewb/superqemu';
|
|
|
|
import AuthManager from './AuthManager.js';
|
|
import WSServer from './WebSocket/WSServer.js';
|
|
import { User } from './User.js';
|
|
import TCPServer from './TCP/TCPServer.js';
|
|
import VM from './vm/interface.js';
|
|
import VNCVM from './vm/vnc/VNCVM.js';
|
|
import GeoIPDownloader from './GeoIPDownloader.js';
|
|
import pino from 'pino';
|
|
import { Database } from './Database.js';
|
|
import { BanManager } from './BanManager.js';
|
|
import { QemuVMShim } from './vm/qemu.js';
|
|
|
|
let logger = pino();
|
|
|
|
logger.info('CollabVM Server starting up');
|
|
|
|
// Parse the config file
|
|
|
|
let Config: IConfig;
|
|
|
|
if (!fs.existsSync('config.toml')) {
|
|
logger.error('Fatal error: Config.toml not found. Please copy config.example.toml and fill out fields');
|
|
process.exit(1);
|
|
}
|
|
try {
|
|
var configRaw = fs.readFileSync('config.toml').toString();
|
|
Config = toml.parse(configRaw);
|
|
} catch (e) {
|
|
logger.error({err: e}, 'Fatal error: Failed to read or parse the config file');
|
|
process.exit(1);
|
|
}
|
|
|
|
let exiting = false;
|
|
let VM: VM;
|
|
|
|
async function stop() {
|
|
if (exiting) return;
|
|
exiting = true;
|
|
await VM.Stop();
|
|
process.exit(0);
|
|
}
|
|
|
|
async function start() {
|
|
let geoipReader = null;
|
|
if (Config.geoip.enabled) {
|
|
let downloader = new GeoIPDownloader(Config.geoip.directory, Config.geoip.accountID, Config.geoip.licenseKey);
|
|
geoipReader = await downloader.getGeoIPReader();
|
|
}
|
|
// Init the auth manager if enabled
|
|
let auth = Config.auth.enabled ? new AuthManager(Config.auth.apiEndpoint, Config.auth.secretKey) : null;
|
|
// Database and ban manager
|
|
if (Config.bans.cvmban && !Config.mysql.enabled) {
|
|
logger.error('MySQL must be configured to use cvmban.');
|
|
process.exit(1);
|
|
}
|
|
if (!Config.bans.cvmban && !Config.bans.bancmd) {
|
|
logger.warn('Neither cvmban nor ban command are configured. Bans will not function.');
|
|
}
|
|
let db = undefined;
|
|
if (Config.mysql.enabled) {
|
|
db = new Database(Config.mysql);
|
|
await db.init();
|
|
}
|
|
let banmgr = new BanManager(Config.bans, db);
|
|
switch (Config.vm.type) {
|
|
case 'qemu': {
|
|
// Fire up the VM
|
|
let def: QemuVmDefinition = {
|
|
id: Config.collabvm.node,
|
|
command: Config.qemu.qemuArgs,
|
|
snapshot: Config.qemu.snapshots,
|
|
forceTcp: false,
|
|
vncHost: '127.0.0.1',
|
|
vncPort: Config.qemu.vncPort,
|
|
};
|
|
|
|
VM = new QemuVMShim(def, Config.qemu.resourceLimits);
|
|
break;
|
|
}
|
|
case 'vncvm': {
|
|
VM = new VNCVM(Config.vncvm);
|
|
break;
|
|
}
|
|
default: {
|
|
logger.error(`Invalid VM type in config: ${Config.vm.type}`);
|
|
process.exit(1);
|
|
return;
|
|
}
|
|
}
|
|
process.on('SIGINT', async () => await stop());
|
|
process.on('SIGTERM', async () => await stop());
|
|
|
|
await VM.Start();
|
|
// Start up the server
|
|
var CVM = new CollabVMServer(Config, VM, banmgr, auth, geoipReader);
|
|
|
|
var WS = new WSServer(Config, banmgr);
|
|
WS.on('connect', (client: User) => CVM.addUser(client));
|
|
WS.start();
|
|
|
|
if (Config.tcp.enabled) {
|
|
var TCP = new TCPServer(Config, banmgr);
|
|
TCP.on('connect', (client: User) => CVM.addUser(client));
|
|
TCP.start();
|
|
}
|
|
}
|
|
start();
|