Calling appcmd results in the wrong password being set - go

I'm trying to call appcmd from within Go. The code below shows success, but the password is set to the wrong thing. If I remove the inner quotes (on the second line of main) it works, but then it doesn't work when the password includes spaces! Now WITH the quotes, if I type in cmd.exe the command exactly as it outputs, it works! So what the heck! Why does it work with the quotes directly in cmd but not when called from Go?
I really don't want to be that guy who says you can't use spaces in passwords because I can't figure out why it doesn't work! UGH!
package main
import (
"bytes"
"fmt"
"os/exec"
"strconv"
"strings"
"syscall"
)
func main() {
iisPath := "C:\\WINDOWS\\sysWOW64\\inetsrv\\"
callAppcmd(iisPath, "-processModel.password:\"password\"")
}
func callAppcmd(iisPath string, param string) {
stdOut, _, _, exitCode := runCommand(
iisPath+"appcmd.exe",
"set",
"apppool",
"/apppool.name:DefaultAppPool",
param)
printOut(stdOut)
printOut(strconv.Itoa(exitCode))
}
func printOut(text string) {
fmt.Println(text)
}
func runCommand(commands ...string) (string, string, error, int) {
printOut(strings.Join(commands, " "))
cmd := exec.Command(commands[0], commands[1:]...)
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
var out bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
err := cmd.Run()
exitCode := 0
if exitError, ok := err.(*exec.ExitError); ok {
exitCode = exitError.ExitCode()
}
return out.String(), stderr.String(), err, exitCode
}
Output:
C:\WINDOWS\sysWOW64\inetsrv\appcmd.exe set apppool /apppool.name:DefaultAppPool -processModel.password:"password"
APPPOOL object "DefaultAppPool" changed
0

It seems to format the string with backticks is a solution to this, which will not do automatic escaping and can process the quotes properly.
cmd := exec.Command(`find`)
cmd.SysProcAttr.CmdLine = `find "SomeText" test.txt`
Please refer to the below link.
exec with double quoted argument

Related

Send stdout of running command to its stdin in go

I have a somewhat challenging situation where I need to write into a system command stdin the same stdout it outputs (in another running program), here's an example program that represents what I mean:
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
)
func main() {
rand.Seed(time.Now().Unix())
var greetings []string = []string{"hi", "hola", "bonjour", "hallo", "whats up"}
var greeting string = greetings[rand.Intn(len(greetings))]
fmt.Println(greeting)
reader := bufio.NewReader(os.Stdin)
message, _ := reader.ReadString('\n')
if message == greeting+"\n" {
fmt.Println("nice to meet you!")
} else {
fmt.Println("oops!")
}
}
Since you greet with a random greeting, you have to read the stdout, send it to stdin and also capture if it was the correct answer or not. I've tried with stdinpipes but it freezes waiting for the stdin close since I think that only works for the start of the command run only, so for a running program it hasn't been working for me...
I appreciate any help!
EDIT
I wanted to add sort of what I was trying to do, I've tried without channels as well but it didn't seem to make a difference on the outcome, it just freezes waiting for stdin to close and I need to get first stdout before closing stdin since it consists of it:
package main
import (
"io"
"os/exec"
)
func main() {
cmd := exec.Command("./executable_program")
stdout, _ := cmd.StdoutPipe()
stdin, _ := cmd.StdinPipe()
var c chan []byte = make(chan []byte)
cmd.Start()
go func() {
b, _ := io.ReadAll(stdout)
c <- b
}()
stdin.Write(<-c)
stdin.Close()
cmd.Wait()
}
You can use a pipe to join the stdout to the stdin of the program that you execute:
package main
import (
"io"
"os/exec"
)
func main() {
r, w := io.Pipe()
cmd := exec.Command("<name-of-program-to-run>")
cmd.Stdin = r
cmd.Stdout = w
cmd.Run()
}
To see this in action, first let's prepare a test program to be executed by the program above. This test program simply prints a line to stdout, and then reads each line of stdin and prints it to stdout until stdin is closed.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
fmt.Fprint(os.Stdout, "priming the pump!\n")
s := bufio.NewScanner(os.Stdin)
for s.Scan() {
line := s.Text()
fmt.Fprint(os.Stdout, line+"\n")
}
}
Then, we modify our initial program to print the bytes traversing through the pipe so we see what's going on.
package main
import (
"fmt"
"io"
"os/exec"
)
func main() {
r, w := io.Pipe()
sr := &readSpy{r: r}
wr := &writeSpy{w: w}
cmd := exec.Command("./test-program")
cmd.Stdin = sr
cmd.Stdout = wr
cmd.Run()
}
type readSpy struct {
r io.Reader
}
func (s *readSpy) Read(d []byte) (int, error) {
size, err := s.r.Read(d)
fmt.Println("readSpy read", string(d[:size]))
return size, err
}
type writeSpy struct {
w io.Writer
}
func (s *writeSpy) Write(d []byte) (int, error) {
size, err := s.w.Write(d)
fmt.Println("writeSpy wrote", string(d[:size]))
return size, err
}
Running the above, you will see the following getting printed in a infinite loop, which makes sense since the priming the pump! string is printed to stdout and fed right back to the stdin of the test program:
writeSpy wrote priming the pump!
readSpy read priming the pump!
...repeated forever...

