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 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();
if (runLevel.get() == 0) {// shutting down the whole system
if (!isSystemInit) {
logger.log(INFO, "Shutting down system...");
- new ProcessBuilder("/usr/bin/kill", "1").start();
+ shutdown(false);
System.exit(0);
} else {
logger.log(ERROR, "Cannot start at run level " + runLevel.get());
}
} 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);
+ 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);
- if (!waitForNetwork(30 * 1000))
+// 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
} catch (Throwable e) {
logger.log(ERROR, "Unexpected exception in free-pid1 init, shutting down... ", e);
System.exit(1);
+ } finally {
+ stopInitDServices();
}
}
Path serviceInit = Paths.get("/etc/init.d/", serviceName);
if (Files.exists(serviceInit))
try {
- new ProcessBuilder(serviceInit.toString(), "start").start().waitFor();
- logger.log(DEBUG, "Service " + serviceName + " started");
+ 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)
- Runtime.getRuntime().addShutdownHook(new Thread(() -> {
- try {
- new ProcessBuilder(serviceInit.toString(), "stop").start().waitFor();
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- }
- }, "FREEd stop service " + serviceName));
+ 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();
}
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)) {
throw new IllegalStateException("No network interface has been found");
}
try {
- Thread.sleep(100);
+ Thread.sleep(1000);
} catch (InterruptedException e) {
// silent
}
}
} catch (Exception e) {
- logger.log(ERROR, "Cannot check whether network is avialable", 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;
@Override
public void run() {
- Console console = System.console();
- console.readLine(); // type return once to activate login prompt
+ boolean getty = true;
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();
+ 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();