tags:

views:

1689

answers:

11

(This is a question my coworker posted elsewhere, but I thought I'd post it here to see if I could hit a different audience.)

Hello all, I'm testing the possibility of writing a small java application the will use Psexec to kick off remote jobs. In the course of testing binding the stdin and stdout of a java program to psexec I came across an odd bug.

My test program is a basic echo program. It starts a thread to read from stdin and then pipes the read output directly back to stdout. When run on the local machine, not from psexec, it works beautifully. Exactly as it should.

However, when I call it from PsExec the first time the input is piped directly into stdout it is lost. What makes the bug really bizzare is that it is only the first time the input is piped directly into stdout that it is lost. If the input String is appended to another string it works fine. Either a String literal or a String variable. However, if the input String is sent directly to stdout it doesn't go through. The second time it is sent to stdout it goes through fine - and everytime there after.

I'm at a complete loss as to what's going on here. I've tried to test for every possible bug I can think of. I'm out of ideas. Did I miss one or is this just something inside psexec?

Here is the code in question, it's in three classes (one of which implements an interface which is a single function interace).

The Main class:

public class Main {
    public static void main(String[] args) {
        System.out.println("Starting up.");

        CReader input = new CReader(new BufferedReader(
            new InputStreamReader(System.in)));
        CEcho echo = new CEcho();

        input.addInputStreamListener(echo);
        input.start();

        System.out.println("Successfully started up.  Awaiting input.");
    }
}

The CReader class which is the thread that reads from stdin:

public class CReader extends Thread {
    private ArrayList<InputStreamListener> listeners = 
        new ArrayList<InputStreamListener>();
    private boolean exit = false;
    private Reader in;

    public CReader(Reader in) {
        this.in = in;
    }

    public void addInputStreamListener(InputStreamListener listener) {
        listeners.add(listener);
    }

    public void fireInputRecieved(String input) {

        if(input.equals("quit"))
        exit = true;

        System.out.println("Input string has made it to fireInputRecieved: "
            + input);

        for(int index = 0; index < listeners.size(); index++)
            listeners.get(index).inputRecieved(input);
    }

    @Override
    public void run() {

        StringBuilder sb = new StringBuilder();
        int current = 0, last = 0;

        while (!exit) {
            try {
                current = in.read();
            }
            catch (IOException e) {
                System.out.println("Encountered IOException.");
            }

            if (current == -1) {
                break;
            }

            else if (current == (int) '\r') {
                if(sb.toString().length() == 0) {
                    // Extra \r, don't return empty string.
                    continue;
                }
                fireInputRecieved(new String(sb.toString()));
                sb = new StringBuilder();
            }

            else if(current == (int) '\n') {
                if(sb.toString().length() == 0) {
                    // Extra \n, don't return empty string.
                    continue;
                }
                fireInputRecieved(new String(sb.toString()));
                sb = new StringBuilder();
            }
            else {
                System.out.println("Recieved character: " + (char)current);
                sb.append((char) current);
                last = current;
            }
        }    
    }
}

The CEcho class, which is the class that pipes it back to stdout:

public class CEcho implements InputStreamListener {
    public void inputRecieved(String input) {
        System.out.println("\n\nSTART INPUT RECIEVED");
        System.out.println("The input that has been recieved is: "+input);
        System.out.println("It is a String, that has been copied from a " +
            "StringBuilder's toString().");
        System.out.println("Outputting it cleanly to standard out: ");
        System.out.println(input);
        System.out.println("Outputting it cleanly to standard out again: ");
        System.out.println(input);
        System.out.println("Finished example outputs of input: "+input);
        System.out.println("END INPUT RECIEVED\n\n");
    }
}

And finally, here is the program output:

>psexec \\remotecomputer "C:\Program Files\Java\jre1.6.0_05\bin\java.exe" -jar "C:\Documents and Settings\testProram.jar"

PsExec v1.96 - Execute processes remotely
Copyright (C) 2001-2009 Mark Russinovich
Sysinternals - www.sysinternals.com


Starting up.
Successfully started up.  Awaiting input.
Test
Recieved character: T
Recieved character: e
Recieved character: s
Recieved character: t
Input string has made it to fireInputRecieved: Test


START INPUT RECIEVED
The input that has been recieved is: Test
It is a String, that has been copied from a StringBuilder's toString().
Outputting it cleanly to standard out:

Outputting it cleanly to standard out again:
Test
Finished example outputs of input: Test
END INPUT RECIEVED
A: 

Does this help?

http://stackoverflow.com/questions/1221739/using-psexec-returns-no-output-from-the-remote-task

Devon_C_Miller
We tried using the full domain name to the machine, no luck, but thanks anyway!
mwalling
A: 

Is System.out not configured for autoflush? After the first print try System.out.flush() and see if the first line appears without more lines being printed.

(oh yeah, seriously, it is "RECEIVED", not "RECIEVED".)

Mark
Nope nothing's different. I'm pretty sure System.out is configured for auto flush by default. And if it wasn't, none of the other printlns should have been sent either. The really bizarre thing about the bug is that it's just the first time you try sending JUST input with out any character's appended. And it's only after input has been set using the StringBuilder. Any other print attempts work fine. It was still worth a try though, thank you.Also, it was a typo. I don't usually think too hard about spelling when coding. And especially not when I'm distracted by a bug like this :p
Daniel Bingham
@Alcon - and *I* am supposed to be the one who can't speel!
mwalling
@mwalling You are the one who can't spell :p I'm just the one who's lazy.
Daniel Bingham
A: 

OK, I've been thinking about this over the weekend and I since you are jumping from machine to machine I wonder if maybe there is a CharSet issue? Maybe it is eating the string the first time and dealing with a different code page or character set issue? Java is 16bit characters normally and windows is either 8bit with code pages or utf-8 these days.

Any chance the local and remote machines have different default character sets? If you are sending localized data over the net it might misbehave.

Mark
Well I suppose there's a chance of it, but they're both Windows XP SP2 built from the same core load and running the same JRE. So it shouldn't really be a character set difference problem. But it's conceivable that its somehow getting converted to a java char set when being read in and then not converted back when being put out. I can try to fix this by forcing the chart set on the way back out. Still I don't think this is likely to be it.
Daniel Bingham
I'm worried that PSExec has a different idea of the charset, too. When you append the text it works flawlessly, but if you print it raw it fails the first time... maybe PSExec has to expand its idea of charset and loses the string in the process? Admittedly it is a SWAG but once you start passing strings between pipes/ports this stuff happens.
Mark
I'll try forcing a few charsets and see what happens.
Daniel Bingham
Alright, tried forcing the BufferedReader that gets passed to CReader to use the Latin-1 and the US-ASCII charsets. Neither made a difference. Doesn't mean that it isn't a charset snafu, but if those two don't work then I kinda doubt that forcing a charset's gonna fix it :( Unless there's somewhere else I need to force it.
Daniel Bingham
+2  A: 

have you tried redirecting the output into a file ( java... >c:\output.txt )? this way you could doublecheck if everything is going into stdout and maybe just getting eaten by psexec

Niko
Good idea. Let me try and see what happens.
Daniel Bingham
Nice call, sure enough psexec is eating it. Okay, so it's an issue with psexec. Now is there a way around it?
Daniel Bingham
I'm accepting this, since it was the first person who said it was psexec's fault, and the bounty needs to go somewhere.
mwalling
A: 

What I see when running psexec is that it spawns a child window to do the work but doesnt return that program's output to it's console window. I would suggest using WMI or some form of windows process API framework to gain a level of control you appear to lack with psexec. Surely java has an equivalent to .Net's System.Diagnotics.Process class.

Joe Caffeine
A: 

Maybe you could try passing a copy of input to your listeners:

 public void fireInputRecieved(String input) {

    if(input.equals("quit"))
    exit = true;

    String inputCopy = new String(input);
    System.out.println("Input string has made it to fireInputRecieved: "
        + input);



    for(int index = 0; index < listeners.size(); index++)
        listeners.get(index).inputRecieved(inputCopy);
}

I had similar problems with listeners where a passed variable would end up empty unless I did pass an explicit copy of it.

Shirkrin
I thought of that and tried similar things, but we've now established that it is definitely psexec and not java that's eating the output. Thanks though!
Daniel Bingham
A: 

I don't necessarily have an answer, but some comments may prove helpful.

  • The "pass a copy" idea shouldn't matter, since your output successfully prints the string twice before the failure, then succeeds again afterward.
  • auto-flush shouldn't matter either, as you've already mentioned
  • Niko's suggestion has some merit, for diagnostic purposes. Mixed with Mark's suggestion, it makes me wonder if there aren't some invisible control characters getting involved somewhere. What if you printed the characters byte values as a diagnostic step?
  • You know that the value is "Test" (at least in the output you gave us). What happens if you pass "Test" directly to the failing printLn statement?
  • In situations like this, you want to gain as much information as possible. Insert breakpoints and analyze characters. Send the bytes to files and open them in hex editors. Do whatever you can to trace things as accurately and as precisely as possible.
  • Come up with weird test scenarios and try them, even if they shouldn't possibly help. You never know what good idea you might have while analyzing the results of the hopeless idea.
John Fisher
Tried passing Test directly to a println in many places and it went through fine. Also tried using inputs other than test and they haven't come back either. So it's nothing to do with the value "test". Using the output capture suggestion we've established that it's definitely psexec eating the output now. The question is why.
Daniel Bingham
Based on those comments, I've got to assume that there is a difference between the "Test" that you received as input and the "Test" that you entered into the code. What is that difference?
John Fisher
You could test my last comment a bit more thoroughly by putting this line at the top of your inputReceived function: 'input = "Test"'. If it always works, but "Test" doesn't work through the normal program flow, the there is something sneaky and malicious hiding in the received text string which is not in the coded string.
John Fisher
A: 

Have you tried running it through cmd.exe?

Noon Silk
I'm calling psexec from a command prompt, not from Java itself. This is psexec in a command prompt calling java on the remote machine. Are you suggesting a run Java through the cmd.exe?
Daniel Bingham
Alcon: forgive me, I misread your question. I thought you were executing 'psexec' from java, and trying to capture the output. Sorry.
Noon Silk
A: 

How are you executing PsExec? My suspicion is that this is some code within PsExec which is actually doing echo suppression, possibly for the purposes of protecting a password. One way to test this hypothesis would be to change this code:

    System.out.println("Outputting it cleanly to standard out: ");
    System.out.println(input);
    System.out.println("Outputting it cleanly to standard out again: ");
    System.out.println(input);

to this:

    System.out.println("Outputting it cleanly to standard out: ");
    System.out.print(' ');
    System.out.println(input);
    System.out.println("Outputting it cleanly to standard out again: ");
    System.out.println(input);

...thereby causing the output to be (if I'm right):

Outputting it cleanly to standard out:
 Test
Outputting it cleanly to standard out again:
Test
Finished example outputs of input: Test

In particular, it's noticeable that the apparently-suppressed line is the first line which consists solely of Test - which is exactly the text you've just sent to the remote system. This sounds like PsExec attempting to suppress a remote system which is echoing its input in addition to producing its own output.

Is the password of the user on the remote machine perhaps Test? Are you using PsExec's -p parameter? Are you specifying -i?

Jon Bright
Neither parameter and no, the password is not Test. You might be on to something though - maybe Psexec does suppress echo text. Of course, if that were the case I would have expected someone to have posted it on the SysInternals forum by now. *goes to check what those parameters do*
Daniel Bingham
Do have a go with the additional space. If that works, then we're very likely on the right path with echo-suppression. If it doesn't, then you can safely ignore me :-)
Jon Bright
No luck. It still eats the output.
Daniel Bingham
Whoops, didn't see your second comment till just now when I commented. It was a great idea, but it didn't pan. The output test suggested a few posts down has established that it's definitely psexec eating the output. But it doesn't seem to have anything to do with echo suppression.
Daniel Bingham
+1  A: 

PsExec is eating the output. Next interesting thing might be where it's eating the output. You could check this by getting a copy of Wireshark and checking whether the output in question is traversing the network or not. If it's not, then it's being eaten on the remote side. If it is, it's being eaten locally.

Not that I'm really sure where to go from there, but collecting more information certainly seems like a good path to be following...

Jon Bright
A: 

I'd guess that there is a bogus byte in there prefacing the T. According to JavaDocs, an InputStreamReader will read one or more bytes, and decode them into characters.

You could have an escape sequence or spurious byte in there, masquerading as a multibyte character.

Quick check - see if "current" is ever > 128 or < 33.

What if you used a CharArrayReader to get individual bytes, without any charset translation?

The theory is that during the first attempt to output the String using println, it's sending an escape character of some sort, eating the rest of the string. During later prints, either Java or the network pipe are handling or removing it, since it previously got that escape sequence, perhaps changing the handling in some way.

As an unrelated nit, sb.toString() returns a new String, so it's unnecessary to call "new String(sb.toString())"

jmanning2k
The new String() was just one of the bajillion things I did to try and figure out what was going wrong. I didn't think it should make a difference, but I wanted to isolate the input from the output as much as possible. You know how debugging goes, you get to a point where you're about willing to try ANYTHING even if it doesn't really make sense ;)
Daniel Bingham
I'll try the check and see what happens.
Daniel Bingham