golang exec command: stream output to stdout *and* capture output in variable

I'm building a cli in go and have the following situation. I'm calling a bash command from go that prompts the user for a login, then prints a token after login. For example:
cmd := exec.Command("vault", "login", "-method=okta", "-format=json", "username=abc")
cmd.Stdin = os.Stdinout
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
_ = cmd.Run()
This streams the output nicely, but I have no way to parse the token from the command's output after the user logs in. I've tried to wrap the cmd.Run() into piping functions like this this and this but in both cases the output returned is empty. Any ideas?
Thanks!
There are probably lots of packages to do this, but it's not hard to whip up your own:
package main
import (
"fmt"
"os"
"os/exec"
)
type saveOutput struct {
savedOutput []byte
}
func (so *saveOutput) Write(p []byte) (n int, err error) {
so.savedOutput = append(so.savedOutput, p...)
return os.Stdout.Write(p)
}
func main() {
var so saveOutput
cmd := exec.Command("factor", "999999")
cmd.Stdin = os.Stdin
cmd.Stdout = &so
cmd.Stderr = os.Stderr
_ = cmd.Run()
fmt.Printf("I got this output: %s\n", so.savedOutput)
}
Playground: https://go.dev/play/p/T-o3QvGOm5q
Don't make your structure for nothing.
Use bytes.Buffer
package main
import (
"bytes"
"log"
)
func main() {
var buffer bytes.Buffer
cmd := exec.Command("vault", "login", "-method=okta", "-format=json", "username=abc")
cmd.Stdout = &buffer
_ = cmd.Run()
log.Printf("Vault login output: %s", buffer.String())
}

Executable file not found in %PATH% golang

package main
import (
"bytes"
"fmt"
//"log"
"os/exec"
)
func main() {
cmd := exec.Command("dir")
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
fmt.Printf("cmd.Run: %s failed: %s\n", err, err)
}
outStr, errStr := string(stdout.Bytes()), string(stderr.Bytes())
if len(errStr) > 1 {
fmt.Printf("out:\n%s\nerr:\n%s\n", outStr, errStr)
}
fmt.Printf(outStr)
}
*Hi guys, whenever I try to run this file with go it shows me This error "cmd.Run: exec: "dir": executable file not found in %PATH% failed:". I have golang in my PATH but it still failed *
dir is not an executable file in Windows, rather it is an internal command of Command prompt. You need to pass dir to command prompt.
Your command would look like this:
cmd.exe /c dir
You can implement it like this:
args := strings.Split("/c dir"," ")
cmd := exec.Command("cmd.exe",args...)
Pass your command line arguments like this, strings.Split() will split "/c dir" into all substrings separated by " " and returns a slice of the substrings between those separators.
Also if you need to print dir of a specific location you can set the working directory of the command:
cmd.Dir = filepath.Join("C:","Windows")
filepath.Join joins any number of path elements into a single path, separating them with an OS specific Separator.
Add the following packages into your file
import (
"os"
"path/filepath"
"strings"
)
To print the result you can connect your output and error to the standard output, and standard error.
cmd.Stdout = os.Stdout
cmd.Stderr = &os.Stderr
Your overall code would be:
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
)
func main() {
args := strings.Split("/c dir"," ")
cmd := exec.Command("cmd.exe",args...)
cmd.Dir = filepath.Join("C:","Windows")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
fmt.Printf("cmd.Run: %s failed: %s\n", err, err)
}
}

