Module: Portless::Daemon
- Defined in:
- lib/portless/daemon.rb
Overview
Starting/stopping the proxy daemon, including the privileged-port dance: for ports < 1024 we re-exec under sudo; the elevated process spawns the detached daemon that binds the socket as root. Falls back to :1355 when sudo is unavailable. Mirrors portless's handleProxy + ensureProxyRunning.
Class Method Summary collapse
- .cleanup_markers ⇒ Object
- .default_port(tls) ⇒ Object
- .ensure_running(tls:) ⇒ Object
- .lib_dir ⇒ Object
- .monotonic ⇒ Object
- .read_pid ⇒ Object
- .spawn_detached(port:, tls:) ⇒ Object
-
.start(tls:, port: nil, foreground: false) ⇒ Object
foreground: become the daemon (binds the port, blocks).
- .start_privileged(port:, tls:) ⇒ Object
- .stop ⇒ Object
- .wait_until_running(port, timeout: 10) ⇒ Object
Class Method Details
.cleanup_markers ⇒ Object
92 93 94 |
# File 'lib/portless/daemon.rb', line 92 def cleanup_markers [ State.proxy_pid_file, State.proxy_port_file ].each { |f| File.delete(f) if File.exist?(f) } end |
.default_port(tls) ⇒ Object
86 |
# File 'lib/portless/daemon.rb', line 86 def default_port(tls) = tls ? Constants::HTTPS_PORT : Constants::HTTP_PORT |
.ensure_running(tls:) ⇒ Object
13 14 15 16 17 18 19 |
# File 'lib/portless/daemon.rb', line 13 def ensure_running(tls:) port = Health.discover_port return port if port start(tls: tls) Health.discover_port end |
.lib_dir ⇒ Object
96 |
# File 'lib/portless/daemon.rb', line 96 def lib_dir = File.("..", __dir__) |
.monotonic ⇒ Object
97 |
# File 'lib/portless/daemon.rb', line 97 def monotonic = Process.clock_gettime(Process::CLOCK_MONOTONIC) |
.read_pid ⇒ Object
88 89 90 |
# File 'lib/portless/daemon.rb', line 88 def read_pid Integer(File.read(State.proxy_pid_file).strip, exception: false) if File.exist?(State.proxy_pid_file) end |
.spawn_detached(port:, tls:) ⇒ Object
64 65 66 67 68 69 70 71 72 73 |
# File 'lib/portless/daemon.rb', line 64 def spawn_detached(port:, tls:) State.ensure_dir! log = File.open(State.proxy_log, "a") args = [ RbConfig.ruby, "-I", lib_dir, Privilege.program, "proxy", "start", "--foreground", "--port", port.to_s, tls ? "--tls" : "--no-tls" ] pid = Process.spawn(*args, out: log, err: log, pgroup: true) Process.detach(pid) log.close wait_until_running(port) end |
.start(tls:, port: nil, foreground: false) ⇒ Object
foreground: become the daemon (binds the port, blocks). Otherwise orchestrate: elevate if needed, then spawn the detached foreground daemon.
23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/portless/daemon.rb', line 23 def start(tls:, port: nil, foreground: false) port ||= Integer(ENV["PORTLESS_PORT"], exception: false) || default_port(tls) return Proxy.new(port: port, tls: tls).run if foreground return if Health.proxy_running?(port) if Privilege.needs_sudo?(port) && !Privilege.root? start_privileged(port: port, tls: tls) else spawn_detached(port: port, tls: tls) end end |
.start_privileged(port:, tls:) ⇒ Object
51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/portless/daemon.rb', line 51 def start_privileged(port:, tls:) unless Privilege.interactive? warn "rb-portless: can't bind :#{port} without a terminal — using :#{Constants::FALLBACK_PROXY_PORT}" return spawn_detached(port: Constants::FALLBACK_PROXY_PORT, tls: tls) end ok = Privilege.reexec_with_sudo([ "proxy", "start", "--port", port.to_s, tls ? "--tls" : "--no-tls" ]) return wait_until_running(port) if ok warn "rb-portless: sudo declined — using :#{Constants::FALLBACK_PROXY_PORT}" spawn_detached(port: Constants::FALLBACK_PROXY_PORT, tls: tls) end |
.stop ⇒ Object
36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/portless/daemon.rb', line 36 def stop pid = read_pid unless pid warn "rb-portless: no proxy is running" return end Process.kill("TERM", pid) rescue Errno::ESRCH cleanup_markers rescue Errno::EPERM # Proxy owned by root (privileged bind) — stop it with sudo. Privilege.reexec_with_sudo([ "proxy", "stop" ]) unless Privilege.root? end |
.wait_until_running(port, timeout: 10) ⇒ Object
75 76 77 78 79 80 81 82 83 84 |
# File 'lib/portless/daemon.rb', line 75 def wait_until_running(port, timeout: 10) deadline = monotonic + timeout until Health.proxy_running?(port) return false if monotonic > deadline sleep 0.2 end State.fix_ownership true end |