Add Linux scheduler

This commit is contained in:
Nathan McRae 2025-03-10 22:54:34 -07:00
parent 0594a64812
commit 7e2d4ca1a4
2 changed files with 153 additions and 1 deletions

View File

@ -0,0 +1,152 @@
package name.nathanmcrae.numbersstation;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.core.Results;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javafx.util.Pair;
public class LinuxScheduler {
private static final Logger logger = Logger.getLogger(Main.class.getName());
public static Result<Boolean, String> registerSchedule(StationSettings settings) {
try {
String taskName = "numbers-station-main_" + settings.getName();
// TODO: assume it's on the PATH
Process listProcess = new ProcessBuilder("/usr/bin/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<String, String> 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();
if (matcher.find()) {
matcher.appendReplacement(sb, cronEntry);
boolean foundMultiple = false;
while (matcher.find()) {
foundMultiple = true;
matcher.appendReplacement(sb, "");
}
matcher.appendTail(sb);
} else {
sb.append(currentCrontab);
sb.append(cronEntry);
}
String newCrontab = sb.toString();
// TODO: assume it's on the PATH
Process addProcess = new ProcessBuilder("/usr/bin/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<String, String> 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 registering schedule";
logger.log(Level.SEVERE, message, e);
return Results.failure(message);
}
// Path cronDPath = Paths.get("/etc/cron.d");
// if (!Files.exists(cronDPath)) {
// String message = "/etc/cron.d does not exist, cannot create cron entry. Select 'Manage schedule externally' and set up scheduling as desired.";
// logger.log(Level.SEVERE, message);
// return Results.failure(message);
// }
// Path cronPath = cronDPath.resolve(settings.safeName());
// try {
// Files.write(cronPath, cronEntry(settings).getBytes(StandardCharsets.UTF_8));
// } catch (Exception e) {
// String message = "Failed to write cron file at '" + cronPath.toString() + "'";
// logger.log(Level.SEVERE, message, e);
// return Results.failure(message);
// }
return Results.success(true);
}
/**
* * * * * * {command to execute}
* | | | | |
* | | | | day of the week (06) (Sunday to Saturday;
* | | | month (112) 7 is also Sunday on some systems)
* | | day of the month (131)
* | hour (023)
* minute (059)
*/
public static String cronExpression(StationSettings settings) throws CronExpressionException {
String minute = Integer.toString(settings.getScheduleStartTime().getMinute());
String hour = Integer.toString(settings.getScheduleStartTime().getHour());
String dayOfMonth = Integer.toString(settings.getScheduleStartDate().getDayOfMonth());
String dayOfWeek = Integer.toString(settings.getScheduleStartDate().getDayOfMonth());
String scheduleString = "";
switch(settings.getMessagePeriod()) {
case DAILY:
scheduleString = minute + " " + hour + " * * *";
break;
case WEEKLY:
scheduleString = minute + " " + hour + " * * " + dayOfWeek;
break;
case MONTHLY:
scheduleString = minute + " " + hour + " " + dayOfMonth + " * *";
break;
default:
throw new CronExpressionException("Message period not implemented: '" + settings.getMessagePeriod() + "'");
}
// TODO: figure out actual invocation
return scheduleString + " /home/nathanmcrae/personal_root/projects/numbers-station/run.sh --station \"" + settings.getName() + "\"";
}
public static class CronExpressionException extends Exception {
public CronExpressionException(String message) {
super(message);
}
}
}

View File

@ -454,7 +454,7 @@ public class StationSettingsController {
if (osName.contains("win")) {
WindowsScheduler.registerSchedule(settings);
} else if (osName.contains("nix") || osName.contains("nux") || osName.contains("aix")) {
logger.log(Level.SEVERE, "Unsupported OS " + osName);
LinuxScheduler.registerSchedule(settings);
} else {
logger.log(Level.SEVERE, "Unsupported OS " + osName);
// TODO: Alert