# Jsch.pm: Class Used for Remote Access with JSCH package RDA::Driver::Jsch; # $Id: Jsch.pm,v 1.40 2015/10/30 09:59:39 RDA Exp $ # ARCS: $Header: /home/cvs/cvs/RDA_8/src/scripting/lib/RDA/Driver/Jsch.pm,v 1.40 2015/10/30 09:59:39 RDA Exp $ # # Change History # 20151030 MSC Enhance the tracing mechanisms. =head1 NAME RDA::Driver::Jsch - Class Used for Remote Access using JSCH =head1 SYNOPSIS require RDA::Driver::Jsch; =head1 DESCRIPTION The objects of the C class are used for execution remote access requests using Java Secure Channel (JSCH). The following methods are available: =cut use strict; BEGIN { use Exporter; use IO::File; use RDA::Text qw(debug get_string); use RDA::Object::Java; use RDA::Object::Rda qw($APPEND $CREATE $FIL_PERMS); } # Define the global public variables use vars qw($STRINGS $VERSION @EXPORT_OK @ISA %INLINE); $VERSION = sprintf('%d.%02d', q$Revision: 1.40 $ =~ /(\d+)\.(\d+)/); @EXPORT_OK = qw(%INLINE); @ISA = qw(Exporter); # Define the Java interface ## no critic (Numbered,Quoted) my $VER = '1.4'; my $PKG1 = 'com.jcraft.jsch'; my $PKG2 = 'oracle.sysman.rda.jsch'; my $NAM = 'Rda2Jsch'; my $COD = < 0) {end = lin.lastIndexOf("'"); if ((nxt = beg + 2) <= end) ctx.put(lin.substring(0, beg), lin.substring(nxt, end)); else {key = lin.substring(0, beg); val = new StringBuffer(); val.append(lin.substring(nxt)).append(EOL); while ((lin = stdin.readLine()) != null && (end = lin.lastIndexOf("'")) < 0) val.append(lin).append(EOL); if (lin == null) break; ctx.put(key, val.append(lin.substring(0, end)).toString()); } } else if (lin.startsWith("/")) {// Execute the request cmd = lin.substring(1); if (execRequest(ssh, cmd, buf.toString(), ctx)) break; // Prepare the next command cmd = ""; ctx = new Hashtable(); } else if (lin.startsWith("#")) {cmd = lin.substring(1); flg = false; } } else {if ("/".equals(lin)) {// Execute the request if (execRequest(ssh, cmd, buf.toString(), ctx)) break; // Prepare the next command buf = new StringBuffer(); cmd = ""; ctx = new Hashtable(); flg = true; } else buf.append(lin).append(EOL); } } // End the SSH interactions and exit ssh.end(); System.exit(0); } } EOF my $NAM2 = 'IdentityRepository'; my $COD2 = < " + cnt + " identities found"); res = new RdaIdentity[cnt]; for (int off = 0 ; off < cnt ; ++off) res[off] = new RdaIdentity(buf.getString(), buf.getString()); return res; } // Remove all identities public void removeAllIdentities() {// Create the request traceln(TRACE, "Request to remove all identities"); buf.addCommand(REMOVE_ALL_IDENTITIES); buf.addLength(); // Execute the request try {query(buf); } catch (RdaAgentException err) {traceln(TRACE, "Cannot remove all identities"); } } // Remove an identity public boolean removeIdentity(byte[] pub) {// Create the request traceln(TRACE, "Request to remove an identity"); buf.addCommand(REMOVE_IDENTITY); buf.addString(pub); buf.addLength(); // Execute the request try {query(buf); } catch (RdaAgentException err) {return false; } return true; } // Execute a sign request public byte[] sign(byte[] pub, byte[] dat) {// Create the request traceln(TRACE, "Request signature"); buf.addCommand(SIGN_REQUEST); buf.addString(pub); buf.addString(dat); buf.addInt(0); buf.addLength(); // Execute the request try {query(buf); } catch (RdaAgentException err) {byte[] res = new byte[1]; res[0] = SSH_FAILURE; return res; } buf.skipByte(); // Return the result return buf.getString(); } // --- Low level methods ----------------------------------------------------- // Close the RDA agent interface public void end() {traceln(TRACE, "Close the RDA agent interface"); // Notify the RDA agent and close the output stream if (ofh != null) {traceln(TRACE, "Close the output stream"); try {byte[] buf = new byte[4]; buf[0] = (byte) 0; buf[1] = (byte) 0; buf[2] = (byte) 0; buf[3] = (byte) 0; ofh.write(buf, 0, 4); ofh.flush(); ofh.close(); } catch (IOException err) {traceln(TRACE, "Cannot close the output stream"); } finally {ofh = null; } } // Close the input stream if (ifh != null) {traceln(TRACE, "Close the input stream"); try {ifh.close(); } catch (IOException err) {traceln(TRACE, "Cannot close the input stream"); } finally {ifh = null; } } // Kill the process if (prc != null) {traceln(TRACE, "Kill the RDA authentication process"); try {prc.destroy(); } finally {prc = null; } } } // Initiate the RDA agent interface private void open() throws RdaAgentException {if (ofh == null) {traceln(TRACE, "Start a RDA proxy to the authentication agent"); try {Runtime run = java.lang.Runtime.getRuntime(); String sep = ""; String str = ""; int lgt = rda.size(); int off = 0; boolean flg = trc.length() > 0; String[] cmd = new String[lgt + (flg ? 4 : 2)]; for (off = 0 ; off < lgt ; off++) {cmd[off] = (String) rda.get(off); } cmd[off++] = "-XRemote"; cmd[off++] = "authenticate"; if (flg) {cmd[off++] = "-t"; cmd[off++] = trc; } prc = run.exec(cmd, null, null); while (off > 0) {str = cmd[--off] + sep + str; sep = ","; } traceln(TRACE, "Using: " + str); ofh = new DataOutputStream(prc.getOutputStream()); ifh = new DataInputStream(prc.getInputStream()); } catch (Exception err) {end(); traceln(TRACE, "Cannot communicate with the agent: " + err); throw new RdaAgentException(err.toString()); } } } // Perform a query public void query(RdaBuffer buf) throws RdaAgentException {traceln(TRACE, " - Execute a query"); try {int lgt,siz; // Send the request write(buf.getBuffer(), 0, buf.getLength()); // Read the length and next the data lgt = read(buf.getBuffer(), 0, 4); if (lgt < 4) throw new IOException("4 bytes expected"); siz = buf.getInt(); if (siz > 0) {lgt = read(buf.getBuffer(), 0, siz); if (lgt < siz) throw new IOException(siz + " bytes expected"); } } catch (IOException err) {// Close the communication in case of problems end(); throw new RdaAgentException(err.toString()); } } // Read data from the RDA agent private int read(byte[] buf, int off, int lgt) throws IOException {int max = lgt; while (max > 0) {traceln(TRACE, " - Read " + max); int cur = ifh.read(buf, off, max); if (cur <= 0) throw new IOException("Agent stream broken"); if (cur > 0) {off += cur; max -= cur; } } return lgt; } // Modify how to execute RDA public void setAgent(String rda) {this.rda.removeAllElements(); try {if (rda.indexOf("\\"") < 0) {// Split the command in arguments String[] arg = rda.trim().split("\\\\s+"); for (int off = 0 ; off < arg.length ; ++off) {this.rda.add(arg[off]); } } else {// Do an approximative parsing of the command rda = rda.trim(); while (rda.length() > 0) {if (rda.startsWith("\\"")) {String[] arg = rda.substring(1).split("\\"\\\\s*", 2); this.rda.add(arg[0]); rda = arg[1]; } else {String[] arg = rda.split("\\\\s+", 2); this.rda.add(arg[0]); rda = arg[1]; } } } } catch (Exception err) {traceln(TRACE, "Cannot parse the RDA launch command: " + err.toString()); } } public void setAgent(Vector rda) {this.rda = rda; } // Modify the trace level public void setLevel(byte lvl) {this.lvl = lvl; } // Modify the trace prefix public void setPrefix(String pre) {this.pre = pre; } // Modify the communication trace file public void setTrace(String trc) {this.trc = trc; // Close the communication interface to use the trace on the next request end(); } // Send data to the RDA agent private void write(byte[] buf, int off, int lgt) throws IOException, RdaAgentException {// Start the communication with the RDA agent on first request open(); // Send the data traceln(TRACE, " - Write " + lgt); ofh.write(buf, off, lgt); ofh.flush(); } // Display a trace line public void traceln(byte msk, String txt) {if ((lvl & msk) == msk) System.out.println(pre + "/" + mod + "[" + msk + "]: " + txt); } } EOF my $NAM4 = 'RdaAgentException'; my $COD4 = <>> 24); buf[idx++] = (byte) (val >>> 16); buf[idx++] = (byte) (val >>> 8); buf[idx++] = (byte) (val); } public void addLength() {int lgt = idx - 4; buf[0] = (byte) (lgt >>> 24); buf[1] = (byte) (lgt >>> 16); buf[2] = (byte) (lgt >>> 8); buf[3] = (byte) (lgt); } public void addString(byte[] dat) {addString(dat, dat.length); } public void addString(byte[] dat, int lgt) {addInt(lgt); addByte(dat, lgt); } public byte[] getBuffer() {off = 0; return buf; } public int getByte() {return (buf[off++] & 0xff); } void getByte(byte[] dat, int lgt) {System.arraycopy(buf, off, dat, 0, lgt); off += lgt; } public int getInt() {int dat = (((int) buf[off++]) << 24) & 0xff000000; dat |= (((int) buf[off++]) << 16) & 0x00ff0000; dat |= (((int) buf[off++]) << 8) & 0x0000ff00; dat |= ((int) buf[off++]) & 0x000000ff; return dat; } public int getLength() {return idx - off; } public byte[] getString() {int cnt = getInt(); if (cnt < 0 || cnt > 262144) cnt = 262144; byte[] dat = new byte[cnt]; getByte(dat, cnt); return dat; } public void skipByte() {off++; } } EOF my $NAM6 = 'RdaCollector'; my $COD6 = <] *\$"); // Get one-off parameters if (ctx.containsKey(CHK)) chk = (String) ctx.get(CHK); if (ctx.containsKey(DIS)) dis = (String) ctx.get(DIS); if (ctx.containsKey(HIT) && ctx.containsKey(STA)) cpb = new StringBuffer(); if (ctx.containsKey(LIM)) {Integer val = new Integer((String) ctx.get(LIM)); max = val.longValue(); } plf = ctx.containsKey(PLF); if (ctx.containsKey(TRY)) {Integer val = new Integer((String) ctx.get(TRY)); rtr = val.intValue(); } // Get other parameters inp = (String) (ctx.containsKey(INP) ? ctx.get(INP) : "0"); pwd = (String) (ctx.containsKey(PWD) ? ctx.get(PWD) : this.pwd); traceln(TRACE_EXEC, "Perform the selection"); traceln(TRACE_EXEC, " - Input: " + inp); if (pwd.length() > 0) traceln(TRACE_EXEC, " - Password: ***"); if (max > 0) lim = System.currentTimeMillis() + 1000 * max; if (ctx.containsKey(FLG) || chk.length() > 0) buf = new StringBuffer(); // Open the communication channel traceln(TRACE_EXEC, "Create new ChannelShell"); chn = (ChannelShell) ses.openChannel("shell"); chn.setPty(true); chn.setPtyType("xterm"); chn.connect(10000); OutputStream ocs = chn.getOutputStream(); PrintStream ocp = new PrintStream(ocs, true); InputStream ics = chn.getInputStream(); Thread.sleep(250); // Wait for an initial prompt traceln(TRACE_EXEC, "Wait for an initial prompt"); hit = waitFor(ics, lim, ipv, ppv, cpv, buf, cpb, plf); if ("".equals(hit)) {if (ctx.containsKey(FLG)) ofh.print(buf); throw new Exception("Cannot choose"); } // Perform the selection for(int cnt = rtr ; "log".equals(hit) ; --cnt) {// Abort when the try limit is reached if (cnt <= 0) throw new Exception("Bad input"); // Send the input value traceln(TRACE_EXEC, "Send the input value"); Thread.sleep(10); ocp.print(dumpString(TRACE_DUMP, DUMP_OUT, inp)); ocp.flush(); Thread.sleep(100); // Wait for a prompt traceln(TRACE_EXEC, "Wait for a prompt"); hit = waitFor(ics, lim, ipv, ppv, cpv, buf, cpb, plf); if ("".equals(hit)) {if (ctx.containsKey(FLG)) ofh.print(buf); throw new Exception("Cannot choose"); } } // Perform the access validation for(int cnt = rtr ; "pwd".equals(hit) ; --cnt) {// Abort when the try limit is reached if (cnt <= 0) throw new Exception("Bad password"); // Send the access password traceln(TRACE_EXEC, "Send the access password"); Thread.sleep(10); ocp.print(dumpString(TRACE_DUMP, DUMP_OUT, pwd + "\\n")); Thread.sleep(100); // Wait for a command prompt or another password prompt traceln(TRACE_EXEC, "Wait for a command prompt"); hit = waitFor(ics, lim, null, ppv, cpv, buf, cpb, plf); if ("".equals(hit)) {if (ctx.containsKey(FLG)) ofh.print(buf); throw new Exception("Cannot choose"); } } // Check the banner if (chk.length() > 0 && !matchReg(chk, buf)) throw new Exception("Banner validation error"); // Indicate the successful completion traceln(TRACE_EXEC, "Got command prompt"); this.dis = dis; if (cpb != null) efh.println("Hit:" + cpb); } // Perform a collection public void collect(PrintStream ofh, PrintStream efh, Hashtable ctx) throws Exception {int cur = 0; int flg = 0; int siz = 0; long lim = 0; long max = this.max; byte[] buf = new byte[32769]; boolean cln = false; Pattern clp = Pattern.compile("^\\\\r\\\\x000+"); Pattern npp = Pattern.compile("^(\\\\r\\\\s*\\\\r|0x010+\\\\s+0x010+)"); String cmd = ""; String lin = ""; String skp = "0"; StringBuffer cpb = null; StringBuffer tmp = new StringBuffer(); Vector nxt = new Vector(); Vector ack = new Vector(); Vector pat = new Vector(); if (chn == null) throw new Exception("No channel available"); traceln(TRACE_EXEC, "Open the communication streams"); InputStream ics = chn.getInputStream(); OutputStream ocs = chn.getOutputStream(); PrintStream ocp = new PrintStream(ocs, true); // Get the list of acceptable command prompts if (ctx.containsKey(PAT)) pat.add((String) ctx.get(PAT)); else if (ctx.containsKey(PAT + "#")) {Integer lgt = new Integer((String) ctx.get(PAT + "#")); for (int off = 1 ; off <= lgt.intValue() ; ++off) {try {pat.add((String) ctx.get(PAT + off)); } catch (Exception e) {throw new Exception("Cannot handle PAT" + off); } } } // Get the list of continuation prompts and the corresponding acknowledge if (ctx.containsKey(NXT)) {nxt.add((String) ctx.get(NXT)); if (ctx.containsKey(ACK) && ((String) ctx.get(ACK)).length() > 0) ack.add(octalToAscii((String) ctx.get(ACK)).toString()); else ack.add("\\n"); // default ACK value } else if (ctx.containsKey(NXT + "#")) {Integer lgt = new Integer((String) ctx.get(NXT + "#")); for (int off = 1 ; off <= lgt.intValue() ; ++off) {try {nxt.add((String) ctx.get(NXT + off)); if (ctx.containsKey(ACK + off) && ((String) ctx.get(ACK + off)).length() > 0) ack.add(octalToAscii((String) ctx.get(ACK + off)).toString()); else ack.add("\\n"); // default ACK value } catch (Exception e) {throw new Exception("Cannot handle NXT" + off); } } } // Get other parameters if (ctx.containsKey(CMD)) cmd = (String) ctx.get(CMD); if (ctx.containsKey(CLN)) cln = !"0".equals((String) ctx.get(CLN)); if (ctx.containsKey(HIT) && ctx.containsKey(STA)) cpb = new StringBuffer(); if (ctx.containsKey(LIM)) {Integer val = new Integer((String) ctx.get(LIM)); max = val.longValue(); } if (ctx.containsKey(SKP)) skp = (String) ctx.get(SKP); // Set time limit and output the command if (max > 0) lim = System.currentTimeMillis() + 1000 * max; ocp.print(dumpString(TRACE_DUMP, DUMP_OUT, ctx.containsKey(FIX) ? cmd : cmd + "\\n")); Thread.sleep(100); // Read incoming stream if (!skp.equals("0")) flg = 1; for (tmp.setLength(0) ;;) {// Read available characters siz = ics.available(); if (siz > 0) siz = ics.read(buf, 0, 32768); // Treat lines already received if (siz > 0) {traceln(TRACE_EXEC, "Data (" + siz + " bytes) received"); dumpBuffer(TRACE_DUMP, DUMP_IN, buf, siz); // Treat available lines cur = extractLine(tmp, buf, 0, siz); if (cur > 0) {// Apply requested treatment if (flg > 0) flg = 0; else {if (flg < 0) {flg = 0; lin = npp.matcher(tmp).replaceAll(""); } else lin = tmp.toString(); if (cln) lin = clp.matcher(lin).replaceAll(""); ofh.print(lin); } // Output the other lines after applying cleanup treatment for (;;) {tmp.setLength(0); cur = extractLine(tmp, buf, cur, siz); if (cur == 0) break; if (cln) ofh.print(clp.matcher(tmp).replaceAll("")); else ofh.print(tmp.toString()); } } // Detect the prompts int ind = matchRegV(nxt,tmp); if (ind >= 0) {tmp.setLength(0); traceln(TRACE_EXEC, "Continuation prompt matched (" + ind + ")"); ocp.print(dumpString(TRACE_DUMP, DUMP_OUT, (String) ack.elementAt(ind))); flg = -1; continue; } if (matchRegV(pat,tmp) >= 0) {traceln(TRACE_EXEC, "Command prompt detected:(" + tmp + ")"); if (ctx.containsKey(HIT) && ctx.containsKey(STA)) efh.println("Hit:'" + tmp + "'"); break; } } else if (siz < 0) break; // Detect timeouts if (lim > 0 && System.currentTimeMillis() > lim) {ofh.flush(); ofh.close(); throw new Exception("Collect timeout"); } Thread.sleep(250); } ofh.flush(); ofh.close(); traceln(TRACE_EXEC, "COLLECT Completed"); } // Perform a login public void login(Session ses, PrintStream ofh, PrintStream efh, Hashtable ctx) throws Exception {long lim = 0; long max = this.max; int rtr = this.rtr; boolean plf; String chk = ""; String dis = ""; String hit = ""; String pwd; String usr; StringBuffer buf = null; StringBuffer cpb = null; StringBuffer hdr = null; Vector cpv = new Vector(); // Get the prompt patterns if (ctx.containsKey(PAT)) cpv.add((String) ctx.get(PAT)); else if (ctx.containsKey(PAT + "#")) {Integer lgt = new Integer((String) ctx.get(PAT + "#")); for (int off = 1 ; off <= lgt.intValue() ; ++off) {try {cpv.add((String) ctx.get(PAT + off)); } catch (Exception e) {throw new Exception("Cannot set PAT" + off); } } } else cpv.add("[\\\\\$%#>] *\$"); // Get one-off parameters if (ctx.containsKey(CHK)) chk = (String) ctx.get(CHK); if (ctx.containsKey(DIS)) dis = (String) ctx.get(DIS); if (ctx.containsKey(FLG)) buf = new StringBuffer(); if (ctx.containsKey(HIT) && ctx.containsKey(STA)) cpb = new StringBuffer(); if (ctx.containsKey(LIM)) {Integer val = new Integer((String) ctx.get(LIM)); max = val.longValue(); } plf = ctx.containsKey(PLF); if (ctx.containsKey(TRY)) {Integer val = new Integer((String) ctx.get(TRY)); rtr = val.intValue(); } // Get other parameters pwd = (String) (ctx.containsKey(PWD) ? ctx.get(PWD) : this.pwd); usr = (String) (ctx.containsKey(USR) ? ctx.get(USR) : this.usr); traceln(TRACE_EXEC, "Perform the login"); traceln(TRACE_EXEC, " - User: " + usr); if (pwd.length() > 0) traceln(TRACE_EXEC, " - Password: ***"); if (chk.length() > 0) hdr = (buf != null) ? buf : new StringBuffer(); if (max > 0) lim = System.currentTimeMillis() + 1000 * max; // Open the communication channel traceln(TRACE_EXEC, "Create new ChannelShell"); chn = (ChannelShell) ses.openChannel("shell"); chn.setPty(true); chn.setPtyType("xterm"); chn.connect(10000); OutputStream ocs = chn.getOutputStream(); PrintStream ocp = new PrintStream(ocs, true); InputStream ics = chn.getInputStream(); Thread.sleep(250); // Wait for an initial prompt (true) and test the banner traceln(TRACE_EXEC, "Wait for an initial prompt"); hit = waitFor(ics, lim, lpv, ppv, cpv, hdr, cpb, plf); if ("".equals(hit)) {if (buf != null) ofh.print(buf); throw new Exception("Cannot login"); } // Check the banner if (hdr != null && !matchReg(chk, hdr)) throw new Exception("Banner validation error"); // Return when no login is requested if ("cmd".equals(hit)) {traceln(TRACE_EXEC, "Got command prompt"); this.dis = dis; if (cpb != null) efh.println("Hit:" + cpb); return; } // Perform the login for (; rtr > 0 ; --rtr) {// Treat the prompt traceln(TRACE_EXEC, "Treat the prompt"); if ("log".equals(hit)) {// Send the user name traceln(TRACE_EXEC, "Send the user name"); Thread.sleep(10); ocp.print(dumpString(TRACE_DUMP, DUMP_OUT, usr + "\\n")); Thread.sleep(100); //Wait for the password prompt traceln(TRACE_EXEC, "Wait for the password prompt"); hit = waitFor(ics, lim, null, ppv, cpv, buf, cpb, plf); if ("".equals(hit)) {if (buf != null) ofh.print(buf); throw new Exception("Cannot login"); } // Return when no password is expected if (!"pwd".equals(hit)) {this.dis = dis; if (cpb != null) efh.println("Hit:" + cpb); return; } } // Send the user password traceln(TRACE_EXEC, "Send the user password"); Thread.sleep(10); ocp.print(dumpString(TRACE_DUMP, DUMP_OUT, pwd + "\\n")); Thread.sleep(100); // Wait for command prompt or another login prompt traceln(TRACE_EXEC, "Wait for command prompt or another login prompt"); hit = waitFor(ics, lim, lpv, null, cpv, buf, cpb, plf); if ("".equals(hit)) {if (buf != null) ofh.print(buf); throw new Exception("Cannot login"); } // Return when expected prompt is detected if ("cmd".equals(hit)) {traceln(TRACE_EXEC, "Got command prompt"); this.dis = dis; if (cpb != null) efh.println("Hit:" + cpb); return; } } traceln(TRACE_EXEC, "Invalid user and password combination"); throw new Exception("Invalid user and password combination"); } // Modify the trace level public void setLevel(byte lvl) {this.lvl = lvl; } // Modify the trace prefix public void setPrefix(String pre) {this.pre = pre; } //--- Private methods ------------------------------------------------------ // Dump a buffer private void dumpBuffer(byte msk, String pre, byte[] buf, int cnt) {if ((lvl & msk) == msk) {int adr, lgt, max, off; String txt; StringBuffer lin; for (adr = 0 , lgt = cnt ; lgt > 0 ; lgt -= 16) {lin = new StringBuffer(); txt = " " + Integer.toHexString(adr) + " "; lin.append(pre) .append(txt.substring(txt.length() - 6)); max = (lgt > 16) ? 16 : lgt; txt = (new String(buf, adr, max)).replaceAll("[^\\040-\\176]", "."); for (off = 0 ; off < max ; ++adr, ++off) lin.append(HEX.charAt((buf[adr] & 0xF0) >> 4)) .append(HEX.charAt(buf[adr] & 0x0F)) .append(" "); while (max++ < 16) lin.append(" "); System.out.println(lin.append(txt)); } System.out.println(); } } // Dump a String private String dumpString(byte msk, String pre, String str) {dumpBuffer(msk, pre, str.getBytes() ,str.length()); return str; } // Encode a buffer private void encodeBuffer(StringBuffer dst, byte[] src, int eol, int cnt) {for (int off = eol ; ++off < cnt ;) dst.append(HEX.charAt((src[off] & 0xF0) >> 4)) .append(HEX.charAt(src[off] & 0x0F)); } // Wait for a pattern private String waitFor(InputStream ics, long lim, Vector lpv, Vector ppv, Vector cpv, StringBuffer hdr, StringBuffer cpb, boolean flg) throws Exception {int cnt, eol, off; byte[] buf = new byte[32769]; StringBuffer lin = new StringBuffer(); try {if (hdr != null) hdr.setLength(0); for (;;) {// Read available characters cnt = ics.available(); if (cnt > 0) cnt = ics.read(buf, 0, 32768); // Treat the buffer content if (cnt > 0) {dumpBuffer(TRACE_DUMP, DUMP_IN, buf, cnt); // Determine last line eol = cnt; if (flg) --eol; while (eol-- > 0) {if (buf[eol] == 10) break; } if (eol >= 0) {lin.setLength(0); if (hdr != null) {for (off = 0 ; off <= eol ; ++off) hdr.append((char) buf[off]); } } for (off = eol ; ++off < cnt ;) lin.append((char) buf[off]); // Check for a prompt if (lin.length() > 0) {traceln(TRACE_EXEC, "Check prompt in '" + lin + "'"); if (lpv != null) {traceln(TRACE_EXEC, "Check login prompts"); if (matchRegV(lpv, lin) >= 0) return "log"; } if (ppv != null) {traceln(TRACE_EXEC, "Check password prompts"); if (matchRegV(ppv, lin) >= 0) return "pwd"; } if (cpv != null) {traceln(TRACE_EXEC, "Check command prompts"); if (matchRegV(cpv, lin) >= 0) {if (cpb != null) encodeBuffer(cpb, buf, eol, cnt); return "cmd"; } } } } else if (cnt < 0) break; // Continue as long as the time limit is not reached if (lim > 0 && System.currentTimeMillis() > lim) throw new Exception("Login timeout"); Thread.sleep(100); } } catch (Exception err) {traceln(TRACE_EXEC, "COLLECT exception: " + err.toString()); } return ""; } // Extract a line from a byte buffer private int extractLine(StringBuffer tmp, byte[] buf, int beg, int lgt) {for(int off = beg ; off < lgt ; ) {tmp.append((char) buf[off]); if (buf[off++] == 10) return off; } return 0; } // Match a pattern private boolean matchReg(String pat, StringBuffer src) {Pattern p = Pattern.compile(pat, Pattern.MULTILINE + Pattern.DOTALL); Matcher m = p.matcher(src); return m.find(); } // Match a pattern from a list private int matchRegV(Vector pat, StringBuffer src) {for (int off = 0 ; off < pat.size() ; off++) {if (matchReg((String) pat.elementAt(off), src)) return off; } return -1; } // Convert an octal string private static StringBuffer octalToAscii(String str) throws Exception {StringBuffer src = new StringBuffer(); StringBuffer dst = new StringBuffer(); char chr; src.append(str); if (str.length() % 4 > 0) throw new Exception("Input <" + str + " > has incorrect lenght."); while (src.length() >= 4) {try {chr = (char) Integer.decode(src.substring(0,4)).byteValue(); if (chr < 0 || chr > 255) throw new Exception("Octal number gives invalid ASCII 0..255"); } catch (Exception e) {throw new Exception("Error decoding <" + src.substring(0,4) + "> as an octal value"); } dst.append(chr); src.delete(0, 4); } return dst; } // Display a trace line private void traceln(byte msk, String txt) {if ((lvl & msk) == msk) System.out.println(pre + "/" + mod + "[" + msk + "]: " + txt); } } EOF my $NAM7 = 'RdaIdentity'; my $COD7 = < assuming pre 0.1.46"); ctl = ctl2; old = true; } if ((lvl & TRACE_JSCH) == TRACE_JSCH) JSch.setLogger(log); // Create the session if (agt == null) {traceln(TRACE_EXEC, "Create a session (no authentication agent)"); ses = ((JSch) ctl).getSession(usr, hst, prt); } else if (old) {traceln(TRACE_EXEC, "Create a session (old JSCH version)"); RdaIdentityCache rep = new RdaIdentityCache(agt); ((RdaJsch) ctl).setIdentityRepository(rep); ses = ((RdaJsch) ctl).getSession(usr, hst, prt); } else {try {traceln(TRACE_EXEC, "Create a session (dynamic approach)"); IdentityRepository rep = (IdentityRepository) new RdaIdentityCache(agt); Method[] methods = ctl.getClass().getDeclaredMethods(); for (int off = 0 ; off < methods.length ; ++off) {if ((methods[off].getName().compareTo("setIdentityRepository")) == 0) {traceln((byte) 8, "Setter found, dynamic invoke ...."); Object[] tmp = new Object[1]; tmp[0] = rep; methods[off].invoke((JSch) ctl, tmp); } } ses = ((JSch) ctl).getSession(usr, hst, prt); } catch (Exception err) {traceln(TRACE_EXEC, "Create a session (static approach)"); IdentityRepository rep = (IdentityRepository) new RdaIdentityCache(agt); ((RdaJsch) ctl).setIdentityRepository((RdaIdentityCache) rep); ses = ((JSch) ctl).getSession(usr, hst, prt); } } } catch (JSchException err) {traceln(TRACE_EXEC, "JSCH exception when establishing session: " + err.getMessage()); if (agt != null) agt.end(); System.exit(1); } catch (Exception err) {traceln(TRACE_EXEC, "Exception when establishing session: " + err.getMessage()); } finally {con = false; } return ses; } // Get a session public Session getSession(Hashtable ctx) {return getSession(ctx, false); } public Session getSession(Hashtable ctx, boolean flg) {String hst, pph, pwd, usr; boolean nwc; // Determine the requested connection hst = (String) (ctx.containsKey(HST) ? ctx.get(HST) : this.hst); pph = (String) (ctx.containsKey(PPH) ? ctx.get(PPH) : this.pph); pwd = (String) (ctx.containsKey(PWD) ? ctx.get(PWD) : this.pwd); usr = (String) (ctx.containsKey(USR) ? ctx.get(USR) : this.usr); nwc = ctx.containsKey(NEW); traceln(TRACE_EXEC, " - Host: " + hst); traceln(TRACE_EXEC, " - Port: " + prt); traceln(TRACE_EXEC, " - User: " + usr); if (pwd.length() > 0) traceln(TRACE_EXEC, " - Password: ***"); if (pph.length() > 0) traceln(TRACE_EXEC, " - Passphrase: ***"); if (nwc) traceln(TRACE_EXEC, " - New connection"); // Create the session when needed if (ses == null) ses = createSession(usr, hst, prt); else if (nwc || !usr.equals(ses.getUserName()) || !hst.equals(ses.getHost()) || !ses.isConnected()) {if (col != null) {col.end(); col = null; } if (con) ses.disconnect(); con = false; ses = createSession(usr, hst, prt); } // Connect to the session if (!con) {try {traceln(TRACE_EXEC, "Set the user information and connect"); RdaUser uio = new RdaUser(); if (pwd.length() > 0) uio.setPassword(pwd); if (pph.length() > 0) uio.setPassphrase(pph); ses.setUserInfo(uio); ses.connect(30000); con = true; } catch (Exception err) {msg = err.getMessage(); ses = null; traceln(TRACE_EXEC, "Could not open ssh channel:" + msg); } } // Initiate a collector when requested if (flg) {if (col != null) col.end(); col = new RdaCollector(usr, pwd, pre, lvl); } // Return the session return ses; } // --- Request methods ------------------------------------------------------- // Execute a CHOOSE request public boolean doChoose(PrintStream ofh, PrintStream efh, Hashtable ctx) {boolean sta = ctx.containsKey(STA) && efh != null; try {traceln(TRACE_EXEC, "Treat a CHOOSE request"); if (col == null) {traceln(TRACE_EXEC, "Missing collector"); if (sta) efh.println("Error: Missing collector"); } else {col.choose(ses, ofh, efh, ctx); if (sta) efh.println("Exit:0"); } } catch (Exception err) {if (sta) efh.println("Error:" + err.toString()); else {traceln(TRACE_EXEC, "CHOOSE exception: " + err.toString()); return true; } } return false; } // Execute a CLOSE request public boolean doClose(PrintStream ofh, PrintStream efh, Hashtable ctx) {boolean sta = ctx.containsKey(STA) && efh != null; try {traceln(TRACE_EXEC, "Treat a CLOSE request"); // Close any existing collector if (col != null) {col.end(); col = null; } // Indicate the sucessful completion if (sta) efh.println("Exit:0"); } catch (Exception err) {if (sta) efh.println("Error:" + err.getMessage()); else {traceln(TRACE_EXEC, "CLOSE exception: " + err.toString()); return true; } } return false; } // Execute a COLLECT request public boolean doCollect(PrintStream ofh, PrintStream efh, Hashtable ctx) {boolean sta = ctx.containsKey(STA) && efh != null; try {traceln(TRACE_EXEC, "Treat a COLLECT request"); if (col == null) {traceln(TRACE_EXEC, "Missing collector"); if (sta) efh.println("Error: Missing collector"); } else {col.collect(ofh, efh, ctx); if (sta) efh.println("Exit:0"); } } catch (Exception err) {if (sta) efh.println("Error:" + err.toString()); else {traceln(TRACE_EXEC, "COLLECT exception: " + err.toString()); return true; } } return false; } // Execute a DEFAULT request public boolean doDefault(PrintStream ofh, PrintStream efh, Hashtable ctx) {String cur = null; Vector rda = new Vector(); boolean sta = ctx.containsKey(STA) && efh != null; try {traceln(TRACE_EXEC, "Treat a DEFAULT request"); if (ctx.containsKey(cur = AGT) && agt != null) agt.setTrace(new String((String) ctx.get(AGT))); if (ctx.containsKey(cur = CMD)) this.shl = new String((String) ctx.get(CMD)); if (ctx.containsKey(cur = HST)) this.hst = new String((String) ctx.get(HST)); if (ctx.containsKey(cur = PRE)) setPrefix(new String((String) ctx.get(PRE))); if (ctx.containsKey(cur = PRT)) {Integer val = new Integer((String) ctx.get(PRT)); this.prt = val.intValue(); } if (ctx.containsKey(cur = PPH)) this.pph = new String((String) ctx.get(PPH)); if (agt != null) {if (ctx.containsKey(cur = RDA + "#")) {Integer lgt = new Integer((String) ctx.get(cur)); for (int off = 1 ; off <= lgt.intValue() ; ++off) {try {rda.add((String) ctx.get(RDA + off)); } catch (Exception e) {throw new Exception("Cannot set RDA" + off); } } agt.setAgent(rda); } else if (ctx.containsKey(cur = RDA)) agt.setAgent(new String((String) ctx.get(RDA))); } if (ctx.containsKey(cur = PWD)) this.pwd = new String((String) ctx.get(PWD)); if (ctx.containsKey(cur = TRC)) {Integer val = new Integer((String) ctx.get(TRC)); setLevel((byte) val.intValue()); } if (ctx.containsKey(cur = USR)) this.usr = new String((String) ctx.get(USR)); if (sta) efh.println("Exit:0"); } catch (Exception err) {if (cur == null) {traceln(TRACE_EXEC, "DEFAULT exception: " + err.toString()); return true; } traceln(TRACE_EXEC, "Error when treating " + cur + ":\\n " + err.toString()); if (sta) efh.println("Error:" + cur + "|" + err.toString()); } return false; } // Execute a EXEC request public boolean doExec(PrintStream ofh, PrintStream efh, String dat, Hashtable ctx) {ChannelExec chn; Session ses; boolean flg = (lvl & TRACE_DATA) == TRACE_DATA; boolean sta = ctx.containsKey(STA) && efh != null; try {String cmd; traceln(TRACE_EXEC, "Treat an EXEC request"); // Get a connected session ses = getSession(ctx); if (ses == null) {traceln(TRACE_EXEC, "Connection error: " + msg); if (sta) efh.println("Error: " + msg); return false; } // Determine the command to execute if (ctx.containsKey(CMD)) cmd = new String((String) ctx.get(CMD)); else cmd = shl; if (flg) traceln(TRACE_DATA, pre + "< Command: " + cmd + "\\n" + pre + "< Input:\\n" + dat); // Create the execution channel traceln(TRACE_EXEC, "Create the channel"); chn = (ChannelExec) ses.openChannel("exec"); chn.setCommand(cmd); chn.connect(); if (efh != null && !sta) chn.setErrStream(efh); // Pass the input to the remote command traceln(TRACE_EXEC, "Send the command input"); OutputStream ocs = chn.getOutputStream(); if (dat.length() > 0) {ocs.write(dat.getBytes()); ocs.flush(); } ocs.close(); // Read command results traceln(TRACE_EXEC, "Read command results"); BufferedReader ics = new BufferedReader(new InputStreamReader(chn.getInputStream())); String lin; while ((lin = ics.readLine()) != null) {ofh.println(lin); if (flg) traceln(TRACE_DATA, pre + "> " + lin); } traceln(TRACE_EXEC, "Report the execution status"); Thread.sleep(250); if (sta) efh.println("Exit:" + chn.getExitStatus()); // Disconnect the channel and terminate the request traceln(TRACE_EXEC, "Disconnect the channel"); chn.disconnect(); traceln(TRACE_EXEC, "Terminate the EXEC request"); } catch (Exception err) {traceln(TRACE_EXEC, "EXEC exception: " + err.toString()); if (sta) efh.println("Error:" + err.toString()); return true; } return false; } // Execute a GET request public boolean doGet(PrintStream ofh, PrintStream efh, Hashtable ctx) {ChannelSftp chn; Session ses; boolean sta = ctx.containsKey(STA) && efh != null; try {String dst, src; boolean flg = ctx.containsKey(FLG); traceln(TRACE_EXEC, "Treat a GET request"); if (ctx.containsKey(DST)) {dst = (String) ctx.get(DST); // Get a connected session ses = getSession(ctx); if (ses == null) {traceln(TRACE_EXEC, "Connection error: " + msg); if (sta) efh.println("Error: " + msg); return false; } // Create the SFTP channel traceln(TRACE_EXEC, "Create the channel"); chn = (ChannelSftp) ses.openChannel("sftp"); chn.connect(); // Get the requested files if (ctx.containsKey(FIL)) exec_get(chn, dst, (String) ctx.get(FIL)); else if (ctx.containsKey(DIR)) {String nam, pat; src = (String) ctx.get(DIR); if (ctx.containsKey(PAT)) {// Validate the destination directory File fil = new File(dst); if (!(fil.exists() || fil.mkdir()) || !fil.isDirectory()) throw new SftpException(1, "Missing or invalid directory " + dst); // Get the files pat = src + "/" + ctx.get(PAT); traceln(TRACE_EXEC, "Get files matching " + pat); Vector tbl = chn.ls(pat); Iterator ite = tbl.iterator(); while (ite.hasNext()) {LsEntry itm = (LsEntry) ite.next(); nam = itm.getFilename(); if (!itm.getAttrs().isDir()) exec_get(chn, dst, src + "/" + nam); else if (".".equals(nam) || "..".equals(nam) || !flg) traceln(TRACE_EXEC, "Skipping directory: " + src + "/" + nam); else exec_mget(chn, dst + "/" + nam, src + "/" + nam); } } else exec_mget(chn, dst, src); } // Disconnect the channel and terminate the request traceln(TRACE_EXEC, "Disconnect the channel"); chn.disconnect(); traceln(TRACE_EXEC, "Terminate the GET request"); // Indicate the successful completion traceln(TRACE_EXEC, "Transfer complete"); if (sta) efh.println("Exit:0"); } } catch (Exception err) {if (sta) efh.println("Error:" + err.toString()); else {traceln(TRACE_EXEC, "GET exception: " + err.toString()); return true; } } return false; } // Execute a LOCAL request public boolean doLocal(PrintStream ofh, FileOutputStream ofs, PrintStream efh, FileOutputStream efs, Hashtable ctx, File fil, String wrk) throws IOException {InputStream ics = null; InputStream ecs = null; OutputStream ocs = null; Process prc = null; DataInputStream ips = new DataInputStream(System.in); boolean lck = ctx.containsKey(LCK); boolean req = true; boolean sta = ctx.containsKey(STA) && efh != null; try {traceln(TRACE_EXEC, "Treat a LOCAL request"); String cur; if (!ctx.containsKey(cur = CMD + "#")) {traceln(TRACE_EXEC,"Missing command"); throw new Exception("Missing command"); } Integer siz = new Integer((String) ctx.get(cur)); int lgt = siz.intValue(); String[] cmd = new String[lgt]; try {for (int off = lgt ; off > 0 ;) {cur = (String) ctx.get(CMD + off); cmd[--off] = cur; } } catch (Exception e) {throw new Exception("Cannot set the command"); } try {System.out.println("Starting local command"); Runtime run = java.lang.Runtime.getRuntime(); prc = run.exec(cmd, null, null); ocs = prc.getOutputStream(); ics = prc.getInputStream(); ecs = prc.getErrorStream(); } catch (Exception err) {prc.destroy(); err.printStackTrace(); traceln(TRACE_EXEC, "Cannot communicate with the local agent"); throw new RdaAgentException(err.getMessage()); } // Treat the exchanges File sig = new File(msg + ".sig"); if (ctx.containsKey(MSG)) {String msg = (String) ctx.get(MSG); traceln(TRACE_EXEC, "Local active (message processing)"); // Treat the output flow traceln(TRACE_EXEC, "Start output flow treatment"); RdaAsyncResponse otf = new RdaAsyncResponse(ics, msg); otf.setLevel(lvl); otf.setPrefix(pre); Thread ott = new Thread(otf); ott.setDaemon(true); ott.start(); // Treat the error flow traceln(TRACE_EXEC, "Start error flow treatment"); RdaAsyncTransfer etf = new RdaAsyncTransfer(ecs, false); Thread ett = new Thread(etf); ett.start(); // Start parent agent monitor RdaAgentMonitor amo = null; Thread amt = null; if (lck) {traceln(TRACE_EXEC, "Start parent agent monitoring"); amo = new RdaAgentMonitor((String) ctx.get(LCK), lvl); amt = new Thread(amo); amt.start(); } // Close and rename the output file if (sta) efh.println("Exit:0"); ofh.close(); ofs.close(); wrk = wrk.replaceAll("tmp\$", "txt"); if (!fil.renameTo(new File(wrk))) System.err.println("Cannot rename the work file"); if (efh != null) efh.close(); if (efs != null) efs.close(); req = sta = false; // Treat the input flow traceln(TRACE_EXEC, "Start input flow"); int cnt = 0; int max = 32678; byte[] buf = new byte[max]; for (;;) {cnt = ips.available(); if (cnt > 0) {cnt = ips.read(buf, 0, (cnt < max) ? cnt : max); if (cnt > 0) {otf.resetFile(); byte[] dat = new byte[cnt]; System.arraycopy(buf, 0, dat, 0, cnt); ocs.write(dat); ocs.flush(); } } if (cnt < 0) break; if (sig.exists()) {traceln(TRACE_EXEC, "Local exiting (signaling)"); endTransfer(otf, ott, etf, ett, amo, amt); break; } if (lck && !amt.isAlive()) {traceln(TRACE_EXEC, "Parent agent exited"); endTransfer(otf, ott, etf, ett, amo, amt); break; } if (isChildEnded(prc)) {traceln(TRACE_EXEC, "Child agent exited"); endTransfer(otf, ott, etf, ett, amo, amt); break; } if (otf.err != null) {traceln(TRACE_EXEC, "Output transfer exception: " + otf.err.getMessage()); endTransfer(otf, ott, etf, ett, amo, amt); break; } if (etf.err != null) {traceln(TRACE_EXEC, "Error transfer exception: " + etf.err.getMessage()); endTransfer(otf, ott, etf, ett, amo, amt); break; } Thread.sleep(250); } } else {traceln(TRACE_EXEC, "Local active (bi-directional communication)"); // Treat the output flow RdaAsyncTransfer otf = new RdaAsyncTransfer(ics, false); Thread ott = new Thread(otf); ott.start(); // Treat the error flow RdaAsyncTransfer etf = new RdaAsyncTransfer(ecs, true); Thread ett = new Thread(etf); ett.start(); // Start master monitor RdaAgentMonitor amo = null; Thread amt = null; if (lck) {amo = new RdaAgentMonitor((String) ctx.get(LCK)); amt = new Thread(amo); amt.start(); } // Close and rename the output file if (sta) efh.println("Exit:0"); ofh.close(); ofs.close(); wrk = wrk.replaceAll("tmp\$", "txt"); if (!fil.renameTo(new File(wrk))) System.err.println("Cannot rename the work file"); if (efh != null) efh.close(); if (efs != null) efs.close(); req = sta = false; // Treat the input flow int cnt = 0; int max = 32678; byte[] buf = new byte[max]; for (;;) {cnt = ips.available(); if (cnt > 0) {cnt = ips.read(buf, 0, (cnt < max) ? cnt : max); if (cnt > 0) {byte[] dat = new byte[cnt]; System.arraycopy(buf, 0, dat, 0, cnt); ocs.write(dat); ocs.flush(); } } if (cnt < 0) break; if (sig.exists() || (lck && !amt.isAlive()) || isChildEnded(prc) || otf.err != null || etf.err != null) {endTransfer(otf, ott, etf, ett, amo, amt); break; } Thread.sleep(25); } } } catch (Exception err) {if (sta) efh.println("Error:" + err.toString()); else if (req) {traceln(TRACE_EXEC, "LOCAL exception: " + err.getMessage()); return true; } } // Close the request if (!req) throw new IOException("Local ended"); traceln(TRACE_EXEC,"Ending local"); return false; } // Execute a LOGIN request public boolean doLogin(PrintStream ofh, PrintStream efh, Hashtable ctx) {boolean sta = ctx.containsKey(STA) && efh != null; try {traceln(TRACE_EXEC, "Treat a LOGIN request"); if (col == null) {traceln(TRACE_EXEC, "Missing collector"); if (sta) efh.println("Error: Missing collector"); } else {col.login(ses, ofh, efh, ctx); if (sta) efh.println("Exit:0"); } } catch (Exception err) {if (sta) efh.println("Error:" + err.getMessage()); else {traceln(TRACE_EXEC, "LOGIN exception: " + err.toString()); return true; } } return false; } // Execute a LOGOUT request public boolean doLogout(PrintStream ofh, PrintStream efh, Hashtable ctx) {boolean sta = ctx.containsKey(STA) && efh != null; try {traceln(TRACE_EXEC, "Treat a LOGOUT request"); // Close any existing collector if (col != null) {col.end(); col = null; } // Indicate the sucessful completion if (sta) efh.println("Exit:0"); } catch (Exception err) {if (sta) efh.println("Error:" + err.getMessage()); else {traceln(TRACE_EXEC, "LOGOUT exception: " + err.toString()); return true; } } return false; } // Execute an OPEN request public boolean doOpen(PrintStream ofh, PrintStream efh, Hashtable ctx) {boolean sta = ctx.containsKey(STA) && efh != null; try {traceln(TRACE_EXEC, "Treat an OPEN request"); // Get a connected session ses = getSession(ctx, true); if (ses == null) {traceln(TRACE_EXEC, "Connection error: " + msg); if (sta) efh.println("Error: " + msg); return false; } if (sta) efh.println("Exit:0"); } catch (Exception err) {if (sta) efh.println("Error:" + err.getMessage()); else {traceln(TRACE_EXEC, "OPEN exception: " + err.toString()); return true; } } return false; } // Execute a PUT request public boolean doPut(PrintStream ofh, PrintStream efh, Hashtable ctx) {ChannelSftp chn; Session ses; boolean sta = ctx.containsKey(STA) && efh != null; try {String rdr, src; traceln(TRACE_EXEC, "Treat a PUT request"); if (ctx.containsKey(RDR)) {// Get a connected session ses = getSession(ctx); if (ses == null) {traceln(TRACE_EXEC, "Connection error: " + msg); if (sta) efh.println("Error: " + msg); return false; } // Create the SFTP channel traceln(TRACE_EXEC, "Create the channel"); chn = (ChannelSftp) ses.openChannel("sftp"); chn.connect(); // Validate the remote directory rdr = exec_mkdir(chn, (String) ctx.get(RDR)); // Put the files if (ctx.containsKey(SRC)) exec_put(chn, ctx.containsKey(RNM) ? rdr + "/" + (String) ctx.get(RNM) : rdr, (String) ctx.get(SRC)); else if (ctx.containsKey(SRC + "#")) {File fil; boolean flg = ctx.containsKey(FLG); Integer max = new Integer((String) ctx.get(SRC + "#")); for (int off = 1 ; off <= max.intValue() ; ++off) {src = (String) ctx.get(SRC + off); fil = new File(src); if (!fil.isDirectory()) exec_put(chn, rdr, fil.getAbsolutePath()); else if (flg) exec_mput(chn, exec_mkdir(chn, rdr + "/" + fil.getName()), fil.getAbsolutePath()); else traceln(TRACE_EXEC, "Skipping directory " + fil.getAbsolutePath()); } } // Disconnect the channel and terminate the request traceln(TRACE_EXEC, "Disconnect the channel"); chn.disconnect(); traceln(TRACE_EXEC, "Terminate the PUT request"); // Indicate the successful completion traceln(TRACE_EXEC, "Transfer complete"); if (sta) efh.println("Exit:0"); } } catch (Exception err) {if (sta) efh.println("Error:" + err.toString()); else {traceln(TRACE_EXEC, "PUT exception: " + err.toString()); return true; } } return false; } // Execute a SHADOW request public boolean doShadow(PrintStream ofh, FileOutputStream ofs, PrintStream efh, FileOutputStream efs, Hashtable ctx, File fil, String wrk) throws IOException {boolean lck = ctx.containsKey(LCK); boolean req = true; boolean sta = ctx.containsKey(STA) && efh != null; ChannelExec chn; Session ses; try {traceln(TRACE_EXEC, "Treat SHADOW request"); // Get a connected session ses = getSession(ctx); if (ses == null) {traceln(TRACE_EXEC, "Connection error: " + msg); if (sta) efh.println("Error: " + msg); return false; } // Determine the command to execute String cmd; if (ctx.containsKey(CMD)) cmd = (String) ctx.get(CMD); else cmd = shl; traceln(TRACE_EXEC,"Remote agent command: " + cmd); // Create the execution channel traceln(TRACE_EXEC, "Create the remote agent channel"); chn = (ChannelExec) ses.openChannel("exec"); chn.setCommand(cmd); chn.connect(10000); if (efh != null && !sta) chn.setErrStream(efh); // Treat the exchanges File sig = new File(msg + ".sig"); if (ctx.containsKey(MSG)) {String msg = (String) ctx.get(MSG); traceln(TRACE_EXEC, "Shadow active (message processing)"); // Treat the output flow traceln(TRACE_EXEC, "Start output flow treatment"); InputStream ics = chn.getInputStream(); RdaAsyncResponse otf = new RdaAsyncResponse(ics, msg); otf.setLevel(lvl); otf.setPrefix(pre); Thread ott = new Thread(otf); ott.start(); // Treat the error flow traceln(TRACE_EXEC, "Start error flow treatment"); InputStream ecs = chn.getErrStream(); RdaAsyncTransfer etf = new RdaAsyncTransfer(ecs, false); Thread ett = new Thread(etf); ett.start(); // Start parent agent monitor RdaAgentMonitor amo = null; Thread amt = null; if (lck) {traceln(TRACE_EXEC, "Start parent agent monitoring"); amo = new RdaAgentMonitor((String) ctx.get(LCK), lvl); amt = new Thread(amo); amt.start(); } // Close and rename the output file if (sta) efh.println("Exit:0"); ofh.close(); ofs.close(); wrk = wrk.replaceAll("tmp\$", "txt"); if (!fil.renameTo(new File(wrk))) System.err.println("Cannot rename the work file"); if (efh != null) efh.close(); if (efs != null) efs.close(); req = sta = false; // Treat the input flow traceln(TRACE_EXEC, "Start input flow"); OutputStream ocs = chn.getOutputStream(); DataInputStream ips = new DataInputStream(System.in); int cnt = 0; int max = 32678; byte[] buf = new byte[max]; for (;;) {cnt = ips.available(); if (cnt > 0) {cnt = ips.read(buf, 0, (cnt < max) ? cnt : max); if (cnt > 0) {otf.resetFile(); byte[] dat = new byte[cnt]; System.arraycopy(buf, 0, dat, 0, cnt); ocs.write(dat); ocs.flush(); } } if (cnt < 0) break; if (sig.exists()) {traceln(TRACE_EXEC, "Shadow exiting (signaling)"); endTransfer(otf, ott, etf, ett, amo, amt); break; } if (lck && !amt.isAlive()) {traceln(TRACE_EXEC, "Parent agent exited"); endTransfer(otf, ott, etf, ett, amo, amt); break; } if (!chn.isConnected()) {traceln(TRACE_EXEC, "Shadow exiting (channel lost)"); endTransfer(otf, ott, etf, ett, amo, amt); break; } if (otf.err != null) {traceln(TRACE_EXEC, "Output transfer exception: " + otf.err.getMessage()); endTransfer(otf, ott, etf, ett, amo, amt); break; } if (etf.err != null) {traceln(TRACE_EXEC, "Error transfer exception: " + etf.err.getMessage()); endTransfer(otf, ott, etf, ett, amo, amt); break; } Thread.sleep(250); } } else {traceln(TRACE_EXEC, "Shadow active (bi-directional communication)"); // Treat the output flow InputStream ics = chn.getInputStream(); RdaAsyncTransfer otf = new RdaAsyncTransfer(ics, false); Thread ott = new Thread(otf); ott.start(); // Treat the error flow InputStream ecs = chn.getErrStream(); RdaAsyncTransfer etf = new RdaAsyncTransfer(ecs, true); Thread ett = new Thread(etf); ett.start(); // Start parent agent monitor RdaAgentMonitor amo = null; Thread amt = null; if (lck) {amo = new RdaAgentMonitor((String) ctx.get(LCK)); amt = new Thread(amo); amt.start(); } // Close and rename the output file if (sta) efh.println("Exit:0"); ofh.close(); ofs.close(); wrk = wrk.replaceAll("tmp\$", "txt"); if (!fil.renameTo(new File(wrk))) System.err.println("Cannot rename the work file"); if (efh != null) efh.close(); if (efs != null) efs.close(); req = sta = false; // Treat the input flow OutputStream ocs = chn.getOutputStream(); DataInputStream ips = new DataInputStream(System.in); int cnt = 0; int max = 32678; byte[] buf = new byte[max]; for (;;) {cnt = ips.available(); if (cnt > 0) {cnt = ips.read(buf, 0, (cnt < max) ? cnt : max); if (cnt > 0) {byte[] dat = new byte[cnt]; System.arraycopy(buf, 0, dat, 0, cnt); ocs.write(dat); ocs.flush(); } } if (cnt < 0) break; if (sig.exists() || (lck && !amt.isAlive()) || !chn.isConnected() || otf.err != null || etf.err != null) {endTransfer(otf, ott, etf, ett, amo, amt); break; } Thread.sleep(25); } } } catch (Exception err) {if (sta) efh.println("Error:" + err.toString()); else if (req) {traceln(TRACE_EXEC, "SHADOW exception: " + err.getMessage()); return true; } } // Close the request if (!req) throw new IOException("Shadow ended"); traceln(TRACE_EXEC,"Ending shadow"); return false; } // Execute a TEST request public boolean doTest(PrintStream ofh, PrintStream efh, Hashtable ctx) {traceln(TRACE_EXEC, "Treat a TEST request"); try {Session ses = getSession(ctx); if (ses == null) ofh.println("Error: " + msg); else ofh.println("OK Connect"); } catch (Exception err) {traceln(TRACE_EXEC, "TEST exception: " + err.toString()); return true; } return false; } // --- Other methods --------------------------------------------------------- // Close all instances operations public void end() {traceln(TRACE_EXEC, "End the SSH instance"); // Disconnect the session if (ses != null) ses.disconnect(); // Close an existing collector if (col != null) {col.end(); col = null; } // Close the RDA agent interface if (agt != null) {agt.end(); agt = null; } } // Modify the trace level public void setLevel(byte lvl) {this.lvl = lvl; if (agt != null) agt.setLevel(lvl); if (col != null) col.setLevel(lvl); } // Modify the trace prefix public void setPrefix(String pre) {this.pre = pre; if (agt != null) agt.setPrefix(pre); if (col != null) col.setPrefix(pre); } // Display a trace string public void trace(byte msk, String txt) {if ((lvl & msk) == msk) System.out.print(txt); } // Display a trace line public void traceln(byte msk, String txt) {if ((lvl & msk) == msk) System.out.println(pre + "/" + mod + "[" + msk + "]: " + txt); } // --- Private methods ------------------------------------------------------- // Check if a given process is running private boolean isChildEnded(Process prc) {try {int val = prc.exitValue(); return true; } catch (IllegalThreadStateException e) {return false; } catch (Exception e) {return true; } } // End the transfer private void endTransfer(RdaAsyncResponse otf, Thread ott, RdaAsyncTransfer etf, Thread ett, RdaAgentMonitor amo, Thread amt) throws Exception {otf.endTransfer(); etf.endTransfer(); if (amt != null) amo.endTransfer(); while (ott.isAlive() || ett.isAlive()) {Thread.sleep(2500); trace(TRACE_EXEC, "."); } trace(TRACE_EXEC, "\\n"); traceln(TRACE_EXEC, "Shadow exit completed"); } private void endTransfer(RdaAsyncTransfer otf, Thread ott, RdaAsyncTransfer etf, Thread ett, RdaAgentMonitor amo, Thread amt) throws Exception {otf.endTransfer(); etf.endTransfer(); if (amt != null) amo.endTransfer(); while (ott.isAlive() || ett.isAlive()) {Thread.sleep(2500); } } // Get a file private void exec_get(ChannelSftp chn, String dst, String src) throws SftpException {traceln(TRACE_EXEC, " " + src + " -> " + dst); chn.get(src, dst); } // Get all files recursively from a subdirectory private void exec_mget(ChannelSftp chn, String dst, String src) throws SftpException {String nam; traceln(TRACE_EXEC, "Treating directory: " + src); // Validate the destination directory File fil = new File(dst); if (!(fil.exists() || fil.mkdir()) || !fil.isDirectory()) throw new SftpException(1, "Missing or invalid directory " + dst); // Get the file Vector tbl = chn.ls(src); Iterator ite = tbl.iterator(); while (ite.hasNext()) {LsEntry itm = (LsEntry) ite.next(); nam = itm.getFilename(); if (!itm.getAttrs().isDir()) exec_get(chn, dst, src + "/" + nam); else if (".".equals(nam) || "..".equals(nam)) traceln(TRACE_EXEC, "Skipping directory: " + src + "/" + nam); else exec_mget(chn, dst + "/" + nam, src + "/" + nam); } } // Create a remote directory when needed private String exec_mkdir(ChannelSftp chn, String dir) throws SftpException {SftpATTRS sta; try {sta = chn.stat(dir); } catch (Exception err) {chn.mkdir(dir); sta = chn.stat(dir); } if (sta.isDir()) return dir; throw new SftpException(1, "Missing or invalid remote directory " + dir); } // Put all files from a directory private void exec_mput(ChannelSftp chn, String dst, String src) throws SftpException {File dir = new File(src); String nam; File[] tbl = dir.listFiles(); for(int i = 0 ; i < tbl.length ; i++) {File fil = tbl[i]; nam = fil.getName(); if (!fil.isDirectory()) exec_put(chn, dst, fil.getAbsolutePath()); else if (".".equals(nam) || "..".equals(nam)) traceln(TRACE_EXEC, "Skipping directory: " + fil.getAbsolutePath()); else exec_mput(chn, exec_mkdir(chn, dst + "/" + nam), fil.getAbsolutePath()); } } // Put a file private void exec_put(ChannelSftp chn, String dst, String src) throws SftpException {traceln(TRACE_EXEC, " " + src + " -> " + dst); chn.put(src, dst); } } EOF my $NAM11 = 'RdaJsch'; my $COD11 = < " + lin); if (lin == null) {traceln(TRACE_SHDW, "Output flow broken"); return; } // Get the message and its attributes if (!pat.matcher(lin).find()) continue; dat = ' '; for (;;) {lin = getLine(msg); traceln(TRACE_SHDW, ">> " + lin); if (lin == null) {traceln(TRACE_SHDW, "Output flow broken"); return; } if (lin.length() == 0) break; if (lin.startsWith("end=\\"")) {dat = 'L'; arg = lin.substring(5, lin.lastIndexOf("\\"")); } else if (lin.startsWith("size=\\"")) {dat = 'S'; arg = lin.substring(6, lin.lastIndexOf("\\"")); } } // Treat the data part if (dat == ' ') {// Write the message and its attributes writeLines(msg); } else if (dat == 'L') {// Write the message and its attributes writeLines(msg); renameFile(hdr); // Treat the data traceln(TRACE_SHDW, "Data lines ending by '" + lin + "'"); for (;;) {lin = getLine(msg); traceln(TRACE_SHDW, ">>> " + lin); if (lin == null) {traceln(TRACE_SHDW, "Output flow broken"); return; } writeLines(msg); if ("".equals(lin)) break; } } else if (dat == 'S') {int siz; // Complete the header syncLine(msg); writeLines(msg); renameFile(hdr); // Determine the data size try {siz = Integer.valueOf(arg).intValue(); } catch (Exception err) {System.err.print("Invalid size value\\n"); break; } traceln(TRACE_SHDW, "Data (" + siz + " bytes)"); // Transfer the data writeData(siz); } // Indicate the message completion renameFile(end); } } catch (Exception err) {err.printStackTrace(); } } // End the transfer public void endTransfer() {run = false; } // Extract a line from the buffer public String getLine(StringBuffer msg) throws Exception {StringBuffer lin = new StringBuffer(); for (;; loadBuffer()) {// Extract the first line from the buffer if (off < lim) {syncLine(msg); for (; off < lim ; off++) {msg.append((char) buf[off]); if (buf[off] == 10) {nxt = false; off++; return lin.toString(); } if (buf[off] == 13) {nxt = true; if (++off < lim && buf[off] == 10) msg.append((char) buf[off++]); return lin.toString(); } lin.append((char) buf[off]); } } // Accept an incomplete last line if (!run) {if (lin.length() > 0) return lin.toString(); return null; } } } // Load the input buffer public void loadBuffer() throws Exception {try {int cnt = 0; off = lim = 0; while (run) {cnt = ics.available(); if (cnt > 0) {lim = ics.read(buf, 0, (cnt < max) ? cnt : max); traceBlock(TRACE_SHDW, buf, off, lim, "Input buffer:"); return; } if (cnt < 0) {run = false; return; } Thread.sleep(250); } run = false; } catch (Exception err) {this.err = err; run = false; } finally { } } // Rename the transfer file public void renameFile(File sta) {if (cur.renameTo(sta)) {cur = sta; traceln(TRACE_SHDW, "Control file renamed to " + cur.toString()); } else System.out.println("Cannot rename the control file"); } // Reset the transfer file public void resetFile() throws Exception {// Create a new status file cur = beg; cur.createNewFile(); // Truncate the transfer file ofs.getChannel().truncate(0); traceln(TRACE_SHDW, "Transfer file truncated"); } // Modify the trace level public void setLevel(byte lvl) {this.lvl = lvl; } // Modify the trace prefix public void setPrefix(String pre) {this.pre = pre; } // Synchronize a line public void syncLine(StringBuffer msg) throws Exception {if (nxt) {if (off >= lim && run) loadBuffer(); if (off < lim && buf[off] == 10) msg.append((char) buf[off++]); nxt = false; } } // Trace a block public void traceBlock(byte msk, byte[] buf, int off, int lim, String ttl) {if ((lvl & msk) == msk) {System.out.println(pre + "/" +mod + "[" + msk + "]: " + ttl); StringBuffer hex = new StringBuffer(); StringBuffer txt = new StringBuffer(); for (int i = off ; i < lim ;) {hex.setLength(0); txt.setLength(0); for (int j = i ; j < java.lang.Math.min(i + 16, lim) ; j++) {String tmp = (buf[j] > 15) ? Integer.toHexString(buf[j]) : "0" + Integer.toHexString(buf[j]); hex.append(tmp + " "); txt.append((buf[j] >= 32 && buf[j] < 127) ? (char) buf[j] : (char) 46 ); } int lgt = 48 - hex.length(); for (int cnt = 0 ; cnt < lgt ; cnt++) { hex.append(" "); } i = i + 16; System.out.println(" " + hex + " " + txt); } } } // Display a trace line public void traceln(byte msk, String txt) {if ((lvl & msk) == msk) System.out.println(pre + "/" +mod + "[" + msk + "]: " + txt); } // Write data public void writeData(int siz) throws Exception {if (siz > 0) {for (;; loadBuffer()) {int lgt = lim - off; if (siz < lgt) lgt = siz; if (lgt > 0) {traceBlock(TRACE_SHDW, buf, off, lgt, "Data fragment:"); ofb.write(buf, off, lgt); off += lgt; siz -= lgt; if (siz == 0) {ofb.flush(); return; } } } } } // Write lines public void writeLines(StringBuffer msg) throws Exception {if (msg.length() > 0) {byte[] str = msg.toString().getBytes(); traceBlock(TRACE_SHDW, str, 0, str.length, "Line buffer:"); ofb.write(str, 0, str.length); ofb.flush(); msg.setLength(0); } } } EOF my $NAM14 = 'RdaAsyncTransfer'; my $COD14 = < 0) {cnt = ics.read(buf, 0, (cnt < max) ? cnt : max); if (cnt > 0) {ops.write(buf, 0, cnt); ops.flush(); } } if (cnt < 0) break; Thread.sleep(25); } } catch (Exception err) {this.err = err; } finally {run = false; } } } EOF my $NAM15 = 'RdaAgentMonitor'; my $COD15 = < 'RDA::Object::Java', top => [$NAM, [$COD], $VER], dep => [['I', $NAM2, [$COD2], $VER, $PKG1], ['C', $NAM4, [$COD4], $VER, $PKG2], ['C', $NAM5, [$COD5], $VER, $PKG2], ['C', $NAM7, [$COD7], $VER, $PKG2], ['I', $NAM9, [$COD9], $VER, $PKG2], ['C', $NAM12, [$COD12], $VER, $PKG2], ['C', $NAM3, [$COD3], $VER, $PKG2], ['C', $NAM8, [$COD8], $VER, $PKG2], ['C', $NAM11, [$COD11], $VER, $PKG2], ['C', $NAM6, [$COD6], $VER, $PKG2], ['C', $NAM13, [$COD13], $VER, $PKG2], ['C', $NAM14, [$COD14], $VER, $PKG2], ['C', $NAM15, [$COD15], $VER, $PKG2], ['C', $NAM10, [$COD10], $VER, $PKG2], ], ); ## use critic # Define the global private constants my $END = "/QUIT\n"; my $OUT = qr{timeout}; my $WRK = 'jsch.tmp'; # Define the global private variables my %tb_cnv = ( CHK => \&_cnv_match, NXT => \&_cnv_next, PAT => \&_cnv_match, SRC => \&_cnv_array, ); my %tb_cmd = map {$_ => 1} qw(CHOOSE CLOSE COLLECT DEFAULT EXEC GET LOCAL LOGIN LOGOUT META OPEN PUT SHADOW TEST); # Report the package version sub Version { return $VERSION; } =head2 S<$h = RDA::Driver::Jsch-Enew($collector)> The remote access manager object constructor. It takes the collector object reference as an argument. =head2 S<$h-Enew($session)> The remote session manager object constructor. It takes the remote session object reference as an argument. C is represented by a blessed hash reference. The following special keys are used: =over 12 =item S< B<'-api'> > Version of the Java interface (M,S) =item S< B<'-cfg'> > Reference to the RDA configuration (M,S) =item S< B<'-cod'> > Reference to the main Java object (M,S) =item S< B<'-col'> > Reference to the collector object (M,S) =item S< B<'-ctl'> > Reference to the language control (M,S) =item S< B<'-flw'> > Interconnection flow direction (S) =item S< B<'-hit'> > Last prompt matched (S) =item S< B<'-hnd'> > Communication handle (M,S) =item S< B<'-ief'> > Interface error file (M,S) =item S< B<'-lim'> > Default execution limit (S) =item S< B<'-lin'> > Stored lines (S) =item S< B<'-lng'> > Interface language (M,S) =item S< B<'-msg'> > Last message (M,S) =item S< B<'-new'> > New connection indicator (M,S) =item S< B<'-nod'> > Node identifier (M,S) =item S< B<'-opt'> > Interface option (M,S) =item S< B<'-out'> > Timeout indicator (M,S) =item S< B<'-pid'> > Process identifier of the Java interface (M,S) =item S< B<'-pre'> > Trace prefix (M,S) =item S< B<'-ses'> > Reference to the session object (S) =item S< B<'-sig'> > Signaling file (S) =item S< B<'-skp'> > Skip indicator (M,S) =item S< B<'-sta'> > Last captured exit code (M,S) =item S< B<'-sys'> > Reference to the system view object (M,S) =item S< B<'-trc'> > Trace indicator (M,S) =item S< B<'-xfc'> > Transfer completion file (S) =item S< B<'-xfh'> > Transfer file handle (S) =item S< B<'-xfr'> > Transfer file path (S) =back Internal keys are prefixed by a dash. =cut sub new { my ($cls, $ses) = @_; my ($nod); # Create the object and return its reference $nod = $ses->get_oid; return ref($cls) ? bless { -api => $cls->{'-api'}, -cfg => $cls->{'-cfg'}, -cod => $cls->{'-cod'}, -col => $cls->{'-col'}, -ctl => $cls->{'-ctl'}, -lim => $ses->get_info('lim'), -lin => [], -lng => $cls->{'-lng'}, -msg => undef, -new => $cls->{'-col'}->get_first("REMOTE.$nod.B_NEW", $cls->{'-new'}), -nod => $nod, -opt => $cls->{'-opt'}, -out => 0, -pre => $cls->{'-col'}->get_first("REMOTE.$nod.W_PREFIX", $nod), -ses => $ses, -skp => 0, -sta => 0, -sys => $cls->{'-sys'}, -trc => $cls->{'-trc'} || $ses->get_level, }, ref($cls) : _create_manager(@_); } =head2 S<$h-Eas_type> This method returns the driver type. =cut sub as_type { return 'jsch'; } =head2 S<$h-Ecan_interconnect> This method indicates whether an interconnection is possible. =cut sub can_interconnect { my ($slf) = @_; return $slf->{'-cfg'}->can_spawn ? 1 : $slf->{'-cfg'}->is_windows ? -1 : 0; } =head2 S<$h-Edelete_object> This method deletes the object. =cut sub delete_object { # Close the communication handle end($_[0]); # Delete the object undef %{$_[0]}; undef $_[0]; return; } # Close the Java interface sub end { my ($slf) = @_; my ($hnd); if ($hnd = delete($slf->{'-hnd'})) { eval { local $SIG{'PIPE'} = 'IGNORE'; $hnd->syswrite($END, length($END)); $hnd->close; }; delete($slf->{'-pid'}); } return $slf; } =head2 S<$h-Eget_access> This method indicates that the driver does not support passwords. =cut sub get_access { return shift->{'-col'}->get_access; } =head2 S<$h-Eget_api> This method returns the version of the Java interface. It returns an undefined value in case of problems. =cut sub get_api { return shift->{'-api'}; } =head2 S<$h-Eget_hit> This method returns the last prompt matched. It returns an undefined value in case of problems. =cut sub get_hit { return shift->{'-hit'}; } =head2 S<$h-Eget_lines> This method returns the lines stored during the last command execution. =cut sub get_lines { return @{shift->{'-lin'}}; } =head2 S<$h-Eget_message> This method returns the last message. =cut sub get_message { return shift->{'-msg'}; } =head2 S<$h-Eget_status> This method returns the last captured status. =cut sub get_status { return shift->{'-sta'}; } =head2 S<$h-Ehas_timeout> This method indicates whether the last request encountered a timeout. =cut sub has_timeout { return shift->{'-out'}; } =head2 S<$h-Einterconnect($var,$ifh,$ofh,$efh)> This method creates a communication channel with a remote command. It returns its process identifier. =cut sub interconnect { my ($slf, $var, $ifh, $ofh, $efh) = @_; my ($bkp, $dis, $flg, $msg, $trc); # Start the interconnection channel $flg = $slf->{'-cfg'}->can_spawn; $trc = $slf->{'-trc'}; $slf->{'-cod'}->set_info('pre', $trc ? 'JSCH' : q{}); $bkp = $slf->{'-sys'}->set_context({RDA_DUMP => undef, RDA_LEVEL => undef, RDA_TRACE => undef, RDA_TRACK => undef}); $slf->{'-hnd'} = $ifh; if ($flg) { eval {$slf->{'-pid'} = $slf->{'-ctl'}->interconnect_code($ifh, $ofh, $efh, $slf->{'-lng'}, $NAM, @{$slf->{'-opt'}})}; $msg = $@; $dis = 1 if RDA::Object::Rda->is_windows; } else { eval {($slf->{'-pid'}, undef, $slf->{'-ief'}) = $slf->{'-ctl'}->pipe_code($ifh, $slf->{'-lng'}, $NAM, @{$slf->{'-opt'}})}; $msg = $@; } $slf->{'-sys'}->restore_context($bkp); if ($msg) { $msg =~ s/[\n\r\s]+$//; $slf->{'-msg'} = $msg; return $slf->{'-hnd'} = undef; } binmode($ifh); # Initialize the interconnection channel if (exists($slf->{'-ses'})) { my ($fil, $wrk, %var); # Load some defaults return $slf->{'-hnd'} = undef if _set_default($slf, $dis); # Start the shadow agent if ($var->{'LOC'}) { $var{'CMD#'} = _set_array(\%var, 'CMD', [$slf->{'-cfg'}->get_value('T_SELF'), @{$var->{'OPT'}}]); } else { $var{'CMD'} = join(q{ }, map {RDA::Object::Rda->quote($_)} @{$var->{'CMD'}}, @{$var->{'OPT'}}); } $var{'HST'} = $var->{'HST'}; $var{'LCK'} = RDA::Object::Rda->native($fil) if RDA::Object::Rda->is_windows && defined($fil = $slf->{'-ses'}->get_lock); $var{'LIM'} = 30; $var{'PWD'} = $var->{'PWD'} if exists($var->{'PWD'}); $var{'STA'} = 1; $var{'TMP'} = 1; $var{'USR'} = $var->{'USR'} if exists($var->{'USR'}); $wrk = $slf->{'-col'}->get_work($slf->{'-nod'}.q{_}.$WRK, 1); $var{'WRK'} = RDA::Object::Rda->native($wrk); unless ($flg) { $wrk =~ s/\.tmp$//; $var{'MSG'} = RDA::Object::Rda->native($slf->{'-xfr'} = $wrk); $slf->{'-flw'} = 1; $slf->{'-sig'} = $fil = "$wrk.sig"; 1 while unlink($fil); $slf->{'-xfc'} = "$wrk.end"; $slf->{'-xfh'} = $ofh; $slf->{'-xfr'} = $fil = "$wrk.msg"; $msg = IO::File->new; $msg->open($fil, $CREATE, $FIL_PERMS) or die get_string('ERR_CREATE', $fil, $!); $msg->close; open($ofh, "<$fil") ## no critic (Close,Open) or die get_string('ERR_OPEN', $fil, $!) } return $slf->{'-hnd'} = undef if request($slf, $var->{'LOC'} ? 'LOCAL' : 'SHADOW', {%var}); } # Return the process identifier return $slf->{'-pid'}; } =head2 S<$h-Eis_skipped> This method indicates whether the last request was skipped. =cut sub is_skipped { return shift->{'-skp'}; } =head2 S<$h-Eneed_password([$var])> This method indicates whether the driver needs a password. =cut sub need_password { my ($slf, $var) = @_; my ($ret); $ret = -1; $var = {} unless ref($var) eq 'HASH'; $var->{'FCT'} = [\&_check_connect, \$ret]; $var->{'NEW'} = 1; request($slf, 'TEST', $var); return $ret; } =head2 S<$h-Eneed_pause> This method indicates whether the current connection could require a pause for providing a password. =cut sub need_pause { return 0; } =head2 S<$h-Erequest($cmd,$var,@dat)> This method executes a requests and returns the result file. It supports the following commands: =over 2 =item * C It performs required input selection with the remote server. =item * C It closes any existing session with the remote server. =item * C It submits a command to the remote servers and collects the results. It manages the command and continuation prompts. =item * C It changes some interface parameters. =item * C It submits one or more commands to the remote servers and collects the results. =item * C It performs required authentication with the remote server. =item * C It ends any current session with the remote server. =item * C It returns the interface information. =item * C It closes any existing session and starts a new session with the remote server. =item * C It closes the interface. =back It returns a negative value in case of problems. =cut sub request ## no critic (Complex) { my ($slf, $cmd, $var, @dat) = @_; my ($buf, $cnt, $err, $fct, $lim, $msk, $sta, $tmp, $trc, $wrk, @arg); local $SIG{'__WARN__'} = sub {}; # Validate the request $slf->{'-out'} = 0; return -30 unless defined($cmd) && ref($var) eq 'HASH'; return -31 unless exists($tb_cmd{$cmd}); # Get the communication handle unless (_get_handle($slf)) { $slf->{'-skp'} = 1 unless defined($slf->{'-msg'}); return -32; } # Execute the request eval { local $SIG{'ALRM'} = 'IGNORE' if exists($SIG{'ALRM'}); local $SIG{'PIPE'} = sub {die "Pipe broken\n"}; # Prepare the request $trc = $slf->{'-pre'}.'] ' if $slf->{'-trc'}; $lim = exists($var->{'LIM'}) ? $var->{'LIM'} : 0; if (exists($var->{'FCT'})) { ($fct, @arg) = @{$fct} if ref($fct = delete($var->{'FCT'})) eq 'ARRAY'; } elsif (exists($var->{'COL'})) { $wrk = delete($var->{'COL'}); ($fct, @arg) = ( ## no critic (Comma) ref($wrk) ? \&_write_result : \&_copy_result, $wrk, delete($var->{'CLR'})); } elsif ($cmd eq 'EXEC') { if (exists($var->{'FLG'})) { ($fct, @arg) = (\&_load_lines, $var->{'FLG'}); } elsif (exists($var->{'OUT'})) { $wrk = delete($var->{'OUT'}); ($fct, @arg) = ( ## no critic (Comma) ref($wrk) ? \&_write_result : \&_copy_result, $wrk, delete($var->{'CLR'})); } $var->{'NEW'} = 1 if $slf->{'-new'}; } elsif ($cmd eq 'GET') { $var->{'DST'} = RDA::Object::Rda->native($var->{'DST'}) if exists($var->{'DST'}); } elsif ($cmd eq 'PUT') { $var->{'SRC'} = (ref($var->{'SRC'}) eq 'ARRAY') ? [map {RDA::Object::Rda->native($_)} @{$var->{'SRC'}}] : RDA::Object::Rda->native($var->{'SRC'}) if exists($var->{'SRC'}); } elsif ($cmd eq 'CHOOSE' || $cmd eq 'LOGIN') { ($fct, @arg) = (\&_load_lines, $var->{'FLG'}) if exists($var->{'FLG'}); } $wrk = $tmp = $slf->{'-col'}->get_work($WRK, 1); $var->{'WRK'} = RDA::Object::Rda->native($wrk); $wrk =~ s/\.tmp$/.txt/; 1 while unlink($tmp, $wrk); if (exists($var->{'STA'})) { $sta = $wrk; $sta =~ s/\.txt$/.sta/; $var->{'ERR'} = RDA::Object::Rda->native($sta); $var->{'STA'} = 1; $slf->{'-sta'} = -33; } else { $slf->{'-sta'} = 0; $var->{'ERR'} = RDA::Object::Rda->native($var->{'ERR'}) if exists($var->{'ERR'}); } $slf->{'-lin'} = []; # Amend some driver attributes foreach my $key (keys(%{$var})) { &{$tb_cnv{$key}}($slf, $var, $key) if exists($tb_cnv{$key}); } if ($cmd eq 'DEFAULT') { $slf->{'-lim'} = $var->{'LIM'} if exists($var->{'LIM'}); $slf->{'-new'} = 1 if delete($var->{'NEW'}); $slf->{'-pre'} = $var->{'PRE'} if exists($var->{'PRE'}); $slf->{'-trc'} = $var->{'TRC'} if exists($var->{'TRC'}); } # Send the request $msk = exists($var->{'MSK'}) ? $var->{'MSK'} : 'PPH|PWD'; debug(join(qq{\n}, $trc."Executing a $cmd request", map {m/^($msk)$/ ? "$trc $_=***" : "$trc $_='".$var->{$_}.q{'}} sort keys(%{$var}))) if $trc; $buf = @dat ? join(qq{\n}, (map {$_.q{='}.$var->{$_}.q{'}} keys(%{$var})), q{#}.$cmd, @dat, qq{/\n}) : join(qq{\n}, (map {$_.q{='}.$var->{$_}.q{'}} keys(%{$var})), q{/}.$cmd, q{}); $slf->{'-hnd'}->syswrite($buf, length($buf)); # Wait for the request completion debug($trc, "Waiting the $cmd results") if $trc; $cnt = $lim ? $lim + 2 : 0; $err = $slf->{'-ief'}; while (! -e $wrk) { die _get_error($err).qq{\n} if -s $err; die "Request timeout\n" if $lim && --$cnt < 0; debug($trc, "* Sleeping ($cnt)") if $trc; sleep(1); } die _get_error($err).qq{\n} if -s $err; $tmp = undef; # Treat the result when requested &$fct($slf, $wrk, @arg) if $fct; if ($sta && -f $sta) { _check_status($slf, $sta); 1 while unlink($sta); } 1 while unlink($wrk); }; # Indicate the completion status if ($buf = $@) { $buf =~ s/[\n\r\s]+$//; $slf->{'-msg'} = $buf; debug($trc, 'Error: ', $buf) if $trc; RDA::Object::Rda->kill_child($slf->{'-pid'}); $slf->{'-hnd'}->close; $slf->{'-hnd'} = undef; if ($buf =~ $OUT) { $slf->{'-out'} = 1; $slf->{'-sta'} = -34; } else { $slf->{'-sta'} = -35; } # Treat partial results when requested eval { if ($fct && $tmp && exists($var->{'TMP'})) { rename($tmp, $wrk) if -f $tmp; &$fct($slf, $wrk, @arg) if -f $wrk; } }; } else { $slf->{'-col'}->clean_work($WRK); } return $slf->{'-sta'}; } =head2 S<$h-Eswitch([$flow])> This method manages changes in flow direction. =cut sub switch { my ($slf, $flw) = @_; my ($cnt, $ifh, $trc); return unless exists($slf->{'-xfh'}); # Close the interconnection when requested $trc = $slf->{'-pre'}.q{] } if $slf->{'-trc'}; unless (defined($flw)) { # Create the signaling file when needed if (exists($slf->{'-sig'})) { debug($trc, 'Creating the signaling file '.$slf->{'-sig'}) if $trc; $ifh = IO::File->new; $ifh->open($slf->{'-sig'}, $CREATE, $FIL_PERMS) or die get_string('ERR_CREATE', $slf->{'-sig'}, $!); $ifh->close; delete($slf->{'-sig'}); } # Close the transfer file debug($trc, 'Closing the transfer file '.$slf->{'-xfr'}) if $trc; close($slf->{'-xfh'}); delete($slf->{'-hnd'}); delete($slf->{'-pid'}); delete($slf->{'-xfc'}); delete($slf->{'-xfh'}); delete($slf->{'-xfr'}); return; } # Treat direction inversion if ($flw) { unless ($slf->{'-flw'}) { # Terminate the message treatment 1 while unlink($slf->{'-xfc'}); debug($trc, 'Truncating the transfer file') if $trc; truncate($slf->{'-xfh'}, 0); $slf->{'-flw'} = $flw; } } else { if ($slf->{'-flw'}) { # Wait for a message debug($trc, 'Waiting a message') if $trc; while (! -e $slf->{'-xfc'}) { debug($trc, q{* Sleeping (}.++$cnt.q{) for }.$slf->{'-xfc'}) if $trc; sleep(1); } debug($trc, 'Rewinding the transfer file') if $trc; seek($slf->{'-xfh'}, 0, 0); $slf->{'-flw'} = $flw; } } return; } # --- Result handling routines ------------------------------------------------ # Check a connection status sub _check_connect { my ($slf, $wrk, $var) = @_; my ($ifh, $trc); $ifh = IO::File->new; $trc = $slf->{'-pre'}.'] ' if $slf->{'-trc'}; debug($trc, 'Check the connection status') if $trc; $$var = 1; if ($ifh->open("<$wrk")) { while (<$ifh>) { s/[\n\r\s]+$//; debug($trc, '* ', $_) if $trc; if (m/^OK\b/) { $$var = 0; last; } elsif (m/^Error:\s*(.*)$/) { $slf->{'-msg'} = $1; } } $ifh->close; } return; } # Check an execution status sub _check_status { my ($slf, $wrk) = @_; my ($ifh, $str, $trc); $ifh = IO::File->new; $trc = $slf->{'-pre'}.'] ' if $slf->{'-trc'}; debug($trc, 'Check the execution status') if $trc; if ($ifh->open("<$wrk")) { while (<$ifh>) { s/[\n\r\s]+$//; debug($trc, '* ', $_) if $trc; if (m/^Exit:\s*(\-?\d+)/) { $slf->{'-sta'} = $1 << 8; } elsif (m/^Error:\s*(.*)$/) { $slf->{'-msg'} = $1; } elsif (m/^Hit:\s*'(.*)'/) { $slf->{'-hit'} = $1; } elsif (m/^Hit:\s*((?:[A-Za-z\d]{2})*)/) { $str = $1; $str =~ s/([A-Za-z\d]{2})/chr(hex($1))/eg; $slf->{'-hit'} = $str; } } $ifh->close; } return; } # Copy the result into a file sub _copy_result { my ($slf, $src, $dst, $new) = @_; my ($buf, $ifh, $lgt, $ofh); debug($slf->{'-pre'}, '] Tranferring results') if $slf->{'-trc'}; $ifh = IO::File->new; $ofh = IO::File->new; $ifh->open("<$src") or die get_string('ERR_OPEN', $src, $!); $ofh->open($dst, $new ? $CREATE : $APPEND, $FIL_PERMS) or die get_string('ERR_TRANSFER', $dst, $!); binmode($ofh); while ($lgt = $ifh->sysread($buf, 65536)) { $ofh->syswrite($buf, $lgt) or die get_string('ERR_WRITE', $dst, $!); } $ifh->close; $ofh->close; return; } # Load the results sub _load_lines { my ($slf, $wrk, $flg) = @_; my ($ifh); $ifh = IO::File->new; debug($slf->{'-pre'}, '] Loading execution ', $flg ? 'results' : 'errors') if $slf->{'-trc'}; if ($ifh->open("<$wrk")) { while (<$ifh>) { s/[\n\r\s]+$//; push(@{$slf->{'-lin'}}, $_) if $flg || m/RDA-\d{5}:/; } $ifh->close; } return; } # Load the interface information sub _load_meta { my ($slf, $wrk) = @_; my ($ifh, $trc); $ifh = IO::File->new; $trc = $slf->{'-pre'}.'] ' if $slf->{'-trc'}; debug($trc, 'Loading META results') if $trc; if ($ifh->open("<$wrk")) { while (<$ifh>) { s/[\n\r\s]+$//; debug($trc, '* ', $_) if $trc; $slf->{$1} = $2 if m/^(\-\w+)\='(.*)'/; } $ifh->close; } return; } # Write the result into a report or a buffer sub _write_result { my ($slf, $src, $dst) = @_; my ($buf, $ifh, $lgt); debug($slf->{'-pre'}, '] Tranferring results') if $slf->{'-trc'}; $ifh = IO::File->new; $ifh->open("<$src") or die get_string('ERR_OPEN', $src, $!); while ($lgt = $ifh->sysread($buf, 65536)) { $dst->syswrite($buf, $lgt) or die get_string('ERR_WRITE', $dst->get_file, $!); } $ifh->close; return; } # --- Conversion routines ----------------------------------------------------- sub _cnv_array { my ($slf, $var, $key) = @_; $var->{$key.q{#}} = _set_array($var, $key, delete($var->{$key})) if ref($var->{$key}) eq 'ARRAY'; return; } sub _cnv_match { my ($slf, $var, $key) = @_; if (ref($var->{$key}) eq 'ARRAY') { my ($cnt); $cnt = 0; foreach my $val (@{delete($var->{$key})}) { ++$cnt; $var->{$key.$cnt} = _fmt_match($val); } $var->{$key.q{#}} = $cnt; } else { $var->{$key} = _fmt_match($var->{$key}); } return; } sub _cnv_next { my ($slf, $var) = @_; if (ref($var->{'NXT'}) eq 'ARRAY') { my ($ack, $cnt, $val); $ack = exists($var->{'ACK'}) ? _fmt_octal(delete($var->{'ACK'})) : '0012'; $cnt = 0; foreach my $rec (@{delete($var->{'NXT'})}) { next unless ref($rec) eq 'ARRAY' && defined($val = $rec->[0]) && length($val); ++$cnt; $var->{"NXT$cnt"} = _fmt_match($val); $var->{"ACK$cnt"} = (defined($val = $rec->[1]) && length($val)) ? _fmt_octal($val) : $ack; } $var->{'NXT#'} = $var->{'ACK#'} = $cnt; } else { $var->{'ACK'} = _fmt_octal($var->{'ACK'}) if exists($var->{'ACK'}); $var->{'NXT'} = _fmt_match($var->{'NXT'}); } return; } sub _fmt_match { my ($str) = @_; my ($opt, $pat, @opt); ($pat, $opt) = ($str =~ m{^\s*/(.*)/([a-z]*)}) ? ($1, $2) : ($str =~ m{^\s*m\s*\{(.*)\}([a-z]*)}) ? ($1, $2) : ($str =~ m{^\s*m\s*\[(.*)\]([a-z]*)}) ? ($1, $2) : ($str =~ m{^\s*m\s*\((.*)\)([a-z]*)}) ? ($1, $2) : ($str =~ m{^\s*m\s*\<(.*)\>([a-z]*)}) ? ($1, $2) : ($str =~ m{^\s*m\s*(\W)(.*)\1([a-z]*)}) ? ($2, $3) : ($str, q{}); $pat = q{(?}.join(q{}, @opt).q{)}.$pat if $opt && (@opt = grep {m/[ims]/} split(//, $opt)); return $pat; } sub _fmt_octal { my ($str) = @_; return join(q{}, map {sprintf('%04o', $_)} unpack('c*', $str)); } sub _set_array { my ($var, $key, $arr) = @_; my ($cnt); $cnt = 0; foreach my $val (@{$arr}) { ++$cnt; $var->{$key.$cnt} = $val; } return $cnt; } # --- Internal routines ------------------------------------------------------- # Create the driver manager sub _create_manager { my ($cls, $col, $lim) = @_; my ($cod, $ctl, $dep, $lib, $rem, $slf, $trc); # Try to locate JSCH $trc = $col->get_trace('JSCH'); if (defined($lib = $col->get_first('REMOTE.T_JSCH_JAR')) && -r $lib) { debug("JSCH] Use $lib") if $trc; } else { debug('JSCH] Searching for jsch.jar ...') if $trc; return unless ($lib = _get_jar($col)); debug("JSCH] => $lib found") if $trc; } # Compile the Java interface eval { $ctl = $col->get_inline->force_context('Java'); debug("JSCH] Defining $NAM Java block ...") if $trc; $cod = $ctl->add_common( RDA::Object::Java->new(@{$INLINE{'top'}})->add_jar($lib)->add_sequence); $cod->set_warning(deprecation => 2); foreach my $dep (@{$INLINE{'dep'}}) { debug('JSCH] Defining ', $dep->[1], ' Java block ...') if $trc; $dep = $ctl->add_common($cod->add_dependency( RDA::Object::Java->new_block(@{$dep})->add_jar($lib))); $dep->set_warning(deprecation => 2); } }; if ($@) { debug("JSCH] Error in Java code generation:\nJSCH] $@") if $trc; return; } # Initialize the authentication agent $rem = $col->get_remote; $rem->set_agent; # Create the driver manager object $slf = bless { -cfg => $col->get_config, -cod => $cod, -col => $col, -ctl => $ctl, -lng => $cod->get_language, -msg => undef, -new => $col->get_first('REMOTE.B_NEW'), -nod => 'JSCH', -opt => $rem->has_agent ? ['-a'] : [], -out => 0, -pre => 'JSCH', -sta => 0, -sys => $col->get_agent->get_system, -trc => $trc, }, $cls; # Request the interface information to test the interface return request($slf, 'META', {FCT => [\&_load_meta], LIM => $lim}) ? undef : end($slf); } # Get the Java error sub _get_error { my ($err) = @_; my ($buf, $ifh); return 'Request error' unless -s $err && ($ifh = IO::File->new)->open("<$err"); $buf = join("\n ", <$ifh>); $ifh->close; return $buf; } # Get the communication handle sub _get_handle { my ($slf) = @_; my ($bkp, $msg); # Initialise the communication handle on the first call unless (exists($slf->{'-hnd'})) { $slf->{'-cod'}->set_info('pre', $slf->{'-trc'} ? q{JSCH} : q{}); $bkp = $slf->{'-sys'}->set_context({RDA_DUMP => undef, RDA_LEVEL => undef, RDA_TRACE => undef, RDA_TRACK => undef}); eval {($slf->{'-pid'}, undef, $slf->{'-ief'}) = $slf->{'-ctl'}->pipe_code($slf->{'-hnd'} = IO::File->new, $slf->{'-lng'}, $NAM, @{$slf->{'-opt'}})}; $msg = $@; $slf->{'-sys'}->restore_context($bkp); if ($msg) { $msg =~ s/[\n\r\s]+$//; $slf->{'-msg'} = $msg; return $slf->{'-hnd'} = undef; } # Load some defaults return $slf->{'-hnd'} = undef if exists($slf->{'-ses'}) && _set_default($slf); } # Delete the previous message $slf->{'-msg'} = undef; # Return the remote handle return $slf->{'-hnd'}; } # Get jsch.jar location sub _get_jar { my ($col) = @_; my ($cfg, $dir, $lib); $cfg = $col->get_config; return $lib if -r ($lib = $cfg->cat_file('da', 'lib', 'jsch.jar')) || -r ($lib = $cfg->get_file('D_RDA', 'da/lib/jsch.jar')) || -r ($lib = $cfg->get_file('D_RDA', '../lib/jsch.jar')); foreach my $key ($col->grep('_ORACLE_HOME$','r')) { return $lib if -d ($dir = $cfg->cat_dir($col->get_first($key))) && defined($lib = _get_jar_home($cfg, $dir)); } return $lib if defined($dir = $cfg->get_env('ORACLE_HOME')) && -d ($dir = $cfg->cat_dir($dir)) && defined($lib = _get_jar_home($cfg, $dir)); return; } sub _get_jar_home { my ($cfg, $dir) = @_; my ($lib); foreach my $rec (['oui', 'modules', 'jsch.jar'], ['oui', 'jlib', 'jsch.jar'], ['oracle_common', 'modules', 'jsch-0.1.51.jar'], ['oracle_common', 'modules', 'jsch-0.1.44.jar'], ['sysman', 'jlib', 'j2ee', 'jsch.jar'], ['jdeveloper', 'jdev', 'lib', 'jsch.jar'], ['jdeveloper', 'sqldeveloper', 'lib', 'jsch.jar'], ['sqldeveloper', 'jdev', 'lib', 'jsch.jar'], ['sqldeveloper', 'sqldeveloper', 'lib', 'jsch.jar']) { return $lib if -r ($lib = $cfg->cat_file($dir, @{$rec})); } return; } # Set some defaults sub _set_default { my ($slf, $flg) = @_; my ($val, %var); if ($val = $slf->{'-col'}->get_first('REMOTE.T_AGENT_LOG')) { $val = sprintf($val, $$); $var{'AGT'} = RDA::Object::Rda->quote(RDA::Object::Rda->is_unix ? $val : RDA::Object::Rda->short($val)); } elsif ($slf->{'-trc'} & 0x0100) ## no critic (Bit,Number) { $var{'AGT'} = 'RdaSsh.log'; } $var{'LIM'} = $slf->{'-lim'} if $slf->{'-lim'}; $var{'PRE'} = $slf->{'-pre'}; $var{'RDA'} = $slf->{'-cfg'}->get_value('T_SHORT'); $var{'TRC'} = $flg ? 0 : $slf->{'-trc'} & 0xff; ## no critic (Bit,Number) $var{'RDA#'} = _set_array(\%var, 'RDA', scalar $slf->{'-cfg'}->get_value('T_SELF')) unless $slf->{'-cfg'}->is_cygwin; return request($slf, 'DEFAULT', {%var}); } 1; __END__ =head1 SEE ALSO L, L, L, L, L, L, L, L =head1 COPYRIGHT NOTICE Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. =head1 TRADEMARK NOTICE Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners. =cut