Module: Portless::Service
- Defined in:
- lib/portless/service.rb
Overview
An OS service that binds the privileged port at boot, so after a one-time
service install you never see a sudo prompt again. macOS → LaunchDaemon,
Linux → systemd unit, Windows → Task Scheduler (phase 3). Mirrors portless's
service.ts. Install/uninstall self-elevate via sudo.
Constant Summary collapse
- LABEL =
"rb.portless.proxy"
Class Method Summary collapse
-
.daemon_argv(port, tls) ⇒ Object
The argv the service runs at boot: the proxy daemon in the foreground.
- .elevate(args) ⇒ Object
- .install(tls: true, port: nil) ⇒ Object
-
.install_launchd(port, tls) ⇒ Object
── macOS (launchd) ─────────────────────────────────────────────────────.
-
.install_systemd(port, tls) ⇒ Object
── Linux (systemd) ─────────────────────────────────────────────────────.
- .launchd_plist(port, tls) ⇒ Object
- .launchd_plist_path ⇒ Object
- .privileged_ok? ⇒ Boolean
- .status ⇒ Object
- .systemd_unit(port, tls) ⇒ Object
- .systemd_unit_path ⇒ Object
- .uninstall ⇒ Object
- .uninstall_launchd ⇒ Object
- .uninstall_systemd ⇒ Object
Class Method Details
.daemon_argv(port, tls) ⇒ Object
The argv the service runs at boot: the proxy daemon in the foreground.
115 116 117 118 |
# File 'lib/portless/service.rb', line 115 def daemon_argv(port, tls) [ RbConfig.ruby, "-I", Daemon.lib_dir, Privilege.program, "proxy", "start", "--foreground", "--port", port.to_s, tls ? "--tls" : "--no-tls" ] end |
.elevate(args) ⇒ Object
122 123 124 125 126 127 |
# File 'lib/portless/service.rb', line 122 def elevate(args) raise Error, "Windows service install is not wired yet (phase 3)" if Constants::WINDOWS ENV["PORTLESS_STATE_DIR"] ||= State.dir # bake the user's state dir for the root daemon Privilege.reexec_with_sudo(args) || raise(Error, "sudo required to manage the boot service") end |
.install(tls: true, port: nil) ⇒ Object
16 17 18 19 20 21 22 |
# File 'lib/portless/service.rb', line 16 def install(tls: true, port: nil) port ||= Daemon.default_port(tls) return elevate([ "service", "install", "--port", port.to_s, tls ? "--tls" : "--no-tls" ]) unless privileged_ok? Constants::MACOS ? install_launchd(port, tls) : install_systemd(port, tls) puts "rb-portless: boot service installed (binds :#{port})" end |
.install_launchd(port, tls) ⇒ Object
── macOS (launchd) ─────────────────────────────────────────────────────
41 42 43 44 45 46 47 48 49 |
# File 'lib/portless/service.rb', line 41 def install_launchd(port, tls) path = launchd_plist_path File.write(path, launchd_plist(port, tls)) FileUtils.chown("root", "wheel", path) system("launchctl", "bootout", "system/#{LABEL}", err: File::NULL) system("launchctl", "bootstrap", "system", path) || raise(Error, "launchctl bootstrap failed") system("launchctl", "enable", "system/#{LABEL}") system("launchctl", "kickstart", "-k", "system/#{LABEL}") end |
.install_systemd(port, tls) ⇒ Object
── Linux (systemd) ─────────────────────────────────────────────────────
82 83 84 85 86 |
# File 'lib/portless/service.rb', line 82 def install_systemd(port, tls) File.write(systemd_unit_path, systemd_unit(port, tls)) system("systemctl", "daemon-reload") system("systemctl", "enable", "--now", "rb-portless") || raise(Error, "systemctl enable failed") end |
.launchd_plist(port, tls) ⇒ Object
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/portless/service.rb', line 58 def launchd_plist(port, tls) args = daemon_argv(port, tls).map { |a| " <string>#{a}</string>" }.join("\n") <<~PLIST <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key><string>#{LABEL}</string> <key>ProgramArguments</key> <array> #{args} </array> <key>EnvironmentVariables</key> <dict><key>PORTLESS_STATE_DIR</key><string>#{State.dir}</string></dict> <key>RunAtLoad</key><true/> <key>KeepAlive</key><true/> <key>StandardOutPath</key><string>#{State.proxy_log}</string> <key>StandardErrorPath</key><string>#{State.proxy_log}</string> </dict> </plist> PLIST end |
.launchd_plist_path ⇒ Object
56 |
# File 'lib/portless/service.rb', line 56 def launchd_plist_path = "/Library/LaunchDaemons/#{LABEL}.plist" |
.privileged_ok? ⇒ Boolean
120 |
# File 'lib/portless/service.rb', line 120 def privileged_ok? = Constants::WINDOWS || Privilege.root? |
.status ⇒ Object
31 32 33 34 35 36 37 38 |
# File 'lib/portless/service.rb', line 31 def status if Constants::MACOS system("launchctl", "print", "system/#{LABEL}", out: $stdout, err: $stdout) || puts("rb-portless: service not installed") else system("systemctl", "status", "rb-portless", out: $stdout, err: $stdout) end end |
.systemd_unit(port, tls) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/portless/service.rb', line 96 def systemd_unit(port, tls) <<~UNIT [Unit] Description=rb-portless proxy After=network-online.target Wants=network-online.target [Service] Type=simple Environment=PORTLESS_STATE_DIR=#{State.dir} ExecStart=#{daemon_argv(port, tls).join(' ')} Restart=on-failure [Install] WantedBy=multi-user.target UNIT end |
.systemd_unit_path ⇒ Object
94 |
# File 'lib/portless/service.rb', line 94 def systemd_unit_path = "/etc/systemd/system/rb-portless.service" |
.uninstall ⇒ Object
24 25 26 27 28 29 |
# File 'lib/portless/service.rb', line 24 def uninstall return elevate([ "service", "uninstall" ]) unless privileged_ok? Constants::MACOS ? uninstall_launchd : uninstall_systemd puts "rb-portless: boot service removed" end |
.uninstall_launchd ⇒ Object
51 52 53 54 |
# File 'lib/portless/service.rb', line 51 def uninstall_launchd system("launchctl", "bootout", "system/#{LABEL}", err: File::NULL) File.delete(launchd_plist_path) if File.exist?(launchd_plist_path) end |
.uninstall_systemd ⇒ Object
88 89 90 91 92 |
# File 'lib/portless/service.rb', line 88 def uninstall_systemd system("systemctl", "disable", "--now", "rb-portless", err: File::NULL) File.delete(systemd_unit_path) if File.exist?(systemd_unit_path) system("systemctl", "daemon-reload") end |