129 lines
4.1 KiB
Java
129 lines
4.1 KiB
Java
package de.ph87.mc.server;
|
|
|
|
import jakarta.annotation.Nullable;
|
|
import lombok.NonNull;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.context.ApplicationEventPublisher;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
@Slf4j
|
|
@Service
|
|
@RequiredArgsConstructor
|
|
public class ServerProcessHelper {
|
|
|
|
public static final String[] CMDLINE = {"java", "-jar", "server.jar"};
|
|
|
|
public static final String CMDLINE_STR = String.join(" ", CMDLINE);
|
|
|
|
private final ApplicationEventPublisher applicationEventPublisher;
|
|
|
|
public void updatePid(@NonNull final Server server) {
|
|
server.setPid(_readAndVerifyPid(server));
|
|
if (server.getPid() == null) {
|
|
deletePidFile(server);
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
private Long _readAndVerifyPid(@NonNull final Server server) {
|
|
if (!server.pidFile.exists()) {
|
|
return null;
|
|
}
|
|
|
|
final long pid;
|
|
try (final FileInputStream stream = new FileInputStream(server.pidFile)) {
|
|
pid = Long.parseLong(new String(stream.readAllBytes(), StandardCharsets.UTF_8));
|
|
} catch (IOException | NumberFormatException e) {
|
|
log.error("Failed to read pid-file: file={}, error={}", server.pidFile, e.getMessage());
|
|
return null;
|
|
}
|
|
|
|
if (!validateProcFile(server, pid)) {
|
|
return null;
|
|
}
|
|
|
|
return pid;
|
|
}
|
|
|
|
private static boolean validateProcFile(@NonNull final Server server, final long pid) {
|
|
final File procFile = new File("/proc/%d/cmdline".formatted(pid));
|
|
if (!procFile.exists()) {
|
|
log.warn("Server not running: {}", server.name);
|
|
return false;
|
|
}
|
|
|
|
try (final FileInputStream stream = new FileInputStream(procFile)) {
|
|
final String cmdline = new String(stream.readAllBytes(), StandardCharsets.UTF_8).replace((char) 0, ' ').trim();
|
|
if (!CMDLINE_STR.equals(cmdline)) {
|
|
log.error("cmdline of running Server does not match: pid={}, running={}, expected={}", pid, cmdline, CMDLINE_STR);
|
|
return false;
|
|
}
|
|
} catch (IOException | NumberFormatException e) {
|
|
log.error("Failed to read proc-file: file={}, error={}", procFile, e.getMessage());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void deletePidFile(@NonNull final Server server) {
|
|
server.setPid(null);
|
|
if (server.pidFile.delete()) {
|
|
log.info("PID-file removed: {}", server.pidFile);
|
|
applicationEventPublisher.publishEvent(server);
|
|
}
|
|
}
|
|
|
|
public void startProcess(@NonNull final Server server) {
|
|
if (server.isRunning()) {
|
|
return;
|
|
}
|
|
log.info("Starting Server: {}", server.name);
|
|
final ProcessBuilder builder = new ProcessBuilder(CMDLINE);
|
|
builder.directory(server.directory);
|
|
try {
|
|
final Process process = builder.start();
|
|
server.setPid(process.pid());
|
|
writePid(server, process.pid());
|
|
} catch (IOException e) {
|
|
log.error("Failed to start server: error={}, name={}", e.getMessage(), server.name);
|
|
}
|
|
}
|
|
|
|
public void stopProcess(@NonNull final Server server) {
|
|
if (!server.isRunning()) {
|
|
return;
|
|
}
|
|
new Thread(() -> {
|
|
try {
|
|
log.info("Stopping Server: {}", server.name);
|
|
new ProcessBuilder("kill", "-15", server.getPid() + "").start();
|
|
while (server.getPid() != null && validateProcFile(server, server.getPid())) {
|
|
//noinspection BusyWait
|
|
Thread.sleep(1000);
|
|
}
|
|
deletePidFile(server);
|
|
} catch (IOException | InterruptedException e) {
|
|
log.error("Failed to stop server: error={}, name={}", e.getMessage(), server.name);
|
|
}
|
|
}).start();
|
|
}
|
|
|
|
private void writePid(@NonNull final Server server, final long pid) throws IOException {
|
|
final File file = server.pidFile;
|
|
try (final FileOutputStream stream = new FileOutputStream(file)) {
|
|
stream.write("%d".formatted(pid).getBytes(StandardCharsets.UTF_8));
|
|
}
|
|
log.info("PID-file written: file={} = {}", server.pidFile, pid);
|
|
applicationEventPublisher.publishEvent(server);
|
|
}
|
|
|
|
}
|