/* * JobServClient * * v1.0 * * May 18, 2019 */ package JobServ; import io.grpc.netty.GrpcSslContexts; import io.grpc.ManagedChannel; import java.util.InputMismatchException; import io.grpc.ManagedChannelBuilder; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import javax.net.ssl.SSLException; import io.grpc.netty.NettyChannelBuilder; import java.util.Scanner; import java.io.File; /* * The JobServClient class extends the gRPC stub code * Additionally, it plugs a command line interface into the API code. */ public class JobServClient { private final String serversideTimeoutErrorMessage = "Timeout locking process control on server\n"+ "Server could be under heavy load\nConsider trying again."; private JobServClientAPIConnector api; private String[] programArgs; /* * Constructor * takes program arguments and an api connector object */ public JobServClient(String[] args, JobServClientAPIConnector api) { this.programArgs = args; this.api = api; } /* * getPidArg() * reentrant code was found in all commands except newjob * this function pulls the pid argument and wraps around the integer cast * returns -1 (an invalid PID) if bad index or unparsable int */ private int getPidArg(int index) { if (this.programArgs.length < index) { System.out.println("Improper formatting, try client --help"); return -1; } try { return Integer.parseInt(this.programArgs[index]); } catch (InputMismatchException e) { System.out.println(this.programArgs[index] + " is not a valid int, much less a valid pid"); return -1; } } /* * outputHelp() * writes help information about all commands in the shell to screen */ public static void outputHelp() { System.out.println("... new (command)\n"+ "Starts a new process on the server\n"+ "example: ./client key.pem cert.crt ca.crt localhost 8448 new echo hello world!\n\n"+ "... output (pid)\n"+ "Garners output from process on server\n"+ "example: ./client key.pem cert.crt ca.crt localhost 8448 output 0\n\n"+ "... status (pid)\n"+ "Returns whether process on server is running"+ "example: ./client key.pem cert.crt ca.crt localhost 8448 status 0\n\n"+ "... return (pid)\n"+ "Collects return code from remote process\n"+ "example: ./client key.pem cert.crt ca.crt localhost 8448 return 0\n\n"+ "... kill (pid)"+ "Immediately destroys remote process"+ "example: ./client key.pem cert.crt ca.crt localhost 8448 kill 0\n\n"); } /* * makeNewProcess * makes a new process */ public void makeNewProcess() { if (this.programArgs.length < 6) { System.out.println("Improper formatting, try client --help"); return; } String command = ""; for (int token = 6; token < this.programArgs.length; token++) { command += " " + this.programArgs[token]; } int newProcess = this.api.sendNewJobMessage(command); switch(newProcess) { case -1: System.out.println("Server failed to spawn process. Bad command."); break; case -2: // error logged by API Connector break; default: System.out.printf("Process started, assigned pid is %d\n", newProcess); break; } return; } /* * getOutput * gets output from a process */ public void getOutput() { int candidatePid = this.getPidArg(6); if (candidatePid < 0) { return; } String processOutput = this.api.getProcessOutput(candidatePid); System.out.println(processOutput); } /* * getStatus * gets the running status of a process */ public void getStatus() { int candidatePid = this.getPidArg(6); if (candidatePid < 0) { return; } int processStatus = this.api.getProcessStatus(candidatePid); switch(processStatus) { case 0: System.out.println("Process is running"); break; case 1: System.out.println("Process is not running"); break; case 2: System.out.println("A client killed the process already"); break; case 3: System.out.println("Process does not exist"); break; case 4: System.out.println(this.serversideTimeoutErrorMessage); break; } } /* * killProcess * kills a process */ public void killProcess() { int candidatePid = this.getPidArg(6); if (candidatePid < 0) { return; } int finalStatus = this.api.killProcess(candidatePid); switch(finalStatus) { case 0: System.out.println("Process is still running"); break; case 1: System.out.println("Process was killed"); break; case 2: System.out.println("Process does not exist"); break; case 3: System.out.println(this.serversideTimeoutErrorMessage); break; case 4: // error logged in API Connector break; } } /* * getReturn * gets return code from a process */ public void getReturn() { int candidatePid = this.getPidArg(6); if (candidatePid < 0) { return; } int returnCode = this.api.getProcessReturn(candidatePid); switch(returnCode){ case 256: System.out.println("Process is still running"); break; case 257: System.out.println("Process was killed manually by a client"); break; case 258: System.out.println("Process does not exist"); break; case 259: System.out.println(this.serversideTimeoutErrorMessage); break; case 260: // error logged in getProcesReturn break; default: System.out.println("Process Exit Code: " + Integer.toString(returnCode)); } } /* * main() * Client entrypoint * Parses arguments, initializes client, and calls the correct functions */ public static void main(String[] args) throws Exception { // check args if (args.length < 7) { System.out.println("Usage: $ ./jobserv-client privatekey, cert, truststore, host, port, command, args"); System.out.println("Or try $ ./jobserv-client help"); outputHelp(); return; } JobServClientAPIConnector api; try { SslContextBuilder builder = GrpcSslContexts.forClient(); builder.trustManager(new File(args[2])); builder.keyManager(new File(args[1]), new File(args[0])); ManagedChannel channel = NettyChannelBuilder.forAddress(args[3], Integer.parseInt(args[4])) .sslContext(builder.build()) .build(); api = new JobServClientAPIConnector(channel); // Likely bad port } catch (NumberFormatException e) { System.out.println("Invalid Port"); return; // bad cert or key format } catch (SSLException e) { System.out.println(e.getMessage()); return; } JobServClient client = new JobServClient(args, api); // parse remaining args switch (args[5]) { case "new": client.makeNewProcess(); break; case "output": client.getOutput(); break; case "status": client.getStatus(); break; case "kill": client.killProcess(); break; case "return": client.getReturn(); break; default: System.out.println("Improper command, try 'help'"); break; } } }