From 1c7069c73789698551169f5fa10c765a12b9c71b Mon Sep 17 00:00:00 2001 From: Nathan McRae Date: Thu, 29 May 2025 00:45:41 -0700 Subject: [PATCH] Add linux schedule management functions --- .../numbersstation/LinuxScheduler.java | 103 ++++++++++++++++++ .../numbersstation/StationSettings.java | 9 +- 2 files changed, 108 insertions(+), 4 deletions(-) diff --git a/src/main/java/name/nathanmcrae/numbersstation/LinuxScheduler.java b/src/main/java/name/nathanmcrae/numbersstation/LinuxScheduler.java index 9bf3ff3..ddccc63 100644 --- a/src/main/java/name/nathanmcrae/numbersstation/LinuxScheduler.java +++ b/src/main/java/name/nathanmcrae/numbersstation/LinuxScheduler.java @@ -60,6 +60,10 @@ public class LinuxScheduler { matcher.appendReplacement(sb, ""); } + if (foundMultiple) { + logger.log(Level.WARNING, "Found multiple instances of '" + taskName + "' prior to replacement"); + } + matcher.appendTail(sb); } else { sb.append(currentCrontab); @@ -95,6 +99,105 @@ public class LinuxScheduler { return Results.success(true); } + public static Result scheduleExists(StationSettings settings) { + try { + String taskName = "numbers-station-main_" + settings.getName(); + + Process listProcess = new ProcessBuilder("crontab", "-l").start(); + if (!listProcess.waitFor(5, TimeUnit.SECONDS)) { + String message = "Failed to query " + taskName + " task: process timed out"; + logger.log(Level.SEVERE, message); + return Results.failure(message); + } + + Pair output = WindowsScheduler.captureProcessOutput(listProcess); + if(listProcess.exitValue() != 0) { + String message = "Failed to get user id. Exit code: " + listProcess.exitValue() + ". stdout: " + output.getKey() + "\n\tstderr: " + output.getValue(); + logger.log(Level.SEVERE, message); + return Results.failure(message); + } + + String currentCrontab = output.getKey(); + + String cronEntry = "# " + taskName + "\n" + cronExpression(settings) + "\n\n"; + + Pattern pattern = Pattern.compile("# " + taskName + "\\n[^\\n]+\\n\\n"); + + Matcher matcher = pattern.matcher(currentCrontab); + + if (matcher.find()) { + return Results.success(true); + } else { + return Results.success(true); + } + } catch (CronExpressionException | IOException | InterruptedException e) { + String message = "Exception while checking for schedule"; + logger.log(Level.SEVERE, message, e); + return Results.failure(message); + } + } + + public static Result removeSchedule(StationSettings settings) { + try { + String taskName = "numbers-station-main_" + settings.getName(); + + Process listProcess = new ProcessBuilder("crontab", "-l").start(); + if (!listProcess.waitFor(5, TimeUnit.SECONDS)) { + String message = "Failed to query " + taskName + " task: process timed out"; + logger.log(Level.SEVERE, message); + return Results.failure(message); + } + + Pair output = WindowsScheduler.captureProcessOutput(listProcess); + if(listProcess.exitValue() != 0) { + String message = "Failed to get user id. Exit code: " + listProcess.exitValue() + ". stdout: " + output.getKey() + "\n\tstderr: " + output.getValue(); + logger.log(Level.SEVERE, message); + return Results.failure(message); + } + + String currentCrontab = output.getKey(); + + String cronEntry = "# " + taskName + "\n" + cronExpression(settings) + "\n\n"; + + Pattern pattern = Pattern.compile("# " + taskName + "\\n[^\\n]+\\n\\n"); + + Matcher matcher = pattern.matcher(currentCrontab); + + StringBuilder sb = new StringBuilder(); + + while (matcher.find()) { + matcher.appendReplacement(sb, ""); + } + + String newCrontab = sb.toString(); + + Process addProcess = new ProcessBuilder("crontab", "-").start(); + Writer w = new OutputStreamWriter(addProcess.getOutputStream(), "UTF-8"); + System.out.println(newCrontab); + w.write(newCrontab); + w.flush(); + w.close(); + if (!addProcess.waitFor(5, TimeUnit.SECONDS)) { + String message = "Failed to register " + taskName + " task: process timed out"; + logger.log(Level.SEVERE, message); + return Results.failure(message); + } + + if(addProcess.exitValue() != 0) { + Pair addOutput = WindowsScheduler.captureProcessOutput(addProcess); + String message = "Failed to get user id. Exit code: " + addProcess.exitValue() + ". stdout: " + addOutput.getKey() + ". stderr: " + addOutput.getValue(); + logger.log(Level.SEVERE, message); + return Results.failure(message); + } + } catch (CronExpressionException | IOException | InterruptedException e) { + String message = "Exception while removing schedule"; + logger.log(Level.SEVERE, message, e); + return Results.failure(message); + } + + return Results.success(true); + } + /** * * * * * * {command to execute} * | | | | | diff --git a/src/main/java/name/nathanmcrae/numbersstation/StationSettings.java b/src/main/java/name/nathanmcrae/numbersstation/StationSettings.java index 637a7e7..53d478c 100644 --- a/src/main/java/name/nathanmcrae/numbersstation/StationSettings.java +++ b/src/main/java/name/nathanmcrae/numbersstation/StationSettings.java @@ -81,8 +81,7 @@ public class StationSettings { if (osName.contains("win")) { WindowsScheduler.removeSchedule(temp); } else if (osName.contains("nix") || osName.contains("nux") || osName.contains("aix")) { - // TODO: linux - logger.log(Level.SEVERE, "Unsupported OS " + osName); + LinuxScheduler.removeSchedule(temp); } else { logger.log(Level.SEVERE, "Unsupported OS " + osName); } @@ -101,8 +100,10 @@ public class StationSettings { return true; } } else if (osName.contains("nix") || osName.contains("nux") || osName.contains("aix")) { - // TODO: linux - logger.log(Level.SEVERE, "Unsupported OS " + osName); + Result result = LinuxScheduler.scheduleExists(temp); + if (result.hasSuccess() && result.getSuccess().get()) { + return true; + } } else { logger.log(Level.SEVERE, "Unsupported OS " + osName); }