]> Shamusworld >> Repos - init-ng/commitdiff
Initial commit.
authorShamus Hammons <jlhamm@acm.org>
Tue, 9 Dec 2014 15:15:38 +0000 (09:15 -0600)
committerShamus Hammons <jlhamm@acm.org>
Tue, 9 Dec 2014 15:15:38 +0000 (09:15 -0600)
.gitignore [new file with mode: 0644]
README [new file with mode: 0644]
init [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/README b/README
new file mode 100644 (file)
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 (executable)
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
+