Embed environment variables in programmatic shell command execution

I am in a situation where I am trying to execute a shell command, but have its arguments be interpreted as environment variables properly.
For example, when I type the following into the terminal
ls $GOPATH
Bash interprets and expands the variable $GOPATH, and lists the contents of the $GOPATH directory. I am trying to do a similar thing with Golang's programmatic shell execution.
I have the following code.
package main
import (
"bytes"
"fmt"
"log"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("echo", "$TESTVAR")
cmd.Env = append(os.Environ(),
"TESTVAR=this_is_a_test",
)
var outBuff bytes.Buffer
var errBuff bytes.Buffer
cmd.Stdout = &outBuff
cmd.Stderr = &errBuff
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
fmt.Println(outBuff.String()) // empty
fmt.Println(errBuff.String()) // empty
}
This program outputs
$ go run test.go
$TESTVAR
Does anyone have any idea how to make the exec library interpret $TESTVAR as an environment variable as opposed to a string literal? Thanks in advance!
Replace
cmd := exec.Command("echo", "$TESTVAR")
with
cmd := exec.Command("sh", "-c", "echo $TESTVAR")
Bash and other shells interprets and expands variables, but the application is not executing Bash.
The application is executing the echo command. The echo command, like most other commands, does not expand environment variables in its arguments.
You can either run Bash as shown in another answer or expand environment variables on your own. Here's how to use os.Expand function to do this:
func newCommandEnv(env []string, cmd string, args ...string) *exec.Cmd {
m := map[string]string{}
for _, e := range env {
if i := strings.Index(e, "="); i >= 0 {
m[e[:i]] = e[i+1:]
}
}
fn := func(placeholder string) string {
return m[placeholder]
}
for i, a := range args {
args[i] = os.Expand(a, fn)
}
fmt.Println(args)
c := exec.Command(cmd, args...)
c.Env = env
return c
}

exec.Run - What's wrong with this Go program?

Isn't this Golang program supposed to output a directory listing to stdout?
It compiles ok, but does nothing.
package main
import "exec"
func main() {
argv := []string{"-la"}
envv := []string{}
exec.Run("ls", argv, envv, "", exec.DevNull, exec.PassThrough, exec.MergeWithStdout)
}
this works:
package main
import "exec"
func main() {
cmd, err := exec.Run("/bin/ls", []string{"/bin/ls", "-la"}, []string{}, "", exec.DevNull, exec.PassThrough, exec.PassThrough)
if (err != nil) {
return
}
cmd.Close()
}
You could also do it in native go using: ioutil.ReadDir(dir), like so:
//listdir.go
package main
import (
"os"
"io/ioutil"
"fmt"
)
func ListDir(dir string) ([]os.FileInfo, error) {
return ioutil.ReadDir(dir)
}
func main() {
dir := "./"
if len(os.Args) > 1 {
dir = os.Args[1]
}
fi, err := ListDir(dir)
if err != nil {
fmt.Println("Error", err)
}
for _, f := range fi {
d := "-"
if f.IsDir() { d = "d" }
fmt.Printf("%s %o %d %s %s\n", d, f.Mode() & 0777, f.Size(), f.ModTime().Format("Jan 2 15:04"), f.Name())
}
}
Checkout the documentation available for ioutil and os packages.
By default exec.Command will leave standard input, output and error connected to /dev/null. So, your 'ls' command is running fine but the output is just being thrown away. If you add:
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
before the exec.Run call then your output will go where you probably expect it.
exec.Run replaces your program with the one it executes -- it never returns to your app. This means that when 'cd' completes, it will exit as normal, and the only effect should be of changing the directory; 'ls' will never run.

Resources