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.



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

Raises:



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_pathObject



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

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

.privileged_ok?Boolean

Returns:

  • (Boolean)


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

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

.statusObject



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_pathObject



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

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



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_systemdObject



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