1 package org
.argeo
.init
;
2 //#! /usr/bin/java --source 17 @/usr/local/etc/freed/pid1/jvm.args
4 import static java
.lang
.System
.Logger
.Level
.DEBUG
;
5 import static java
.lang
.System
.Logger
.Level
.ERROR
;
6 import static java
.lang
.System
.Logger
.Level
.INFO
;
7 import static java
.lang
.System
.Logger
.Level
.WARNING
;
9 import java
.io
.Console
;
10 import java
.io
.IOException
;
11 import java
.lang
.System
.Logger
;
12 import java
.lang
.management
.ManagementFactory
;
13 import java
.net
.InetAddress
;
14 import java
.net
.InterfaceAddress
;
15 import java
.net
.NetworkInterface
;
16 import java
.net
.SocketException
;
17 import java
.nio
.file
.Files
;
18 import java
.nio
.file
.Path
;
19 import java
.nio
.file
.Paths
;
20 import java
.util
.ArrayList
;
21 import java
.util
.Collections
;
22 import java
.util
.Enumeration
;
23 import java
.util
.List
;
24 import java
.util
.TreeMap
;
25 import java
.util
.concurrent
.atomic
.AtomicInteger
;
27 import sun
.misc
.Signal
;
29 /** A minimalistic Linux init process. */
31 final static AtomicInteger runLevel
= new AtomicInteger(-1);
33 private final static Logger logger
= System
.getLogger(SysInitMain
.class.getName());
35 private final static List
<String
> initDServices
= Collections
.synchronizedList(new ArrayList
<>());
37 public static void main(String
... args
) {
39 final long pid
= ProcessHandle
.current().pid();
40 Signal
.handle(new Signal("TERM"), (signal
) -> {
41 System
.out
.println("SIGTERM caught");
44 Signal
.handle(new Signal("INT"), (signal
) -> {
45 System
.out
.println("SIGINT caught");
48 Signal
.handle(new Signal("HUP"), (signal
) -> {
49 System
.out
.println("SIGHUP caught");
53 boolean isSystemInit
= pid
== 1 || pid
== 2;
55 if (isSystemInit
&& args
.length
> 0 && ("1".equals(args
[0]) //
56 || "single".equals(args
[0]) //
57 || "emergency".equals(args
[0]))) {
59 for (Object key
: new TreeMap
<>(System
.getProperties()).keySet()) {
60 System
.out
.println(key
+ "=" + System
.getProperty(key
.toString()));
62 System
.out
.println("Single user mode");
64 ProcessBuilder pb
= new ProcessBuilder("/bin/bash");
65 pb
.redirectError(ProcessBuilder
.Redirect
.INHERIT
);
66 pb
.redirectOutput(ProcessBuilder
.Redirect
.INHERIT
);
67 pb
.redirectInput(ProcessBuilder
.Redirect
.INHERIT
);
68 Process singleUserShell
= pb
.start();
69 singleUserShell
.waitFor();
74 runLevel
.set(Integer
.parseInt(args
[0]));
76 if (runLevel
.get() == 0) {// shutting down the whole system
78 logger
.log(INFO
, "Shutting down system...");
82 logger
.log(ERROR
, "Cannot start at run level " + runLevel
.get());
85 } else if (runLevel
.get() == 6) {// reboot the whole system
87 logger
.log(INFO
, "Rebooting the system...");
90 logger
.log(ERROR
, "Cannot start at run level " + runLevel
.get());
95 logger
.log(INFO
, "FREEd Init daemon starting with pid " + pid
+ " after "
96 + ManagementFactory
.getRuntimeMXBean().getUptime() + " ms");
98 String hostname
= Files
.readString(Paths
.get("/etc/hostname"));
99 new ProcessBuilder("/usr/bin/hostname", hostname
).start();
100 logger
.log(DEBUG
, "Set hostname to " + hostname
);
103 startInitDService("networking", true);
104 // Thread.sleep(3000);// leave some time for network to start up
105 if (!waitForNetwork(10 * 1000))
106 logger
.log(ERROR
, "No network available");
109 // TODO make it coherent with Java sshd
110 startInitDService("ssh", true);
113 startInitDService("nslcd", false);// Note: nslcd fails to stop
116 ServiceMain
.addPostStart(() -> new LoginThread().start());
119 logger
.log(INFO
, "FREEd Init daemon starting Argeo Init after "
120 + ManagementFactory
.getRuntimeMXBean().getUptime() + " ms");
121 ServiceMain
.main(args
);
123 } catch (Throwable e
) {
124 logger
.log(ERROR
, "Unexpected exception in free-pid1 init, shutting down... ", e
);
131 static void initSysctl() {
133 Path sysctlD
= Paths
.get("/etc/sysctl.d/");
134 for (Path conf
: Files
.newDirectoryStream(sysctlD
, "*.conf")) {
136 new ProcessBuilder("/usr/sbin/sysctl", "-p", conf
.toString()).start();
137 } catch (IOException e
) {
141 } catch (IOException e
) {
146 static void startInitDService(String serviceName
, boolean stopOnShutdown
) {
147 Path serviceInit
= Paths
.get("/etc/init.d/", serviceName
);
148 if (Files
.exists(serviceInit
))
150 int exitCode
= new ProcessBuilder(serviceInit
.toString(), "start").start().waitFor();
152 logger
.log(ERROR
, "Service " + serviceName
+ " dit not stop properly");
154 logger
.log(DEBUG
, "Service " + serviceName
+ " started");
156 initDServices
.add(serviceName
);
157 // Runtime.getRuntime().addShutdownHook(new Thread(() -> {
159 // new ProcessBuilder(serviceInit.toString(), "stop").start().waitFor();
160 // } catch (IOException | InterruptedException e) {
161 // e.printStackTrace();
163 // }, "FREEd stop service " + serviceName));
164 } catch (IOException
| InterruptedException e
) {
168 logger
.log(WARNING
, "Service " + serviceName
+ " not found and therefore not started");
171 static boolean waitForNetwork(long timeout
) {
172 long begin
= System
.currentTimeMillis();
174 boolean networkAvailable
= false;
176 networkAvailable
: while (!networkAvailable
) {
177 duration
= System
.currentTimeMillis() - begin
;
178 if (duration
> timeout
)
179 break networkAvailable
;
180 Enumeration
<NetworkInterface
> netInterfaces
= null;
182 netInterfaces
= NetworkInterface
.getNetworkInterfaces();
183 } catch (SocketException e
) {
184 throw new IllegalStateException("Cannot list network interfaces", e
);
186 if (netInterfaces
!= null) {
187 while (netInterfaces
.hasMoreElements()) {
188 NetworkInterface netInterface
= netInterfaces
.nextElement();
189 logger
.log(DEBUG
, "Interface:" + netInterface
);
190 for (InterfaceAddress addr
: netInterface
.getInterfaceAddresses()) {
191 InetAddress inetAddr
= addr
.getAddress();
192 logger
.log(DEBUG
, " addr: " + inetAddr
);
193 if (!inetAddr
.isLoopbackAddress() && !inetAddr
.isLinkLocalAddress()) {
195 if (inetAddr
.isReachable((int) timeout
)) {
196 networkAvailable
= true;
197 duration
= System
.currentTimeMillis() - begin
;
199 "Network available after " + duration
+ " ms. IP: " + inetAddr
);
200 break networkAvailable
;
202 } catch (IOException e
) {
203 logger
.log(ERROR
, "Cannot check whether " + inetAddr
+ " is reachable", e
);
209 throw new IllegalStateException("No network interface has been found");
213 } catch (InterruptedException e
) {
217 } catch (Exception e
) {
218 logger
.log(ERROR
, "Cannot check whether network is available", e
);
220 return networkAvailable
;
223 static void shutdown(boolean reboot
) {
226 Path sysrqP
= Paths
.get("/proc/sys/kernel/sysrq");
227 Files
.writeString(sysrqP
, "1");
228 Path sysrqTriggerP
= Paths
.get("/proc/sysrq-trigger");
229 Files
.writeString(sysrqTriggerP
, "e");// send SIGTERM to all processes
230 // Files.writeString(sysrqTriggerP, "i");// send SIGKILL to all processes
231 Files
.writeString(sysrqTriggerP
, "e");// flush data to disk
232 Files
.writeString(sysrqTriggerP
, "u");// unmount
234 Files
.writeString(sysrqTriggerP
, "b");
236 Files
.writeString(sysrqTriggerP
, "o");
237 } catch (IOException e
) {
238 logger
.log(ERROR
, "Cannot shut down system", e
);
242 static void stopInitDServices() {
243 for (int i
= initDServices
.size() - 1; i
>= 0; i
--) {
244 String serviceName
= initDServices
.get(i
);
245 Path serviceInit
= Paths
.get("/etc/init.d/", serviceName
);
247 int exitCode
= new ProcessBuilder(serviceInit
.toString(), "stop").start().waitFor();
249 logger
.log(ERROR
, "Service " + serviceName
+ " dit not stop properly");
250 } catch (InterruptedException
| IOException e
) {
251 logger
.log(ERROR
, "Cannot stop service " + serviceName
, e
);
256 /** A thread watching the login prompt. */
257 static class LoginThread
extends Thread
{
258 private boolean systemShuttingDown
= false;
259 private Process process
= null;
261 public LoginThread() {
262 super("FREEd login prompt");
264 Runtime
.getRuntime().addShutdownHook(new Thread(() -> {
265 systemShuttingDown
= true;
273 boolean getty
= true;
274 prompt
: while (!systemShuttingDown
) {
277 ProcessBuilder pb
= new ProcessBuilder("/usr/sbin/getty", "38400", "tty2");
278 process
= pb
.start();
280 Console console
= System
.console();
281 console
.readLine(); // type return once to activate login prompt
282 console
.printf("login: ");
283 String username
= console
.readLine();
284 username
= username
.trim();
285 if ("".equals(username
))
287 ProcessBuilder pb
= new ProcessBuilder("su", "--login", username
);
288 pb
.redirectError(ProcessBuilder
.Redirect
.INHERIT
);
289 pb
.redirectOutput(ProcessBuilder
.Redirect
.INHERIT
);
290 pb
.redirectInput(ProcessBuilder
.Redirect
.INHERIT
);
291 process
= pb
.start();
293 Runtime
.getRuntime().addShutdownHook(new Thread(() -> process
.destroy()));
296 } catch (InterruptedException e
) {
299 } catch (Exception e
) {