//#! /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.nio.file.Paths;
import java.util.Enumeration;
import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicInteger;
import org.argeo.init.Service;
-import jdk.jshell.tool.JavaShellToolBuilder;
import sun.misc.Signal;
class FreedPid1 {
+ final static AtomicInteger runLevel = new AtomicInteger(-1);
+
+ private final static Logger logger = System.getLogger(FreedPid1.class.getName());
+
public static void main(String... args) {
try {
final long pid = ProcessHandle.current().pid();
- System.out.println("FREEd Init daemon starting with pid " + pid + "...");
-// System.out.println(System.getProperty("user.dir"));
-// System.out.println(System.getProperty("user.name"));
-// System.out.println(System.getProperty("user.home"));
-
- // System.setProperty("user.dir", "/tmp");
- for (Object key : new TreeMap<>(System.getProperties()).keySet()) {
- System.out.println(key + "=" + System.getProperty(key.toString()));
- }
-
- flushStd();
-
Signal.handle(new Signal("TERM"), (signal) -> {
System.out.println("SIGTERM caught");
System.exit(0);
System.exit(0);
});
- if (args.length > 0 && ("1".equals(args[0]) //
+ boolean isSystemInit = pid == 1 || pid == 2;
+
+ if (isSystemInit && args.length > 0 && ("1".equals(args[0]) //
|| "single".equals(args[0]) //
|| "emergency".equals(args[0]))) {
- // TODO check if we can remove dependency to management
- String classpath = ManagementFactory.getRuntimeMXBean().getClassPath();
- String feedbackMode = "concise";
- // TODO --startup script
- JavaShellToolBuilder builder = JavaShellToolBuilder.builder();
- try {
- builder.start("--execution", "direct", "--class-path", classpath, "--feedback", feedbackMode);
- } catch (Exception e) {
- e.printStackTrace();
- System.err.flush();
- System.exit(1);
- return;
+ 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 {
- // init Linux services
+ 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...");
+ new ProcessBuilder("/usr/bin/kill", "1").start();
+ 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 down system...");
+ Path sysrqP = Paths.get("/proc/sys/kernel/sysrq");
+ Files.writeString(sysrqP, "1");
+ Path sysrqTriggerP = Paths.get("/proc/sysrq-trigger");
+ Files.writeString(sysrqTriggerP, "b");
+ System.exit(0);
+ } 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");
+ // networking
initSysctl();
- startInitDService("networking");
- flushStd();
+ startInitDService("networking", true);
+ if (!waitForNetwork(30 * 1000))
+ logger.log(ERROR, "No network available");
- if (!waitForNetwork(30 * 1000)) {
- System.err.println("No network available");
- }
- flushStd();
+ // NSS services
+ startInitDService("nslcd", false);// Note: nslcd fails to stop
- startInitDService("nslcd");
+ // 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) {
- System.err.println("Unexpected exception in free-pid1 init, shutting down... " + e.getMessage());
- e.printStackTrace();
- flushStd();
+ logger.log(ERROR, "Unexpected exception in free-pid1 init, shutting down... ", e);
System.exit(1);
-// if(e instanceof Error)
-// throw e;
}
}
}
}
- static void startInitDService(String serviceName) {
+ static void startInitDService(String serviceName, boolean stopOnShutdown) {
Path serviceInit = Paths.get("/etc/init.d/", serviceName);
if (Files.exists(serviceInit))
try {
new ProcessBuilder(serviceInit.toString(), "start").start().waitFor();
- System.out.println("Service " + serviceName + " started");
- Runtime.getRuntime().addShutdownHook(new Thread(() -> {
- try {
- new ProcessBuilder(serviceInit.toString(), "stop").start().waitFor();
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- }
- }, "Stop service " + serviceName));
+ logger.log(DEBUG, "Service " + serviceName + " started");
+ if (stopOnShutdown)
+ 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
- System.out.println("Service " + serviceName + " not found and therefore not started");
+ 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;
- 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();
- for (InterfaceAddress addr : netInterface.getInterfaceAddresses()) {
- InetAddress inetAddr = addr.getAddress();
- if (!inetAddr.isLoopbackAddress() && !inetAddr.isLinkLocalAddress()) {
- try {
- if (inetAddr.isReachable((int) timeout)) {
- networkAvailable = true;
- System.out.println("Network available after " + duration + " ms. IP: " + inetAddr);
- break networkAvailable;
+ 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();
+ for (InterfaceAddress addr : netInterface.getInterfaceAddresses()) {
+ InetAddress inetAddr = addr.getAddress();
+ 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);
}
- } catch (IOException e) {
- System.err.println(
- "Cannot check whether " + inetAddr + " is reachable: " + e.getMessage());
}
}
}
+ } else {
+ throw new IllegalStateException("No network interface has been found");
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // silent
}
- } else {
- throw new IllegalStateException("No network interface has been found");
- }
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
}
+ } catch (Exception e) {
+ logger.log(ERROR, "Cannot check whether network is avialable", e);
}
return networkAvailable;
}
- static void flushStd() {
- System.err.flush();
- System.out.flush();
- }
+ /** 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() {
+ Console console = System.console();
+ console.readLine(); // type return once to activate login prompt
+ prompt: while (!systemShuttingDown) {
+ try {
+ 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;
+ }
+ }
+ }
+
+ }
}