└── init /init: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'socket' 4 | 5 | def do_cmd(*cmd) 6 | ctl = UNIXSocket.open('/run/initctl') 7 | ctl.puts(cmd.join(' ')) 8 | puts(ctl.readline.chomp) 9 | exit 10 | end 11 | 12 | if $$ != 1 13 | case ARGV[0] 14 | when 'poweroff', 'restart', 'halt' 15 | do_cmd(ARGV[0].to_sym) 16 | when 'status' 17 | do_cmd(ARGV.shift.to_sym, *ARGV) 18 | when 'test' 19 | map = { poweroff: 0x4321fedc, restart: 0x01234567, halt: 0xcdef0123 } 20 | syscall(169, 0xfee1dead, 537993216, map[:poweroff]) 21 | else 22 | exit 1 23 | end 24 | end 25 | 26 | $daemons = {} 27 | 28 | Signal.trap(:SIGCHLD) do 29 | loop do 30 | begin 31 | status = Process.wait(-1, Process::WNOHANG) 32 | key = $daemons.key(status) 33 | $daemons.delete(key) if key 34 | break if status == nil 35 | rescue Errno::ECHILD 36 | break 37 | end 38 | end 39 | end 40 | 41 | def action(name) 42 | print(name) 43 | begin 44 | yield 45 | rescue => e 46 | print(' (error: %s)' % e) 47 | end 48 | puts 49 | end 50 | 51 | NETFS = %w[nfs nfs4 smbfs cifs codafs ncpfs shfs fuse fuseblk glusterfs davfs fuse.glusterfs] 52 | VIRTFS = %w[proc sysfs tmpfs devtmpfs devpts] 53 | 54 | def init 55 | def is_mounted(path) 56 | return false unless File.directory?(path) 57 | path = File.realpath(path) 58 | a = File.stat(path) 59 | b = File.stat(path + '/..') 60 | return (a.dev != b.dev) || (a.ino == b.ino) 61 | end 62 | 63 | def mount(type, device, dir, opts) 64 | return if is_mounted(dir) 65 | Dir.mkdir(dir) unless File.directory?(dir) 66 | system('mount', '-t', type, device, dir, '-o', opts) 67 | end 68 | 69 | action 'Mounting virtual file-systems' do 70 | mount('proc', 'proc', '/proc', 'nosuid,noexec,nodev') 71 | mount('sysfs', 'sys', '/sys', 'nosuid,noexec,nodev') 72 | mount('tmpfs', 'run', '/run', 'mode=0755,nosuid,nodev') 73 | mount('devtmpfs', 'dev', '/dev', 'mode=0755,nosuid') 74 | mount('devpts', 'devpts', '/dev/pts', 'mode=0620,gid=5,nosuid,noexec') 75 | mount('tmpfs', 'shm', '/dev/shm', 'mode=1777,nosuid,nodev') 76 | end 77 | 78 | action 'Setting hostname' do 79 | hostname = File.read('/etc/hostname').chomp 80 | File.write('/proc/sys/kernel/hostname', hostname) 81 | end 82 | 83 | action 'Starting udev daemon' do 84 | system('/usr/lib/systemd/systemd-udevd', '--daemon') 85 | end 86 | 87 | action 'Triggering udev uevents' do 88 | system('udevadm', 'trigger', '--action=add', '--type=subsystems') 89 | system('udevadm', 'trigger', '--action=add', '--type=devices') 90 | end 91 | 92 | action 'Waiting for udev uevents to be processed' do 93 | system('udevadm', 'settle') 94 | end 95 | 96 | if not File.directory?('/sys/class/net/lo') 97 | action 'Bringing up loopback interface' do 98 | system('ip', 'link', 'set', 'up', 'dev', 'lo') 99 | end 100 | end 101 | 102 | action 'Mounting local filesystems' do 103 | except = NETFS.map { |e| 'no' + e }.join(',') 104 | system('mount', '-a', '-t', except, '-O', 'no_netdev') 105 | end 106 | 107 | action 'Manage temporary files' do 108 | system('systemd-tmpfiles', '--create', '--remove', '--clean') 109 | end 110 | 111 | end 112 | 113 | def shutdown 114 | def killall 115 | 116 | def allgone?() 117 | Dir.glob('/proc/*').each do |e| 118 | pid = File.basename(e).to_i 119 | begin 120 | next if pid < 2 121 | # Is it a kernel process? 122 | next if File.read('/proc/%i/cmdline' % pid).empty? 123 | rescue Errno::ENOENT 124 | end 125 | return false 126 | end 127 | return true 128 | end 129 | 130 | def wait_until(timeout = 2, interval = 0.25) 131 | start = Time.now 132 | begin 133 | break true if yield 134 | sleep(interval) 135 | end while (Time.now - start) < timeout 136 | end 137 | 138 | ok = false 139 | 140 | action 'Sending SIGTERM to processes' do 141 | Process.kill(:SIGTERM, -1) 142 | ok = wait_until(10) { allgone? } 143 | raise 'Failed' unless ok 144 | end 145 | 146 | return if ok 147 | 148 | action 'Sending SIGKILL to processes' do 149 | Process.kill(:SIGKILL, -1) 150 | ok = wait_until(15) { allgone? } 151 | raise 'Failed' unless ok 152 | end 153 | 154 | end 155 | 156 | action 'Shutting down udev' do 157 | system('udevadm', 'control', '--exit') 158 | end 159 | 160 | # Kill everything 161 | killall 162 | 163 | action 'Unmounting real filesystems' do 164 | except = (NETFS + VIRTFS).map { |e| 'no' + e }.join(',') 165 | system('umount', '-a', '-t', except, '-O', 'no_netdev') 166 | end 167 | 168 | sys_sync() 169 | 170 | end 171 | 172 | init 173 | 174 | begin 175 | File.delete('/run/nologin') 176 | rescue Errno::ENOENT 177 | end 178 | 179 | ARGV.each do |e| 180 | case e 181 | when 'emergency' 182 | $emergency = true 183 | end 184 | end 185 | 186 | def start(id, cmd) 187 | pid = fork do 188 | Process.setsid() 189 | exec(*cmd) 190 | end 191 | $daemons[id] = pid 192 | end 193 | 194 | if $emergency 195 | start('agetty1', %w[agetty tty1 --noclear --autologin root]) 196 | else 197 | (1..5).each do |n| 198 | start("agetty#{n}", %W[agetty tty#{n}]) 199 | end 200 | start('slim', %w[slim -nodaemon]) 201 | 202 | Dir.mkdir('/run/dbus', 0755) unless File.exists?('/run/dbus') 203 | start('dbus', %w[dbus-daemon --system --nofork --nopidfile]) 204 | end 205 | 206 | def sys_reboot(cmd) 207 | map = { poweroff: 0x4321fedc, restart: 0x01234567, halt: 0xcdef0123 } 208 | syscall(169, 0xfee1dead, 537993216, map[cmd]) 209 | end 210 | 211 | def sys_sync 212 | syscall(162) 213 | end 214 | 215 | begin 216 | server = UNIXServer.open('/run/initctl') 217 | rescue Errno::EADDRINUSE 218 | File.delete('/run/initctl') 219 | retry 220 | end 221 | 222 | loop do 223 | ctl = server.accept 224 | args = ctl.readline.chomp.split 225 | cmd = args.shift.to_sym 226 | case cmd 227 | when :poweroff, :restart, :halt 228 | shutdown 229 | sys_reboot(cmd) 230 | when :status 231 | ctl.puts($daemons[args.first] ? 'ok' : 'dead') 232 | end 233 | end 234 | --------------------------------------------------------------------------------