#!/usr/bin/ruby require 'socket' def do_cmd(*cmd) ctl = UNIXSocket.open('/run/initctl') ctl.puts(cmd.join(' ')) puts(ctl.readline.chomp) exit end # These are hashes $daemons = {} $daemonCmds = {} # Needed to prevent respawning during a reboot... $shuttingDown = false # Here we do some process monitoring... Signal.trap(:SIGCHLD) do loop do begin pid = Process.wait(-1, Process::WNOHANG) key = $daemons.key(pid) if key cmd = $daemonCmds[key] $daemons.delete(key) $daemonCmds.delete(key) # Respawn any processes that exit... if key != 'openrc' && !$shuttingDown launch(key, cmd) end end break if pid == nil rescue Errno::ECHILD break end end end def launch(id, cmd) puts('Starting %s...' % id) pid = fork do Process.setsid() exec(*cmd) end $daemons[id] = pid $daemonCmds[id] = cmd end def init puts('*** init-ng v1.0.0 starting...') launch('openrc', %w[/sbin/rc sysinit]) Process.wait($daemons['openrc']) launch('openrc', %w[/sbin/rc boot]) Process.wait($daemons['openrc']) launch('openrc', %w[/sbin/rc default]) Process.wait($daemons['openrc']) end def shutdown $shuttingDown = true launch('openrc', %w[/sbin/rc reboot]) Process.wait($daemons['openrc']) sys_sync() end # This shows the one of the hazards of coding this in Ruby... def sys_reboot(cmd) # LINUX_REBOOT_CMD_POWER_OFF == 0x4321FEDC # LINUX_REBOOT_CMD_RESTART == 0x01234567 # LINUX_REBOOT_CMD_HALT == 0xCDEF0123 map = { poweroff: 0x4321fedc, restart: 0x01234567, halt: 0xcdef0123 } # 169 == SYS_reboot # LINUX_REBOOT_MAGIC1 == 0xfee1dead # LINUX_REBOOT_MAGIC2C == 0x20112000 syscall(169, 0xfee1dead, 0x20112000, map[cmd]) end def sys_sync # 162 == SYS_sync syscall(162) end # # Start of the script proper # # If we're not PID 1, parse & send commands to /run/initctl # if $$ != 1 case ARGV[0] when 'poweroff', 'restart', 'halt' do_cmd(ARGV[0].to_sym) when 'status' do_cmd(ARGV.shift.to_sym, *ARGV) when 'test' map = { poweroff: 0x4321fedc, restart: 0x01234567, halt: 0xcdef0123 } # 169 == SYS_reboot syscall(169, 0xfee1dead, 0x20112000, map[:poweroff]) else puts('I know the following commands: poweroff, restart, halt, status, test') exit 1 end end init ARGV.each do |e| case e when 'emergency' $emergency = true end end if $emergency launch('agetty1', %w[/sbin/agetty tty1 --noclear --autologin root]) else # Launch TTYs... (1..5).each do |n| launch("agetty#{n}", %W[/sbin/agetty tty#{n} --noclear]) end end begin server = UNIXServer.open('/run/initctl') rescue Errno::EADDRINUSE File.delete('/run/initctl') retry end loop do ctl = server.accept args = ctl.readline.chomp.split cmd = args.shift.to_sym case cmd when :poweroff, :restart, :halt shutdown sys_reboot(cmd) when :status # ctl.puts($daemons[args.first] ? 'ok' : 'dead') # This is NFG. It only shows on the main console, not on the socket, so # you get nothing if you're ssh'ed in... $daemons.each { |key, value| puts(key + ': ' + (value ? '[OK]' : '[!!]') + ' (' + value.to_s + ')') } ctl.puts end end