From 68eabf65107a1e8be6d3b7bd467b0d9aa30dd7a5 Mon Sep 17 00:00:00 2001 From: Aidan Hahn Date: Fri, 17 May 2019 18:22:38 -0700 Subject: [PATCH] refactor API based on design document feedback --- src/main/java/JobServ/JobServClient.java | 292 ++++++++++++++--------- src/main/java/JobServ/JobServServer.java | 46 +++- src/main/proto/jobserv.proto | 32 +-- 3 files changed, 234 insertions(+), 136 deletions(-) diff --git a/src/main/java/JobServ/JobServClient.java b/src/main/java/JobServ/JobServClient.java index dfc6ff2..d2170af 100644 --- a/src/main/java/JobServ/JobServClient.java +++ b/src/main/java/JobServ/JobServClient.java @@ -12,14 +12,6 @@ import java.util.Scanner; // GRPC Client Class public class JobServClient { - // RequestMessage types - // TODO: refactor to enum? - public static final int OUTPUT = 0; - public static final int RETURN = 1; - public static final int PAUSE = 2; - public static final int RESUME = 3; - public static final int STATUS = 4; - /* The client should not use the same logging module as the server. * In a more robust product the server logging module will take advantage of system level * log aggregators such as journalctl, which the client should not be writing to on the users system @@ -52,47 +44,20 @@ public class JobServClient { channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); } - // sends the server a RequestMessage, blocks until response - // returns an integer representing process status - private int makeProcessRequest(int pid, int requestType) { - logger.info("[+] Sending request"); - - RequestMessage request = RequestMessage.newBuilder() - .setPID(pid) - .setRequestType(requestType) - .build(); - JobStatusMessage response; - - try { - // blocking network operation - response = blockingStub.getStatusDetail(request); - } catch (StatusRuntimeException e) { - /* if this was using an async stub it might be - * worthwhile to include the PID and request type - */ - logger.log(Level.WARNING, "[-]Status Request Failed: %s", e.getStatus()); - return -1; - } - - // return process status - return response.getProcessStatus(); - } - // sends the server a request for output from PID // different from getProcessStatus in output expected from the server // returns process output as string public String getProcessOutput(int pid) { logger.info("[+] requesting output"); - RequestMessage request = RequestMessage.newBuilder() - .setPID(pid) - .setRequestType(OUTPUT) + RequestMessage request = PIDMessage.newBuilder() + .setPid(pid) .build(); OutputMessage response; try { // blocking network operation - response = blockingStub.getJobOutput(request); + response = blockingStub.getOutput(request); } catch (StatusRuntimeException e) { logger.log(Level.WARNING, "[-] Request for output failed: %s", e.getStatus()); // TODO: refactor this out by throwing here and catching in the shell @@ -111,119 +76,226 @@ public class JobServClient { NewJobMessage request = NewJobMessage.newBuilder() .setCommand(command) .build(); - JobStatusMessage response; + PIDMessage response; try { // blocking network operation - response = blockingStub.makeNewJob(request); + response = blockingStub.newJob(request); } catch (StatusRuntimeException e) { logger.log(Level.WARNING, "[-] Request for new job failed!"); return -1; } - if(response.getPID() == -1) { + if(response.getpid() == -1) { logger.log(Level.WARNING, "New job creation failed server side!"); } - return response.getPID(); + return response.getPid(); + } + + // requests running status of job + // returns true if job still running else false + public bool getProcessStatus(int pid) { + logger.info("[+] Requesting status of a job"); + + PIDMessage request = PIDMessage.newBuilder() + .setPid(pid) + .build(); + StatusMessage response; + + try { + // blocking network operation + response = blockingStub.getStatus(request); + } catch (StatusRuntimeException e) { + logger.log(Level.WARNING, "[-] Request for status failed!"); + } + + return response.getIsRunning(); + } + + // sends PID to server expecting the return cod eof a process + // function returns a 0-255 return code or 277 if still running + // or 278 if error in API + public int getProcessReturn(int pid) { + logger.info("[+] Requesting return code of a job"); + + PIDMessage request = PIDMessage.newBuilder() + .setPid(pid) + .build(); + ReturnMessage response; + + try { + // blocking network operation + response = blockingStub.getReturn(request); + } catch (StatusRuntimeException e) { + logger.log(Level.WARNING, "[-] Failed to get return code!"); + return 278; + } + + return response.getProcessReturnCode(); + } + + // send a PID to be killed, returns nothing + // logs warning if job status comes back still running + public void killProcess(int pid) { + logger.info("[+] Killing a job"); + + PIDMessage request = PIDMessage.newBuilder() + .setPid(pid) + .build(); + StatusMessage response; + + try { + // blocking network operation + response = blockingStub.killJob(request); + } catch (StatusRuntimeException e) { + logger.log(Level.WARNING, "[-] Failed to send request!"); + return; + } + + if (response.getIsRunning()) { + logger.log(Level.WARNING, "[-] Server failed to kill job!"); + } } // Client entrypoint public static void main(String[] args) throws Exception { + if (args.length == 1 && args[0] == "--help"){ + outputHelp(); + } + // check args - if(args.length != 3) { - System.out.println("Usage: $ jobservclient host port"); + if (args.length < 3) { + System.out.println("Usage: $ jobservclient host port command"); + System.out.println("Or try client --help") return; } // start client (or fail if port is improperly formatted) JobServClient client; try { - client = new JobServClient(args[1], Integer.parseInt(args[2])); + client = new JobServClient(args[0], Integer.parseInt(args[1])); + } catch (NumberFormatException e) { System.out.println("Invalid Port"); return; } - Scanner reader = new Scanner(System.in); - while(true) { - System.out.print("> "); - int pid, status; - String input = reader.next(); - switch (input) { - - case "pause": - pid = getPid(); - status = client.makeProcessRequest(pid, PAUSE); - // TODO: parse status return - break; - - case "resume": - pid = getPid(); - status = client.makeProcessRequest(pid, RESUME); - // TODO: parse status return - break; - + // declare up here so that multiple switch cases can use it + int candidatePid; + switch (args[2]) { case "new": - System.out.println("Enter a command"); - String command = reader.next(); - pid = client.sendNewJobMessage(command); - System.out.println(String.format("New process on server: %d", pid)); + if (args.length < 4) { + System.out.println("Improper formatting, try client --help"); + break; + } + + String command = ""; + for (int token = 3; i < args.length; i++) { + command += " " + args[token]; + } + + int newProcess = client.sendNewJobMessage(command); + System.out.println("Process started, assigned pid is %d", newProcess); break; case "output": - pid = getPid(); - String out = client.getProcessOutput(pid); - System.out.println(out); + if (args.length != 4) { + System.out.println("Improper formatting, try client --help"); + break; + } + + try { + candidatePid = Integer.parseInt(args[3]); + } catch (InputMismatchException e) { + System.out.println(args[3] + " is not a valid int, much less a valid pid"); + break; + } + + String processOutput = client.getProcessOutput(candidatePid); + System.out.println(processOutput); break; case "status": - pid = getPid(); - status = client.makeProcessRequest(pid, STATUS); - System.out.println(String.format("Current status of program is: %d", status)); + if (args.length != 4) { + System.out.println("Improper formatting, try client --help"); + break; + } + + try { + candidatePid = Integer.parseInt(args[3]); + + } catch (InputMismatchException e) { + System.out.println(args[3] + " is not a valid int, much less a valid pid"); + break; + } + + Boolean processStatus = client.getProcessStatus(candidatePid); + System.out.println("Process is currently running? %b", processStatus); + break; + + case "kill": + if (args.length != 4) { + System.out.println("Improper formatting, try client --help"); + break; + } + + try { + candidatePid = Integer.parseInt(args[3]); + + } catch (InputMismatchException e) { + System.out.println(args[3] + " is not a valid int, much less a valid pid"); + break; + } + + client.getProcessOutput(candidatePid); + System.out.println("End process request recieved!"); break; case "return": - pid = getPid(); - status = client.makeProcessRequest(pid, RETURN); - System.out.println(String.format("Exit code of process is: %d", status)); - break; - - case "quit": - reader.close(); - return; - - case "help": - System.out.println("pause: pauses a process on the server"); - System.out.println("resume: resumes a process on the server"); - System.out.println("new: starts a new process on the server"); - System.out.println("output: garners (new) output from a process on the server"); - System.out.println("status: outputs the current status of a program on the server"); - System.out.println("return: outputs exit code of a process on the server"); - System.out.println("quit: exits this server"); - break; - - default: - System.out.println("Improper output, try 'help'"); - break; - } - } - - private static int getPid(Scanner reader) { - System.out.println("Enter a PID"); - int pid; - - while(true) { - try { - pid = reader.nextInt(); - - } catch (InputMismatchException e) { - System.out.println("That was not a valid integer"); - continue; + if (args.length != 4) { + System.out.println("Improper formatting, try client --help"); + break; } + try { + candidatePid = Integer.parseInt(args[3]); + + } catch (InputMismatchException e) { + System.out.println(args[3] + " is not a valid int, much less a valid pid"); + break; + } + + int returnCode = client.getProcessOutput(candidatePid); + + if (returnCode == 277) { + System.out.println("Process is still running"); + break; + + } else if (returnCode == 278) { + System.out.println("RPC Call error!"); + break; + + } else { + System.out.println("Process Exit Code: %d", returnCode); + } + + default: + System.out.println("Improper command, try 'help'"); break; } } + + public static void outputHelp() { + System.out.println("... new (command)"); + System.out.println("Starts a new process on the server") + System.out.println("... output (pid)"); + System.out.println("Garners output from process on server"); + System.out.println("... status (pid)"); + System.out.println("Returns whether process on server is running"); + System.out.println("... return (pid)"); + System.out.println("Collects return code from remote process"); + System.out.println("... kill (pid)"); + System.out.println("Immediately destroys remote process"); } } diff --git a/src/main/java/JobServ/JobServServer.java b/src/main/java/JobServ/JobServServer.java index 985db88..6f0211b 100644 --- a/src/main/java/JobServ/JobServServer.java +++ b/src/main/java/JobServ/JobServServer.java @@ -56,34 +56,56 @@ public class JobServServer { static class ShellServerImpl extends ShellServerGrpc.ShellServerImplBase { @Override - public void getStatusDetail(RequestMessage request, - StreamObserver responder) { + public void getStatus(PIDMessage request, + StreamObserver responder) { // TODO: Implement job controller, check request type, do the thing - JobStatusMessage reply = JobStatusMessage.newBuilder() - .setPID(-1) - .setProcessStatus(-1) + StatusMessage reply = StatusMessage.newBuilder() + .setStatusCode(-1) .build(); responder.onNext(reply); responder.onCompleted(); } @Override - public void getJobOutput(RequestMessage request, - StreamObserver responder) { + public void getOutput(PIDMessage request, + StreamObserver responder) { // TODO: Implement job controller, get output, do the thing OutputMessage reply = OutputMessage.newBuilder() + .setOutput("test data") .build(); responder.onNext(reply); responder.onCompleted(); } @Override - public void makeNewJob(NewJobMessage request, - StreamObserver responder) { + public void newJob(NewJobMessage request, + StreamObserver responder) { // TODO: Implement job controller, Start Job, do the thing - JobStatusMessage reply = JobStatusMessage.newBuilder() - .setPID(-1) - .setProcessStatus(-1) + PIDMessage reply = PIDMessage.newBuilder() + .setPid(-1) + .build(); + responder.onNext(reply); + responder.onCompleted(); + } + + @Override + public void getReturn(PIDMessage request, + StreamObserver responder) { + // TODO: Implement job controller, get return code + ReturnMessage reply = ReturnMessage.newBuilder() + .setprocessReturnCode(277) + .build(); + responder.onNext(reply); + responder.onCompleted(); + } + + @Override + public void killJob(PIDMessage request, + StreamObserver responder) { + // TODO: implement job controller, do the thing + // TODO: kill job here + ReturnMessage reply = ReturnMessage.newBuilder() + .setprocessReturnCode(-1) .build(); responder.onNext(reply); responder.onCompleted(); diff --git a/src/main/proto/jobserv.proto b/src/main/proto/jobserv.proto index 5f71bca..da55ece 100644 --- a/src/main/proto/jobserv.proto +++ b/src/main/proto/jobserv.proto @@ -8,25 +8,29 @@ option objc_class_prefix = "JSV"; package JobServ; service ShellServer { - rpc getStatusDetail (RequestMessage) returns (JobStatusMessage) {} - rpc getJobOutput (RequestMessage) returns (OutputMessage) {} - rpc makeNewJob (NewJobMessage) returns (JobStatusMessage) {} + rpc getStatus (PIDMessage) returns (StatusMessage) {} + rpc getReturn (PIDMessage) returns (ReturnMessage) {} + rpc getOutput (PIDMessage) returns (OutputMessage) {} + rpc killJob (PIDMessage) returns (StatusMessage) {} + rpc newJob (NewJobMessage) returns (PIDMessage) {} } -message RequestMessage { - int32 PID = 1; - int32 RequestType = 2; +message StatusMessage { + bool IsRunning = 1 } -message NewJobMessage { - string Command = 1; -} - -message JobStatusMessage { - int32 PID = 1; // linux PIDs, by default, go up to 2^22 - int32 ProcessStatus = 2; // TODO: figure out smallest possible data size for this +message ReturnMessage { + int32 ProcessReturnCode = 1; } message OutputMessage { - string ProcessOutput = 2; + string Output = 1; +} + +message NewJobMessage { + string Command = 1; +} + +message PIDMessage { + int32 Pid = 1; }