exec.Command output for Windows query command

Hi! I’m new to GO and run into a problem where I would need some help.
I’m writing a little utility (on Manjaro Linux, GO version: go1.17.6 linux/amd64) and compile it for Windows (W10, amd64).
(I have no Administrator rights on the Windows machine, so I can’t install GO there.)
It should start the Windows query utility (“query user /server:xxx.xxx.xxx.xxx”) in loop for a number of hosts and I would like to do some further processing on the result.
Below is a simplified version of the code with only the troublesome bits.

The code itself runs perfectly and if I replace “query” with some linux commands, it prints the proper output for every iteration on my dev Linux machine. I think it’s not really a language problem but rather a specific Windows interaction problem with GO.
For testing, I’ve chosen 3 IPs with 3 different “query” responses:
(Responses when query is started from the Windows command prompt)

  1. Remote machine is used: query’s OUTPUT is the USER NAME
  2. Remote machine is free: query’s OUTPUT is No user exists for
  3. Remote machine is down: query’s OUTPUT is Error The RPC server is unavailable

When I run the code, the fmt.print at the end of the loop prints the correct user name for the PC where a user is present. For the other two possibilities, it prints nothing. The String is empty.

I asked this question over at Stackoverflow.com and there were some ideas with pipes but none of the suggested solutions has worked.
I don’t understand what the difference is, when I start query from the command line and when I start it inside my code. If I replace “query” with “ipconfig” and run it on the Windows machine, it nicely prints the output for each iteration.
What does query differently, so that the StdOut in my code only receives one of the three possible answers. Does anyone have an idea? Thanks.

package main

import (
    "os/exec"
    "strings"
    "fmt"
    "syscall"
    "log"
)

var ip_addresses []string

func main() {
        fmt.Print("Starting... \n")
        ip_addresses = append(ip_addresses,"/server:192.168.10.1")
        ip_addresses = append(ip_addresses,"/server:192.168.10.3")
        ip_addresses = append(ip_addresses,"/server:192.168.10.4")
        

    for _, eachline := range ip_addresses {
        if strings.HasPrefix(eachline, "#") != true {
            fmt.Print("IP Address: ", eachline, "\n")
            cmd := exec.Command("query", "user", eachline)
            stdout, err := cmd.Output()
            if exit, ok := err.(*exec.ExitError); ok {
                if status, ok := exit.Sys().(syscall.WaitStatus); ok {
                    log.Printf("Exit Status: %d", status.ExitStatus())
                }
            } else {
                log.Fatal(err)
            }
            fmt.Print(string(stdout))
        }
    }
}
1 Like

I’m able to duplicate this, and I think I have a workaround, but I don’t really understand why. Here’s similar code that I wrote:

package main

import (
	"fmt"
	"os/exec"
)

func main() {
	servers := []string{
		"localhost",
	}

	for _, server := range servers {
		cmd := exec.Command("query", "user", "/server:" + server)
		out, err := cmd.Output()
		fmt.Println(string(out))
		if err != nil {
			if ee, ok := err.(*exec.ExitError); ok {
				fmt.Println("ExitError:", string(ee.Stderr))
			} else {
				fmt.Println("err:", err)
			}
		}
	}
}

Which produces the following output:

C:\Users\Sean>go run Downloads\test.go
 USERNAME              SESSIONNAME        ID  STATE   IDLE TIME  LOGON TIME
 sean                  rdp-tcp#2           2  Active          .  1/25/2022 6:48 AM

ExitError:

Which (I think) tells me that query user is getting its output, but also returning a non-nil error.

Thanks Sean! :+1: What happens if you run your code with a non existent host? Query should display the error message as in #3. Does your code prints that message? (I can only test your code tomorrow morning when I log in to my work PC.) Or is this code not yet the workaround…
It’s too late and I’m no longer able to think in Go…:grinning_face_with_smiling_eyes:

Yes, the (*exec.ExitError).Stderr contains the message:

Error 0x000006BA enumerating sessionnames
Error [1722]:The RPC server is unavailable.

Fantastic! I just tested it and it works perfectly. Thank you! :slightly_smiling_face:

Have you tried experimenting with capturing both stdout and stderr from the executed command? Sometimes, information that’s meant for stderr gets mixed up with stdout. That might explain why you’re not getting the expected results for some cases.
You mentioned that the outputs differ when running “query” from your code versus the command line. It’s intriguing how Windows commands can sometimes behave differently when executed programmatically. Speaking of exploring, have you considered checking out the helpful tips on the cheap Windows keys subreddit? It’s surprising how those tips can sometimes lead to solutions you wouldn’t expect.

Thank you for your reply.
I abandoned the project as it was no longer necessary.