From c0aa669d41317e07ba05c29fa5011425c9200114 Mon Sep 17 00:00:00 2001 From: Shamus Hammons Date: Tue, 9 Dec 2014 09:15:38 -0600 Subject: [PATCH] Initial commit. --- .gitignore | 0 README | 44 +++++++++++++ init | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+) create mode 100644 .gitignore create mode 100644 README create mode 100755 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..be745eb --- /dev/null +++ b/README @@ -0,0 +1,44 @@ +README for init-ng + + +RATIONALE +--------- + +init-ng is meant to be a drop-in replacement for Sys V init. It leverages the +mighty power of OpenRC to do only what's needed, and nothing more! It is still +somewhat experimental (hence written in Ruby) but works well. Currently, it +can reliably bring the system up, bring it down, and respawn agettys if they +die. + +init-ng can also be extended fairly easily as well; for those who want service +supervision or socket activation, these can be fairly easily added. There is +already some supervision capability built-in, as init-ng monitors the agettys +that it spawns; this can be extended to other services as well. + +Note that while this init is written in Ruby, init-ng will eventually be +written in C. It's being developed in Ruby in order to make development and +testing easy. + + +HOW TO INSTALL/USE IT +--------------------- + + • Make sure you have Ruby 2.1.x installed. + • Move your current init binary out of the way (mv /sbin/init /sbin/init.sysv) + • Copy the init script to /sbin (cp ./init /sbin/) + • Make sure /sbin/init is marked executable (chmod ugo+x /sbin/init) + • Reboot + +Once you have rebooted successfully and logged in as root, typing 'init' on the +command line will display the commands that init-ng understands. + + +CREDITS +------- + +init-ng was initially written by James Hammons, who took his inspiration from +Felipe Contreras's blog post entitled "Demystifying the init system (PID 1)" +(https://felipec.wordpress.com/2013/11/04/init/). Felipe deserves most of the +credit for this, as without his article and code we wouldn't have known where +to begin. :-) + diff --git a/init b/init new file mode 100755 index 0000000..f281acd --- /dev/null +++ b/init @@ -0,0 +1,179 @@ +#!/usr/bin/ruby + +require 'socket' + + +def do_cmd(*cmd) + ctl = UNIXSocket.open('/run/initctl') + ctl.puts(cmd.join(' ')) + puts(ctl.readline.chomp) + exit +end + + +# 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 + + +# 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 action(name) + print(name) + begin + yield + rescue => e + print(' (error: %s)' % e) + end + puts +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 + + +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 + + +# 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 + + +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 + -- 2.37.2