UNPKG

camera.ui

Version:
216 lines 8.39 kB
import { sleep } from '@camera.ui/common/utils'; import { outputFile } from 'fs-extra/esm'; import { execSync } from 'node:child_process'; import { existsSync, unlinkSync } from 'node:fs'; import { chmod } from 'node:fs/promises'; import { userInfo } from 'node:os'; import { resolve } from 'node:path'; import { BasePlatform } from './base.js'; export class FreeBSDInstaller extends BasePlatform { get rcServiceName() { return this.cli.serviceName.toLowerCase(); } get rcServicePath() { return resolve('/usr/local/etc/rc.d', this.rcServiceName); } async install() { this.checkForRoot(); await this.checkUser(); await this.cli.portCheck(); await this.cli.homePathCheck(); try { await this.createRCService(); await this.enableService(); await this.start(); } catch (e) { console.error(e.toString()); this.cli.logger('ERROR: Failed Operation', 'fail'); } } async uninstall() { this.checkForRoot(); await this.stop(); // try and disable the service await this.disableService(); try { if (existsSync(this.rcServicePath)) { this.cli.logger(`Removed ${this.rcServiceName} service`, 'succeed'); unlinkSync(this.rcServicePath); } else { this.cli.logger(`Could not find installed ${this.rcServiceName} service.`, 'fail'); } } catch (e) { console.error(e.toString()); this.cli.logger('ERROR: Failed Operation', 'fail'); } } async reinstall() { this.checkForRoot(); await this.uninstall(); await sleep(2000); await this.install(); } async start() { this.checkForRoot(); try { execSync(`service ${this.rcServiceName} start`, { stdio: 'inherit' }); this.cli.logger(`${this.rcServiceName} started`, 'succeed'); await this.waitForApiAndPrintInstructions('start'); } catch (error) { this.cli.logger(`Failed to start ${this.rcServiceName}: ${error.message}`, 'fail'); } } async stop() { this.checkForRoot(); try { execSync(`service ${this.rcServiceName} stop`, { stdio: 'inherit' }); this.cli.logger(`${this.rcServiceName} stopped`, 'succeed'); } catch (error) { this.cli.logger(`Failed to stop ${this.rcServiceName}: ${error.message}`, 'fail'); } } async restart() { this.checkForRoot(); try { execSync(`service ${this.rcServiceName} restart`, { stdio: 'inherit' }); this.cli.logger(`${this.rcServiceName} restarted`, 'succeed'); await this.waitForApiAndPrintInstructions('restart'); } catch (error) { this.cli.logger(`Failed to restart ${this.rcServiceName}: ${error.message}`, 'fail'); } } async getId() { if ((process.getuid?.() === 0 && this.cli.asUser) || process.env.SUDO_USER) { const uid = execSync(`id -u ${this.cli.asUser ?? process.env.SUDO_USER}`).toString('utf8'); const gid = execSync(`id -g ${this.cli.asUser ?? process.env.SUDO_USER}`).toString('utf8'); return { uid: parseInt(uid, 10), gid: parseInt(gid, 10), }; } else { return { uid: userInfo().uid, gid: userInfo().gid, }; } } getPidOfPort(port) { try { return execSync(`sockstat -P tcp -p ${port} -l -q 2> /dev/null | awk '{print $3}' | head -n 1`).toString('utf8').trim(); } catch { return null; } } async waitForApiAndPrintInstructions(type) { const apiReady = await this.cli.waitForApiHealth(); if (apiReady) { await this.cli.printPostInstallInstructions(type); } else { this.cli.logger('WARNING: There might be errors, or the setup might still be in progress.\n' + ' You can check the logs using the command: cameraui logs', 'warn'); } } async enableService() { try { execSync(`sysrc ${this.rcServiceName}_enable="YES" 2> /dev/null`); } catch (error) { this.cli.logger(`WARNING: failed to run sysrc ${this.rcServiceName}_enable="YES": ${error.message}`, 'warn'); } } async disableService() { try { execSync(`sysrc ${this.rcServiceName}_enable="NO" 2> /dev/null`); } catch (error) { this.cli.logger(`WARNING: failed to run sysrc ${this.rcServiceName}_enable="NO": ${error.message}`, 'warn'); } } checkForRoot() { if (process.getuid?.() !== 0) { this.cli.logger('ERROR: This command must be executed using sudo on FreeBSD', 'fail'); this.cli.logger(`EXAMPLE: sudo cameraui ${this.cli.action}`, 'fail'); process.exit(1); } if (this.cli.action === 'install' && !process.env.SUDO_USER && !this.cli.asUser) { this.cli.logger('ERROR: Could not detect user. Pass in the user you want to run camera.ui as using the --user flag eg.', 'fail'); this.cli.logger(`EXAMPLE: sudo cameraui ${this.cli.action} --user your-user`, 'fail'); process.exit(1); } } async checkUser() { try { // check if user exists execSync(`id ${this.cli.asUser} 2> /dev/null`); } catch { // if not create the user execSync(`pw useradd -q -n ${this.cli.asUser} -s /usr/sbin/nologin 2> /dev/null`); this.cli.logger(`Created service user: ${this.cli.asUser}`, 'info'); } } async createRCService() { const sysctlPath = execSync('which sysctl').toString().trim(); const rcFileContents = [ '#!/bin/sh', '#', '# PROVIDE: ' + this.rcServiceName, '# REQUIRE: NETWORKING SYSLOG', '# KEYWORD: shutdown', '#', '# Add the following lines to /etc/rc.conf to enable ' + this.rcServiceName + ':', '#', '#' + this.rcServiceName + '_enable="YES"', '', '. /etc/rc.subr', '', 'name="' + this.rcServiceName + '"', 'rcvar="' + this.rcServiceName + '_enable"', '', 'load_rc_config $name', '', ': ${' + this.rcServiceName + '_user:="' + this.cli.asUser + '"}', ': ${' + this.rcServiceName + '_enable:="NO"}', ': ${' + this.rcServiceName + '_facility:="daemon"}', ': ${' + this.rcServiceName + '_priority:="debug"}', ': ${' + this.rcServiceName + '_home_path:="' + this.cli.homePath + '"}', ': ${' + this.rcServiceName + '_storage_path:="' + this.cli.storagePath + '"}', '', 'export HOME="$(eval echo ~${' + this.rcServiceName + '_user})"', 'export PATH=/usr/local/bin:${PATH}', '', 'pidfile="/var/run/${name}.pid"', 'command="/usr/sbin/daemon"', 'procname="daemon"', 'command_args=" -c -f -R 3 -P ${pidfile} ' + this.cli.selfPath + ` run -H ${this.cli.homePath}"`, 'start_precmd="cameraui_precmd"', '', 'start_precmd="${name}_precmd"', '', '${name}_precmd()', '{', ' sleep 10', ' chown -R ${' + this.rcServiceName + '_user}: ${' + this.rcServiceName + '_home_path}', ' chown -R ${' + this.rcServiceName + '_user}: ${' + this.rcServiceName + '_storage_path}', ' install -o ${' + this.rcServiceName + '_user} /dev/null ${pidfile}', ' # Set sysctl values for better network performance', ` ${sysctlPath} net.inet.udp.recvspace=5242880`, '}', '', 'run_rc_command "$1"', ] .filter((x) => x) .join('\n'); await outputFile(this.rcServicePath, rcFileContents); await chmod(this.rcServicePath, '755'); } } //# sourceMappingURL=freebsd.js.map