001/* 002 * Copyright (c) 2004-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: barseghian $' 006 * '$Date: 2012-10-30 22:37:51 +0000 (Tue, 30 Oct 2012) $' 007 * '$Revision: 30990 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.ssh; 031 032import java.io.BufferedReader; 033import java.io.BufferedWriter; 034import java.io.ByteArrayOutputStream; 035import java.io.File; 036import java.io.FileInputStream; 037import java.io.FileOutputStream; 038import java.io.IOException; 039import java.io.InputStreamReader; 040import java.io.OutputStream; 041import java.io.OutputStreamWriter; 042import java.nio.channels.FileChannel; 043import java.util.Collection; 044import java.util.Iterator; 045 046import org.apache.commons.logging.Log; 047import org.apache.commons.logging.LogFactory; 048import org.kepler.util.FilenameFilter_RegularPattern; 049 050import ptolemy.util.StringUtilities; 051 052/** 053 * Local command execution. This class implements the ExecInterface to provide 054 * the same functionality for local operations as what Ssh does for remote ones. 055 * Thus, other classes can hide the difference between an ssh execution and 056 * local execution. 057 */ 058public class LocalExec implements ExecInterface { 059 060 private static final Log log = LogFactory.getLog(LocalExec.class.getName()); 061 private static final boolean isDebugging = log.isDebugEnabled(); 062 063 private static int nInstances = 0; // to catch the very first instantiation 064 065 // timeout variables 066 private int timeout = 0; // timeout in seconds 067 private boolean timeoutRestartOnStdout = false; // restart timer if stdout 068 // has data 069 private boolean timeoutRestartOnStderr = false; // restart timer if stderr 070 // has data 071 072 // public final static int timeoutErrorCode = -32767; 073 074 public LocalExec() { 075 _commandCount = getSystemProps(); 076 nInstances++; 077 078 /* 079 * On local host we have no session opening/closing, so this is the 080 * place to generate a SESSION_OPENED event, but only once (and there 081 * will be no SESSION_CLOSED event) 082 */ 083 if (nInstances == 1){ 084 SshEventRegistry.instance.notifyListeners(new SshEvent( 085 SshEvent.SESSION_OPENED, "local")); 086 } 087 088 } 089 090 /** 091 * addIdentity, useless for local exec 092 */ 093 public void addIdentity(String identity) { 094 // do nothing 095 } 096 097 /** 098 * port forwarding not working on local exec 099 */ 100 public void setPortForwardingL(String spec) throws ExecException { 101 // do nothing 102 } 103 104 /** 105 * port forwarding not working on local exec 106 */ 107 public void setPortForwardingR(String spec) throws ExecException { 108 // do nothing 109 } 110 111 public boolean openConnection() throws ExecException { 112 return true; 113 } 114 115 public void closeConnection() { 116 // do nothing 117 } 118 119 /** 120 * Specify if killing of external processes (i.e. clean-up) after error or 121 * timeout is required. Not implemented for local execution. 122 */ 123 public void setForcedCleanUp(boolean foo) { 124 } 125 126 /** 127 * Set timeout for the operations. Timeout should be given in seconds. If 128 * 'stdout' is set to true, the timer is restarted whenever there is data on 129 * stdout. If 'stderr' is set to true, the timer is restarted whenever there 130 * is data on stderr. executeCmd will throw an ExecException, an instance of 131 * ExecTimeoutException if the timeout limit is reached. 'seconds' = 0 means 132 * no timeout at all. 133 */ 134 public void setTimeout(int seconds, boolean stdout, boolean stderr) { 135 timeout = seconds; 136 timeoutRestartOnStdout = stdout; 137 timeoutRestartOnStderr = stderr; 138 } 139 140 public void setTimeout(int seconds) { 141 timeout = seconds; 142 } 143 144 /** 145 * Execute a command on the local machine 'command' is the full command with 146 * arguments to be executed return: the exit code of the command additional 147 * effects: streamOut is continuously written during execution the stdout of 148 * the command. Similarly, streamErr is continuously written during exec the 149 * stderr. It forwards all Exceptions that arise during java exec. 150 */ 151 public int executeCmd(String command, OutputStream streamOut, 152 OutputStream streamErr) throws ExecException { 153 return executeCmd(command, streamOut, streamErr, null); 154 } 155 156 public int executeCmd(String command, OutputStream streamOut, 157 OutputStream streamErr, String thirdPartyTarget) 158 throws ExecException { 159 _commandArr[_commandCount] = command; 160 161 Runtime rt = Runtime.getRuntime(); 162 Process proc; 163 164 // get the pwd/passphrase to the third party (and perform authentication 165 // if not yet done) 166 String pwd = SshSession.getPwdToThirdParty(thirdPartyTarget); 167 168 169 try { 170 proc = rt.exec(_commandArr); 171 } catch (Exception ex) { 172 //ex.printStackTrace(); 173 throw new ExecException("Cannot execute cmd ** : " + _commandArr[_commandCount] + ex); 174 } 175 176 // System.out.println("%%% Process started"); 177 178 // the streams from the process: stdout and stderr 179 BufferedReader out_in = new BufferedReader(new InputStreamReader(proc 180 .getInputStream())); // stdout 181 BufferedReader err_in = new BufferedReader(new InputStreamReader(proc 182 .getErrorStream())); // stderr 183 184 // the streams towards the caller: stdout and stderr 185 BufferedWriter out_out = new BufferedWriter(new OutputStreamWriter( 186 streamOut)); 187 BufferedWriter err_out = new BufferedWriter(new OutputStreamWriter( 188 streamErr)); 189 190 BufferedWriter proc_in = new BufferedWriter(new OutputStreamWriter(proc 191 .getOutputStream())); // stdin 192 193 String line; // Temp for each line of output. 194 int exitVal = -32766; 195 boolean readOut = true; 196 boolean readErr = true; 197 boolean finished = false; 198 boolean checkForPwd = (pwd != null); 199 char c[] = new char[256]; 200 int charsRead; 201 202 // variables for the timeout checking 203 long start = System.currentTimeMillis(); 204 long current = 0; 205 long maxtime = timeout * 1000L; 206 207 while (!finished) { // will stop when the process terminates or after 208 // timeout 209 // check the status of the process 210 try { 211 exitVal = proc.exitValue(); 212 finished = true; // process terminated so exit this loop after 213 // reading the buffers 214 } catch (IllegalThreadStateException ex) { 215 // process not yet terminated so we go further 216 } 217 218 // read stdout 219 if (readOut) { 220 try { 221 while (out_in.ready()) { 222 charsRead = out_in.read(c, 0, 256); 223 out_out.write(c, 0, charsRead); 224 225 // System.out.println("%%% "+ new String(c, 0, 226 // charsRead)); 227 /* 228 * try { proc_in.write("Anyadat\n", 0, 8); // send the 229 * password proc_in.flush(); } catch (Exception ex) { 230 * System.out.println("### "+ex); 231 * 232 * } 233 */ 234 if (checkForPwd 235 && containsPasswordRequest(c, 0, charsRead)) { 236 237 // System.out.println("%%% Found password request"); 238 239 out_out.flush(); // so you may see the request on 240 // stdout already 241 proc_in.write(pwd + "\n", 0, pwd.length() + 1); // send 242 // the 243 // password 244 proc_in.flush(); 245 log.info("Sent password to third party."); 246 checkForPwd = false; // even if it's wrong, do not 247 // do it again 248 } 249 if (timeoutRestartOnStdout) 250 start = System.currentTimeMillis(); // restart 251 // timeout timer 252 } 253 } catch (IOException ioe) { 254 log.error("<IOException> when reading the stdout: " + ioe 255 + "</IOException>"); 256 readOut = false; 257 } 258 } 259 260 // read stderr 261 if (readErr) { 262 try { 263 while (err_in.ready()) { 264 charsRead = err_in.read(c, 0, 256); 265 err_out.write(c, 0, charsRead); 266 System.out 267 .println("### " + new String(c, 0, charsRead)); 268 if (checkForPwd 269 && containsPasswordRequest(c, 0, charsRead)) { 270 271 System.out.println("### Found password request"); 272 273 out_out.flush(); // so you may see the request on 274 // stdout already 275 proc_in.write(pwd + "\n", 0, pwd.length() + 1); // send 276 // the 277 // password 278 proc_in.flush(); 279 log.info("Sent password to third party."); 280 checkForPwd = false; // even if it's wrong, do not 281 // do it again 282 } 283 if (timeoutRestartOnStderr) 284 start = System.currentTimeMillis(); // restart 285 // timeout timer 286 } 287 } catch (IOException ioe) { 288 log.error("<IOException> when reading the stderr: " + ioe 289 + "</IOException>"); 290 readErr = false; 291 } 292 } 293 294 // sleep a bit to not overload the system 295 if (!finished) 296 try { 297 java.lang.Thread.sleep(100); 298 } catch (InterruptedException ex) { 299 } 300 301 // check timeout 302 current = System.currentTimeMillis(); 303 if (timeout > 0 && maxtime < current - start) { 304 log.error("Timeout: " + timeout + "s elapsed for command " 305 + command); 306 proc.destroy(); 307 throw new ExecTimeoutException(command); 308 // exitVal = timeoutErrorCode; 309 // finished = true; 310 } 311 312 } 313 314 try { 315 // flush to caller 316 out_out.flush(); 317 err_out.flush(); 318 // close streams from/to child process 319 out_in.close(); 320 err_in.close(); 321 proc_in.close(); 322 } catch (IOException ex) { 323 log.error("Could not flush output streams: " + ex); 324 } 325 326 // System.out.println("ExitValue: " + exitVal); 327 return exitVal; 328 329 } 330 331 /** 332 * Look for one of the strings password/passphrase/passcode in the char[] 333 * array. Return true if found any. Case insensitive search. 334 */ 335 private boolean containsPasswordRequest(char[] buf, int startPos, int endPos) { 336 // look for strings password/passphrase/passcode 337 int i = startPos; 338 while (i +7 < endPos) { 339 if (Character.toLowerCase(buf[i]) == 'p' 340 && Character.toLowerCase(buf[i + 1]) == 'a' 341 && Character.toLowerCase(buf[i + 2]) == 's' 342 && Character.toLowerCase(buf[i + 3]) == 's') { 343 344 // found "pass", look further for word/code/phrase 345 if (Character.toLowerCase(buf[i + 4]) == 'w' 346 && Character.toLowerCase(buf[i + 5]) == 'o' 347 && Character.toLowerCase(buf[i + 6]) == 'r' 348 && Character.toLowerCase(buf[i + 7]) == 'd') { 349 log.info("PWDSearch: found request for password."); 350 return true; 351 } else if (Character.toLowerCase(buf[i + 4]) == 'c' 352 && Character.toLowerCase(buf[i + 5]) == 'o' 353 && Character.toLowerCase(buf[i + 6]) == 'd' 354 && Character.toLowerCase(buf[i + 7]) == 'e') { 355 log.info("PWDSearch: found request for passcode."); 356 return true; 357 } else if ((i + 9 < endPos) 358 && (Character.toLowerCase(buf[i + 4]) == 'p' 359 && Character.toLowerCase(buf[i + 5]) == 'h' 360 && Character.toLowerCase(buf[i + 6]) == 'r' 361 && Character.toLowerCase(buf[i + 7]) == 'a' 362 && Character.toLowerCase(buf[i + 8]) == 's' 363 && Character.toLowerCase(buf[i + 9]) == 'e')) { 364 log.info("PWDSearch: found request for passphrase."); 365 return true; 366 } 367 } 368 i = i + 1; 369 } 370 return false; 371 } 372 373 /** 374 * Create a directory given as String parameter. It calls File.mkdir() or 375 * File.mkdirs() depending on the parentflag. Returns true iff directory is 376 * created. False is not returned but an exception otherwise. It catches 377 * SecurityException (from File.mkdir() or File.mkdirs()) and rethrows it as 378 * ExecException. 379 * 380 * The method works equivalently with SshExec.createDir(). That is, if the 381 * directory exists and parentflag is set, true is returned; if parentflag 382 * is not set, an exception is thrown. 383 */ 384 public boolean createDir(String dir, boolean parentflag) 385 throws ExecException { 386 387 if (dir == null || dir.trim().length() == 0) { 388 log.error("Directory name not given"); 389 return false; 390 } 391 392 boolean b = false; 393 try { 394 File d = new File(dir); 395 396 // error check: a file with this name exists and is not a directory 397 if (d.exists() && !d.isDirectory()) { 398 throw new ExecException("File " + dir 399 + " exists but is not a directory."); 400 } 401 402 // error check: directory exists 403 if (d.isDirectory()) 404 if (parentflag) // parentflag is set: we are done and return 405 // success 406 return true; 407 else 408 // parentflag is not set: we should return error 409 throw new ExecException("Directory " + dir 410 + " already exists."); 411 412 // call mkdir or mkdirs 413 if (parentflag) 414 b = d.mkdirs(); 415 else 416 b = d.mkdir(); 417 418 if (!b) { 419 throw new ExecException("Directory " + dir 420 + " has NOT been created for unknown reasons"); 421 } 422 } catch (SecurityException ex) { 423 throw new ExecException("Security error: " + ex); 424 } 425 426 return b; 427 } 428 429 /** 430 * To be implemented. Delete files or directories! BE CAREFUL It should be 431 * relative to the current dir, or an absolute path For safety, * and ? is 432 * allowed in filename string only if explicitely asked with allowFileMask = 433 * true If you want to delete a directory, recursive should be set to true 434 * 435 * @return true if succeeded throws ExecException 436 */ 437 public boolean deleteFile(String fname, boolean recursive, 438 boolean allowFileMask) throws ExecException { 439 440 if (fname == null || fname.trim().length() == 0) 441 throw new ExecException("File name not given"); 442 443 // some error checking to avoid malicious removals 444 if (!allowFileMask) { 445 if (fname.indexOf('*') != -1 || fname.indexOf('?') != -1) 446 throw new ExecException( 447 "File name contains file mask, but this was not allowed: " 448 + fname); 449 } 450 451 if (fname.equals("*") || fname.equals("./*") || fname.equals("../*") 452 || fname.equals("/*")) 453 throw new ExecException( 454 "All files in directories like . .. / are not allowed to be removed: " 455 + fname); 456 457 String temp = fname; 458 if (temp.length() > 1 && temp.endsWith(File.separator)) { 459 temp = temp.substring(0, temp.length() - 1); 460 if (isDebugging) 461 log.debug(" % " + fname + " -> " + temp); 462 } 463 464 if (temp.equals(".") || temp.equals("..") || temp.equals("/") 465 || temp.equals(File.separator)) 466 throw new ExecException( 467 "Directories like . .. / are not allowed to be removed: " 468 + fname); 469 470 // end of error checking 471 472 // to be implemented... 473 LocalDelete ld = new LocalDelete(); 474 return ld.deleteFiles(fname, recursive); 475 476 } 477 478 /** 479 * Copy local files to a local/remote directory Input: files is a Collection 480 * of files of type File, targetPath is either a directory in case of 481 * several files, or it is either a dir or filename in case of one single 482 * local file recursive: true if you want traverse directories 483 * 484 * @return number of files copied successfully 485 */ 486 public int copyTo(Collection files, String targetPath, boolean recursive) 487 throws ExecException { 488 489 int numberOfCopiedFiles = 0; 490 491 Iterator fileIt = files.iterator(); 492 while (fileIt.hasNext()) { 493 File lfile = (File) fileIt.next(); 494 numberOfCopiedFiles += copyTo(lfile, targetPath, recursive); 495 } 496 return numberOfCopiedFiles; 497 } 498 499 /** 500 * Copy a local file to a local directory/path Input: file of type File 501 * (which can be a directory). The file name can be wildcarded too (but not 502 * the path elements!). targetPath is either a directory or filename 503 * 504 * @return number of files copied successfully (i.e either returns true or 505 * an exception is raised) 506 */ 507 public int copyTo(File lfile, String targetPath, boolean recursive) 508 throws ExecException { 509 510 File[] files = null; 511 // if the file is wildcarded, we need the list of files 512 // Anand : Changed getName() to getPath() 513 //getName fails in case of *.txt - indexOf returns '0' 514 String name = lfile.getPath(); 515 516 517 if (name.indexOf("*") > 0 || name.indexOf("?") > 0) { 518 String pattern = name.replaceAll("\\.", "\\\\.").replaceAll("\\*", 519 ".*").replaceAll("\\?", "."); 520 521 FilenameFilter_RegularPattern filter = new FilenameFilter_RegularPattern( 522 pattern); 523 String dirname = lfile.getParent(); 524 if (dirname == null || dirname == "") 525 dirname = "."; 526 File dir = new File(dirname); 527 files = dir.listFiles(filter); 528 529 } else { // no wildcards 530 files = new File[1]; 531 files[0] = lfile; 532 } 533 534 int numberOfCopiedFiles = 0; 535 for (int i = 0; i < files.length; i++) 536 numberOfCopiedFiles += _copyTo(files[i], targetPath, recursive); 537 538 return numberOfCopiedFiles; 539 } 540 541 /** 542 * Copy _one_ local file to a local directory/path Input: file of type File 543 * (which can be a directory) Input must not have wildcards. targetPath is 544 * either a directory or filename 545 * 546 * @return number of files copied successfully (i.e either returns true or 547 * an exception is raised) 548 */ 549 private int _copyTo(File lfile, String targetPath, boolean recursive) 550 throws ExecException { 551 552 if (!lfile.exists()) { 553 throw new ExecException("File does not exist: " + lfile); 554 } 555 556 // check: recursive traversal of directories enabled? 557 if (lfile.isDirectory()) { 558 if (!recursive) 559 throw new SshException("File " + lfile 560 + " is a directory. Set recursive copy!"); 561 } 562 563 int numberOfCopiedFiles = 0; 564 565 try { 566 // recursive handling of directories 567 if (lfile.isDirectory()) { 568 numberOfCopiedFiles = copyDir(lfile, new File(targetPath)); 569 } else { 570 // copy one file 571 File target = new File(targetPath); 572 if (target.exists() && target.isDirectory()) 573 target = new File(targetPath, lfile.getName()); 574 copyFile(lfile, target); 575 numberOfCopiedFiles++; 576 } 577 } catch (IOException ex) { 578 log.error(ex); 579 throw new ExecException("Cannot copy " + lfile + " to " 580 + targetPath + ":\n" + ex); 581 } 582 583 return numberOfCopiedFiles; 584 } 585 586 /** 587 * Copy files from a directory to a local path Input: 'files' is a 588 * Collection of files of type String (! not like at copyTo !), 'sourcePath' 589 * is either empty string (or null) in case the 'files' contain full paths 590 * to the individual files, or it should be a remote dir, and in this case 591 * each file name in 'files' will be extended with the remote dir name 592 * before copy. 'localPath' should be a directory name in case of several 593 * files. It can be a filename in case of a single file to be copied. This 594 * is a convenience method for copyFrom on several remote files. recursive: 595 * true if you want traverse directories 596 * 597 * @return number of files copied successfully 598 */ 599 public int copyFrom(String sourcePath, Collection files, File localPath, 600 boolean recursive) throws ExecException { 601 602 int numberOfCopiedFiles = 0; 603 604 String sdir; 605 if (sourcePath == null || sourcePath.trim().equals("")) { 606 sdir = ""; 607 } else { 608 sdir = sourcePath; 609 if (!sdir.endsWith(File.separator)) 610 sdir = sdir + File.separator; 611 } 612 613 Iterator fileIt = files.iterator(); 614 while (fileIt.hasNext()) { 615 String sfile = (String) fileIt.next(); 616 numberOfCopiedFiles += copyFrom(sdir + sfile, localPath, recursive); 617 } 618 return numberOfCopiedFiles; 619 } 620 621 /** 622 * Copy a local file into a local file Input: 'sfile' of type String (can be 623 * a directory or filename) 'localPath' is either a directory or filename 624 * Only if 'recursive' is set, will directories copied recursively. 625 * 626 * @return number of files copied successfully (i.e either returns true or 627 * an exception is raised) Note: on local filesystem, this method 628 * does the same as copyTo 629 */ 630 public int copyFrom(String sourcePath, File localPath, boolean recursive) 631 throws ExecException { 632 633 return copyTo(new File(sourcePath), localPath.getAbsolutePath(), 634 recursive); 635 636 } 637 638 /* 639 * 640 * Private methods 641 */ 642 643 /** 644 * Copies src directory to dst. It assumes that src exists and is a 645 * directory. If the dst directory does not exist, it is created. If it 646 * exists, a subdirectory with the name of src is created. Thus, this method 647 * works the same way as 'cp' and 'scp' and org.kepler.ssh.SshExec 648 */ 649 private int copyDir(File src, File dst) throws IOException { 650 651 int numberOfCopiedFiles = 0; 652 653 if (dst.exists()) { 654 // create a subdirectory withing dst (to be compliant with 'cp' and 655 // 'scp') 656 dst = new File(dst, src.getName()); 657 } 658 dst.mkdir(); 659 660 numberOfCopiedFiles++; // the directory counts one 661 662 File[] files = src.listFiles(); 663 for (int i = 0; i < files.length; i++) { 664 // if (isDebugging) log.debug(" % " + files[i]); 665 if (files[i].isDirectory()) { 666 numberOfCopiedFiles += copyDir(files[i], new File(dst, files[i] 667 .getName())); 668 } else { 669 copyFile(files[i], new File(dst, files[i].getName())); 670 numberOfCopiedFiles++; 671 } 672 } 673 return numberOfCopiedFiles; 674 } 675 676 /** 677 * Copies src file to dst file. If the dst file does not exist, it is 678 * created 679 */ 680 private void copyFile(File src, File dst) throws IOException { 681 // see if source and destination are the same 682 if(src.equals(dst)) { 683 // do not copy 684 return; 685 } 686 687 //System.out.println("copying " + src + " to " + dst); 688 689 FileChannel srcChannel = new FileInputStream(src).getChannel(); 690 FileChannel dstChannel = new FileOutputStream(dst).getChannel(); 691 dstChannel.transferFrom(srcChannel, 0, srcChannel.size()); 692 srcChannel.close(); 693 dstChannel.close(); 694 695 /* hacking for non-windows */ 696 // set the permission of the target file the same as the source file 697 if (_commandArr[0] == "/bin/sh") { 698 699 String osName = StringUtilities.getProperty("os.name"); 700 if(osName.startsWith("Mac OS X")) { 701 702 // chmod --reference does not exist on mac, so do the best 703 // we can using the java file api 704 // WARNING: this relies on the umask to set the group, world 705 // permissions. 706 dst.setExecutable(src.canExecute()); 707 dst.setWritable(src.canWrite()); 708 709 } else { 710 711 String cmd = "chmod --reference=" + src.getAbsolutePath() + " " 712 + dst.getAbsolutePath(); 713 try { 714 ByteArrayOutputStream streamOut = new ByteArrayOutputStream(); 715 ByteArrayOutputStream streamErr = new ByteArrayOutputStream(); 716 executeCmd(cmd, streamOut, streamErr); 717 } catch (ExecException e) { 718 log 719 .warn("Tried to set the target file permissions the same as " 720 + "the source but the command failed: " 721 + cmd 722 + "\n" + e); 723 } 724 } 725 } 726 } 727 728 //TODO: Anand: this function does not handle case of windows 7. 729 //It considers windows 7 to be in last else block, hence added 730 //and extra condition for contains(windows). 731 private int getSystemProps() { 732 // Get OS name 733 String osName = System.getProperty("os.name"); 734 //System.out.println("<OS>" + osName + "</OS>"); 735 if (osName.equals("Windows 95")) { 736 _commandArr[0] = "command.com"; 737 _commandArr[1] = "/C"; 738 _charsToSkip = 6; 739 return 2; 740 } else if (osName.equals("Windows NT") || osName.equals("Windows XP") 741 || osName.equals("Windows 2000") || osName.toLowerCase().contains("windows") ) { 742 _commandArr[0] = "cmd.exe"; 743 _commandArr[1] = "/C"; 744 _charsToSkip = 6; 745 return 2; 746 } else { 747 _commandArr[0] = "/bin/sh"; 748 _commandArr[1] = "-c"; 749 _charsToSkip = 5; 750 return 2; 751 } 752 } // end-of-getSystemProps 753 754 // //////////////////////////////////////////////////////////////////// 755 // // private variables //// 756 757 // The combined command to execute. 758 private int _commandCount; 759 private String _commandStr = ""; 760 private String _commandArr[] = new String[3]; 761 private int _charsToSkip = 6; 762 763} // end-of-class-CondorJob