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

Class Method Details

.daemon_argv(port, tls) ⇒ Object

The argv the service runs at boot: the proxy daemon in the foreground.



121
122
123
124
# File 'lib/portless/service.rb', line 121

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

Raises:



128
129
130
131
132
133
# File 'lib/portless/service.rb', line 128

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) ─────────────────────────────────────────────────────



47
48
49
50
51
52
53
54
55
# File 'lib/portless/service.rb', line 47

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) ─────────────────────────────────────────────────────



88
89
90
91
92
# File 'lib/portless/service.rb', line 88

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

.installed?Boolean

Is a boot service on disk? (Both locations are world-readable, so this never needs sudo — clean uses it to skip a pointless elevation.)

Returns:

  • (Boolean)


33
34
35
# File 'lib/portless/service.rb', line 33

def installed?
  Constants::MACOS ? File.exist?(launchd_plist_path) : File.exist?(systemd_unit_path)
end

.launchd_plist(port, tls) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/portless/service.rb', line 64

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_pathObject



62
# File 'lib/portless/service.rb', line 62

def launchd_plist_path = "/Library/LaunchDaemons/#{LABEL}.plist"

.privileged_ok?Boolean

Returns:

  • (Boolean)


126
# File 'lib/portless/service.rb', line 126

def privileged_ok? = Constants::WINDOWS || Privilege.root?

.statusObject



37
38
39
40
41
42
43
44
# File 'lib/portless/service.rb', line 37

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



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/portless/service.rb', line 102

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_pathObject



100
# File 'lib/portless/service.rb', line 100

def systemd_unit_path = "/etc/systemd/system/rb-portless.service"

.uninstallObject



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_launchdObject



57
58
59
60
# File 'lib/portless/service.rb', line 57

def uninstall_launchd
  system("launchctl", "bootout", "system/#{LABEL}", err: File::NULL)
  File.delete(launchd_plist_path) if File.exist?(launchd_plist_path)
end

.uninstall_systemdObject



94
95
96
97
98
# File 'lib/portless/service.rb', line 94

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