#!/bin/bash ### BEGIN INIT INFO # Provides: emalibre # Required-Start: $remote_fs $network $syslog # Required-Stop: $remote_fs $network $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: EMA Libre Carpincho web ### END INIT INFO PATH=/usr/sbin:/usr/bin:/sbin:/bin export PATH . /lib/lsb/init-functions appFilePath=/usr/share/emalibre pidFile=/run/emalibre.pid logDir=/var/log/emalibre modeFile=/etc/emalibre_modo foreverApp="$appFilePath/node_modules/forever/bin/forever" # Mantener el registro de instalaciones anteriores iniciadas como root. export HOME=/root export FOREVER_ROOT=/root/.forever nodeBin="$appFilePath/node_modules/node/bin/node" export PATH="$appFilePath/node_modules/node/bin:$PATH" fail() { echo "emalibre: $*" >&2; return 1; } require_root() { [ "$(id -u)" -eq 0 ] || { fail "Este comando requiere root."; return 4; } } check_runtime() { [ -x "$nodeBin" ] || { fail "Falta el Node incluido en $nodeBin (Node.js >= 20)."; return 5; } "$nodeBin" -e 'process.exit(Number(process.versions.node.split(".")[0]) >= 20 ? 0 : 1)' || { fail "Se requiere Node.js >= 20."; return 5; } [ -f "$foreverApp" ] && [ -f "$appFilePath/gui_web/express.js" ] || { fail "La instalación web está incompleta."; return 5; } } node_run() { "$nodeBin" "$@" 9>&-; } forever_cmd() { node_run "$foreverApp" "$@"; } # Consultar el supervisor, sin confiar en archivos PID residuales. # 0: activo; 3: detenido; 4: no se pudo consultar. process_status() { "$nodeBin" - "$appFilePath" <<'JS' const root = process.argv[2]; const timer = setTimeout(() => process.exit(4), 5000); try { require(root + '/node_modules/forever').list(false, (err, processes) => { clearTimeout(timer); if (err) { console.error(err.message); process.exit(4); } const active = (processes || []).some(p => p.uid === 'EMA_Web'); process.exit(active ? 0 : 3); }); } catch (err) { console.error(err.message); process.exit(4); } JS } start() { local state mode process_status; state=$? case "$state" in 0) echo "EMA Libre ya está ejecutándose."; return 0 ;; 3) ;; *) fail "No se pudo consultar Forever."; return "$state" ;; esac if [ ! -f "$modeFile" ]; then printf 'web\n' > "$modeFile" || return 1 fi mode=$(tr -d '[:space:]' < "$modeFile") || return 1 [ "$mode" = web ] || { fail "Modo inválido: '$mode'; se esperaba web."; return 2; } mkdir -p "$logDir" || return 1 rm -f "$pidFile" || return 1 # Forever concatena sourceDir y script: el script debe ser relativo. forever_cmd start --uid EMA_Web --id EMA_Web --minUptime 5000 --spinSleepTime 5000 \ --sourceDir "$appFilePath" --workingDir "$appFilePath" --pidFile "$pidFile" \ -l "$logDir/forever.log" -o "$logDir/stdout.log" -e "$logDir/stderr.log" \ -a -c "$nodeBin" "gui_web/express.js" iniciar || return $? # Forever arranca en segundo plano: esperar a que registre el supervisor. local attempt for attempt in {1..10}; do process_status; state=$? [ "$state" -eq 0 ] && { echo "EMA Libre iniciado."; return 0; } [ "$state" -eq 3 ] || return "$state" sleep 1 done fail "El supervisor no arrancó; revise $logDir." } stop() { local state process_status; state=$? case "$state" in 0) forever_cmd stop EMA_Web || return $? ;; 3) echo "EMA Libre ya está detenido." ;; *) fail "No se pudo consultar Forever."; return "$state" ;; esac process_status; state=$? [ "$state" -eq 3 ] || { fail "No se pudo confirmar la parada."; return 1; } rm -f "$pidFile" } status() { local state process_status; state=$? case "$state" in 0) echo "EMA Libre: supervisor activo." ;; 3) echo "EMA Libre: detenido." ;; *) fail "Estado desconocido; no se pudo consultar Forever." ;; esac return "$state" } set_boot() { # El arranque del servicio es independiente del inicio del recolector. if [ -d /run/systemd/system ]; then systemctl "$1" emalibre else update-rc.d emalibre defaults && update-rc.d emalibre "$1" fi } view_conf() { printf 'Modo: '; cat "$modeFile" || return 1 if [ -d /run/systemd/system ]; then systemctl is-enabled emalibre else echo 'Inicio del servicio: consultar enlaces /etc/rc[2-5].d/*emalibre' fi "$nodeBin" - "$appFilePath/conf/ConfiguracionGeneral.js" <<'JS' const cfg = require(process.argv[2]).ConfGeneral; for (const key of ['Tiempo_de_Recoleccion', 'UTC', 'SistemaMetrico', 'debug', 'debugTamanio', 'NombreEstacion', 'Modo', 'inicio_automatico', 'Audio', 'notificar_cambios', 'audioVoces', 'IP', 'Puerto', 'Ruta']) { console.log(key + ': ' + cfg[key]); } JS } case "${1:-}" in start|stop|restart|force-reload|status|uptime|reporte|view-conf|set-inicio-enabled|set-inicio-disable) ;; *) echo "Uso: $0 {start|stop|restart|force-reload|status|uptime|reporte|view-conf|set-inicio-enabled|set-inicio-disable}" >&2; exit 2 ;; esac require_root || exit $? check_runtime || exit $? case "$1" in start|stop|restart|force-reload|set-inicio-enabled|set-inicio-disable) # Evitar arranques/paradas simultáneos. Cerrar el descriptor en Node # impide que el daemon herede y retenga el bloqueo. exec 9>/run/lock/emalibre.lock || exit 1 flock -w 30 9 || exit 1 ;; esac case "$1" in start) start ;; stop) stop ;; restart|force-reload) stop && start ;; status) status ;; uptime) forever_cmd list ;; reporte) (cd "$appFilePath" && node_run core/reporte.js) ;; view-conf) view_conf ;; set-inicio-enabled) set_boot enable ;; set-inicio-disable) set_boot disable ;; esac exit $?