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