Module: Portless::Privilege

Defined in:
lib/portless/privilege.rb

Overview

The whole privileged-port trick (ports < 1024): re-exec the CLI under sudo so the elevated process binds the socket itself, then chown state files back to the invoking user. Fall back to the unprivileged port 1355 if sudo is denied and no explicit port was asked for. Refuse silently-in-CI. Mirrors portless.

Class Method Summary collapse

Class Method Details

.interactive?Boolean

A real terminal we can prompt on. CI / no-TTY must never hang on a sudo password — callers turn this into a clear error or the 1355 fallback.

Returns:

  • (Boolean)


23
24
25
# File 'lib/portless/privilege.rb', line 23

def interactive?
  $stdin.tty? && $stdout.tty? && !truthy(ENV["CI"])
end

.needs_sudo?(port) ⇒ Boolean

Returns:

  • (Boolean)


17
18
19
# File 'lib/portless/privilege.rb', line 17

def needs_sudo?(port)
  !Constants::WINDOWS && privileged_port?(port) && !root?
end

.portless_env_argsObject



34
35
36
# File 'lib/portless/privilege.rb', line 34

def portless_env_args
  ENV.select { |k, _| k.start_with?("PORTLESS") }.map { |k, v| "#{k}=#{v}" }
end

.privileged_port?(port) ⇒ Boolean

Returns:

  • (Boolean)


15
# File 'lib/portless/privilege.rb', line 15

def privileged_port?(port) = port.to_i < Constants::PRIVILEGED_PORT_THRESHOLD

.programObject

The executable to re-invoke. $PROGRAM_NAME is the exe path (gem wrapper or exe/rb-portless in dev).



40
# File 'lib/portless/privilege.rb', line 40

def program = $PROGRAM_NAME

.reexec_with_sudo(args) ⇒ Object

Re-run this CLI under sudo, preserving PORTLESS_* env (sudo strips env). Returns true on success. stdio is inherited so the password prompt shows.



29
30
31
32
# File 'lib/portless/privilege.rb', line 29

def reexec_with_sudo(args)
  cmd = [ "sudo", "env", *portless_env_args, RbConfig.ruby, program, *args ]
  system(*cmd)
end

.root?Boolean

Returns:

  • (Boolean)


13
# File 'lib/portless/privilege.rb', line 13

def root? = Process.respond_to?(:uid) && Process.uid.zero?

.truthy(value) ⇒ Object



42
# File 'lib/portless/privilege.rb', line 42

def truthy(value) = %w[1 true yes].include?(value.to_s.downcase)