-//#! /usr/bin/java --source 17 @/usr/local/etc/freed/pid1/jvm.args
-
-import static java.lang.System.Logger.Level.DEBUG;
-import static java.lang.System.Logger.Level.ERROR;
-import static java.lang.System.Logger.Level.INFO;
-import static java.lang.System.Logger.Level.WARNING;
-
-import java.io.Console;
-import java.io.IOException;
-import java.lang.System.Logger;
-import java.lang.management.ManagementFactory;
-import java.net.InetAddress;
-import java.net.InterfaceAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.TreeMap;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.argeo.init.Service;
-
-import sun.misc.Signal;
-
-/** A minimalistic Linux init process. */
-class FreedPid1 {
- final static AtomicInteger runLevel = new AtomicInteger(-1);
-
- private final static Logger logger = System.getLogger(FreedPid1.class.getName());
-
- private final static List<String> initDServices = Collections.synchronizedList(new ArrayList<>());
-
- public static void main(String... args) {
- try {
- final long pid = ProcessHandle.current().pid();
- Signal.handle(new Signal("TERM"), (signal) -> {
- System.out.println("SIGTERM caught");
- System.exit(0);
- });
- Signal.handle(new Signal("INT"), (signal) -> {
- System.out.println("SIGINT caught");
- System.exit(0);
- });
- Signal.handle(new Signal("HUP"), (signal) -> {
- System.out.println("SIGHUP caught");
- System.exit(0);
- });
-
- boolean isSystemInit = pid == 1 || pid == 2;
-
- if (isSystemInit && args.length > 0 && ("1".equals(args[0]) //
- || "single".equals(args[0]) //
- || "emergency".equals(args[0]))) {
- runLevel.set(1);
- for (Object key : new TreeMap<>(System.getProperties()).keySet()) {
- System.out.println(key + "=" + System.getProperty(key.toString()));
- }
- System.out.println("Single user mode");
- System.out.flush();
- ProcessBuilder pb = new ProcessBuilder("/bin/bash");
- pb.redirectError(ProcessBuilder.Redirect.INHERIT);
- pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
- pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
- Process singleUserShell = pb.start();
- singleUserShell.waitFor();
- } else {
- if (args.length == 0)
- runLevel.set(5);
- else
- runLevel.set(Integer.parseInt(args[0]));
-
- if (runLevel.get() == 0) {// shutting down the whole system
- if (!isSystemInit) {
- logger.log(INFO, "Shutting down system...");
- shutdown(false);
- System.exit(0);
- } else {
- logger.log(ERROR, "Cannot start at run level " + runLevel.get());
- System.exit(1);
- }
- } else if (runLevel.get() == 6) {// reboot the whole system
- if (!isSystemInit) {
- logger.log(INFO, "Rebooting the system...");
- shutdown(true);
- } else {
- logger.log(ERROR, "Cannot start at run level " + runLevel.get());
- System.exit(1);
- }
- }
-
- logger.log(INFO, "FREEd Init daemon starting with pid " + pid + " after "
- + ManagementFactory.getRuntimeMXBean().getUptime() + " ms");
- // hostname
- String hostname = Files.readString(Paths.get("/etc/hostname"));
- new ProcessBuilder("/usr/bin/hostname", hostname).start();
- logger.log(DEBUG, "Set hostname to " + hostname);
- // networking
- initSysctl();
- startInitDService("networking", true);
-// Thread.sleep(3000);// leave some time for network to start up
- if (!waitForNetwork(10 * 1000))
- logger.log(ERROR, "No network available");
-
- // OpenSSH
- // TODO make it coherent with Java sshd
- startInitDService("ssh", true);
-
- // NSS services
- startInitDService("nslcd", false);// Note: nslcd fails to stop
-
- // login prompt
- Service.addPostStart(() -> new LoginThread().start());
-
- // init Argeo CMS
- logger.log(INFO, "FREEd Init daemon starting Argeo Init after "
- + ManagementFactory.getRuntimeMXBean().getUptime() + " ms");
- Service.main(args);
- }
- } catch (Throwable e) {
- logger.log(ERROR, "Unexpected exception in free-pid1 init, shutting down... ", e);
- System.exit(1);
- } finally {
- stopInitDServices();
- }
- }
-
- static void initSysctl() {
- try {
- Path sysctlD = Paths.get("/etc/sysctl.d/");
- for (Path conf : Files.newDirectoryStream(sysctlD, "*.conf")) {
- try {
- new ProcessBuilder("/usr/sbin/sysctl", "-p", conf.toString()).start();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- static void startInitDService(String serviceName, boolean stopOnShutdown) {
- Path serviceInit = Paths.get("/etc/init.d/", serviceName);
- if (Files.exists(serviceInit))
- try {
- int exitCode = new ProcessBuilder(serviceInit.toString(), "start").start().waitFor();
- if (exitCode != 0)
- logger.log(ERROR, "Service " + serviceName + " dit not stop properly");
- else
- logger.log(DEBUG, "Service " + serviceName + " started");
- if (stopOnShutdown)
- initDServices.add(serviceName);
-// Runtime.getRuntime().addShutdownHook(new Thread(() -> {
-// try {
-// new ProcessBuilder(serviceInit.toString(), "stop").start().waitFor();
-// } catch (IOException | InterruptedException e) {
-// e.printStackTrace();
-// }
-// }, "FREEd stop service " + serviceName));
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- }
- else
- logger.log(WARNING, "Service " + serviceName + " not found and therefore not started");
- }
-
- static boolean waitForNetwork(long timeout) {
- long begin = System.currentTimeMillis();
- long duration = 0;
- boolean networkAvailable = false;
- try {
- networkAvailable: while (!networkAvailable) {
- duration = System.currentTimeMillis() - begin;
- if (duration > timeout)
- break networkAvailable;
- Enumeration<NetworkInterface> netInterfaces = null;
- try {
- netInterfaces = NetworkInterface.getNetworkInterfaces();
- } catch (SocketException e) {
- throw new IllegalStateException("Cannot list network interfaces", e);
- }
- if (netInterfaces != null) {
- while (netInterfaces.hasMoreElements()) {
- NetworkInterface netInterface = netInterfaces.nextElement();
- logger.log(DEBUG, "Interface:" + netInterface);
- for (InterfaceAddress addr : netInterface.getInterfaceAddresses()) {
- InetAddress inetAddr = addr.getAddress();
- logger.log(DEBUG, " addr: " + inetAddr);
- if (!inetAddr.isLoopbackAddress() && !inetAddr.isLinkLocalAddress()) {
- try {
- if (inetAddr.isReachable((int) timeout)) {
- networkAvailable = true;
- duration = System.currentTimeMillis() - begin;
- logger.log(DEBUG,
- "Network available after " + duration + " ms. IP: " + inetAddr);
- break networkAvailable;
- }
- } catch (IOException e) {
- logger.log(ERROR, "Cannot check whether " + inetAddr + " is reachable", e);
- }
- }
- }
- }
- } else {
- throw new IllegalStateException("No network interface has been found");
- }
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // silent
- }
- }
- } catch (Exception e) {
- logger.log(ERROR, "Cannot check whether network is available", e);
- }
- return networkAvailable;
- }
-
- static void shutdown(boolean reboot) {
- try {
- stopInitDServices();
- Path sysrqP = Paths.get("/proc/sys/kernel/sysrq");
- Files.writeString(sysrqP, "1");
- Path sysrqTriggerP = Paths.get("/proc/sysrq-trigger");
- Files.writeString(sysrqTriggerP, "e");// send SIGTERM to all processes
- // Files.writeString(sysrqTriggerP, "i");// send SIGKILL to all processes
- Files.writeString(sysrqTriggerP, "e");// flush data to disk
- Files.writeString(sysrqTriggerP, "u");// unmount
- if (reboot)
- Files.writeString(sysrqTriggerP, "b");
- else
- Files.writeString(sysrqTriggerP, "o");
- } catch (IOException e) {
- logger.log(ERROR, "Cannot shut down system", e);
- }
- }
-
- static void stopInitDServices() {
- for (int i = initDServices.size() - 1; i >= 0; i--) {
- String serviceName = initDServices.get(i);
- Path serviceInit = Paths.get("/etc/init.d/", serviceName);
- try {
- int exitCode = new ProcessBuilder(serviceInit.toString(), "stop").start().waitFor();
- if (exitCode != 0)
- logger.log(ERROR, "Service " + serviceName + " dit not stop properly");
- } catch (InterruptedException | IOException e) {
- logger.log(ERROR, "Cannot stop service " + serviceName, e);
- }
- }
- }
-
- /** A thread watching the login prompt. */
- static class LoginThread extends Thread {
- private boolean systemShuttingDown = false;
- private Process process = null;
-
- public LoginThread() {
- super("FREEd login prompt");
- setDaemon(true);
- Runtime.getRuntime().addShutdownHook(new Thread(() -> {
- systemShuttingDown = true;
- if (process != null)
- process.destroy();
- }));
- }
-
- @Override
- public void run() {
- boolean getty = true;
- prompt: while (!systemShuttingDown) {
- try {
- if (getty) {
- ProcessBuilder pb = new ProcessBuilder("/usr/sbin/getty", "38400", "tty2");
- process = pb.start();
- } else {
- Console console = System.console();
- console.readLine(); // type return once to activate login prompt
- console.printf("login: ");
- String username = console.readLine();
- username = username.trim();
- if ("".equals(username))
- continue prompt;
- ProcessBuilder pb = new ProcessBuilder("su", "--login", username);
- pb.redirectError(ProcessBuilder.Redirect.INHERIT);
- pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
- pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
- process = pb.start();
- }
- Runtime.getRuntime().addShutdownHook(new Thread(() -> process.destroy()));
- try {
- process.waitFor();
- } catch (InterruptedException e) {
- process.destroy();
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- process = null;
- }
- }
- }
-
- }
-}