package ru.kirillius.pf.sdn; import lombok.Getter; import lombok.SneakyThrows; import ru.kirillius.pf.sdn.External.API.Components.FRR; import ru.kirillius.pf.sdn.External.API.Components.OVPN; import ru.kirillius.pf.sdn.External.API.Components.TDNS; import ru.kirillius.pf.sdn.External.API.HEInfoProvider; import ru.kirillius.pf.sdn.core.*; import ru.kirillius.pf.sdn.core.Auth.AuthManager; import ru.kirillius.pf.sdn.core.Auth.TokenService; import ru.kirillius.pf.sdn.core.Networking.BGPInfoService; import ru.kirillius.pf.sdn.core.Networking.NetworkingService; import ru.kirillius.pf.sdn.core.Subscription.SubscriptionService; import ru.kirillius.pf.sdn.core.Util.Wait; import ru.kirillius.pf.sdn.web.WebService; import ru.kirillius.utils.logging.SystemLogger; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import static ru.kirillius.pf.sdn.core.Util.CommandLineUtils.getArgument; /** * Entry point for the SDN control application that wires configuration, services, and shutdown handling. */ public class App implements Context, Closeable { protected final static String CTX = App.class.getSimpleName(); static { SystemLogger.initializeLogging(Level.INFO, List.of(InMemoryLogHandler.class)); } private final AtomicBoolean shouldRestart = new AtomicBoolean(false); private final AtomicBoolean running = new AtomicBoolean(true); @Getter private final ContextEventsHandler EventsHandler = new ContextEventsHandler(); @Getter private final ServiceManager serviceManager; @Getter private final LauncherConfig launcherConfig; @Getter private final Config config; /** * Loads configuration from disk, creating a default file if missing. */ private Config loadConfig() { Config loadedConfig = null; try { loadedConfig = Config.load(launcherConfig.getConfigFile()); } catch (IOException e) { loadedConfig = new Config(); try { Config.store(loadedConfig, launcherConfig.getConfigFile()); } catch (IOException ex) { throw new RuntimeException(ex); } } return loadedConfig; } /** * Instantiates all application services and performs initial wiring. */ private ServiceManager loadServiceManager() { var manager = new ServiceManager(this, List.of(AuthManager.class, ComponentHandlerService.class, TokenService.class, AppUpdateService.class, BGPInfoService.class, NetworkingService.class, SubscriptionService.class, ResourceUpdateService.class, WebService.class)); manager.getService(BGPInfoService.class).setProvider(new HEInfoProvider()); return manager; } /** * Ensures an admin password exists, defaulting to {@code admin} when missing. */ private void checkDefaultPassword() { if (config.getPasswordHash() == null || config.getPasswordHash().isEmpty()) { SystemLogger.error("There is no password for admin. Setting default password: admin", CTX); getServiceManager().getService(AuthManager.class).updatePassword("admin"); } } /** * Constructs the application binding to the provided launcher configuration. */ @SneakyThrows public App(LauncherConfig launcherConfig) { this.launcherConfig = launcherConfig; config = loadConfig(); if (config.isDisplayDebuggingInfo()) { SystemLogger.setExceptionDumping(true); } serviceManager = loadServiceManager(); serviceManager.getService(SubscriptionService.class).triggerUpdate(); checkDefaultPassword(); serviceManager.getService(ComponentHandlerService.class).syncComponentsWithConfig(); serviceManager.getService(ResourceUpdateService.class).start(); } /** * Application entry point. */ public static void main(String[] args) { try (var app = new App(LauncherConfig.builder() .configFile(new File(getArgument("c", args))) .appLibrary(new File(getArgument("l", args))) .repository(getArgument("r", args)) .availableComponentClasses(List.of(FRR.class, OVPN.class, TDNS.class)).build())) { Wait.when(app.running::get); if (app.shouldRestart.get()) { System.exit(303); } else { System.exit(0); } } catch (Exception e) { SystemLogger.error("Unhandled error", CTX, e); System.exit(1); } } /** * Requests the application to exit, optionally restarting. */ public void requestExit(boolean restart) { running.set(false); shouldRestart.set(restart); } /** * Closes all managed services. */ @Override public void close() throws IOException { serviceManager.close(); } }