Go Web Application

HI,

I am still a beginner at golang, and I was looking at trying to create a webserver that would display a webpage on localhost:8080, and would run terminal commands on the click of a button, and output the results to some sort of output box on the webpage. I can execute terminal commands fine using go, and I have my webserver running such that when I click on a button, the terminal command is executed and the results are outputted in my terminal window. But I’m stuck looking for ways to post the output of my terminal command to my webpage. I have tried looking into the textarea tag in html, but I am not able to post to this. Also, I need a way of creating some sort of progress bar in my webpage, something that loads while the command is being executed, so the page shows something happening instead of waiting until the command is finished. I have not yet come up with a solution to this. And one more question, is golang compatible with rendering webpages using twitter bootstrap. I’m looking for something to make my webpage “better looking”.

Any suggestions would be very helpful.

Thanks,

Chris

Hey @Chris_S,

My suggestion to do with rendering the command’s output to your webpage would be to use an ajax request when a button is clicked and then using the response of the ajax request which is the result of the executed command to render the result on the page using javascript.

@Chris_S,

Here’s a quick example for my previous comment (go to http://localhost:9000 to test):

main.go:

package main

import (
	"html/template"
	"log"
	"net/http"
	"os/exec"
)

var tmpl = template.Must(template.New("tmpl").ParseFiles("index.html"))

func execCommandHandler(w http.ResponseWriter, r *http.Request) {
	// Execute an ls command and store the command's output.
	output, err := exec.Command("ls").Output()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Write(output) // Write the command's output to the response.
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	if err := tmpl.ExecuteTemplate(w, "index.html", nil); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func main() {
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/exec", execCommandHandler)
	log.Fatal(http.ListenAndServe(":9000", nil))
}

index.html:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Execute Command</title>
		<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
		<script type="text/javascript">
			$(document).ready(function() {
				$("#exec").on("click", function() {
					$.ajax({
						url: "/exec",
						method: "GET",
						success: function(data) {
							$("#response").html(data);
						},
					});
				});
			});
		</script>
	</head>
	<body>
		<button id="exec">Execute Command</button>
		<div id="response"></div>
	</body>
</html>
2 Likes

Thanks for the suggestion, this is exactly what I needed. Also any thoughts for a real-time progress bar in my webpage? As mentioned above, I need one that would be able to monitor the length of time the terminal command is taking to obtain an output. I’m not too familiar with javascript, but I’m thinking I can use ajax somehow to do the same? Again, any suggestions would be appreciated.

Regards,

Chris

Hey @Chris_S,

If you are simply trying to show a timer until output is shown, I think the easiest solution would be to use javascript to start a timer on the page once the initial ajax request is made and then once the response is received, stop the timer in the ajax success callback code area.

This wouldn’t actually be an accurate duration with how long the process took to execute since there is latency between sending the ajax request and then receiving the results, but unless you meant something else, I would think that it’d be fine as a display to let the person know that something is actually running.

This would be a great solution but I am trying to find a way to show the progress time of the terminal command execution.

So right now, I found a way of gathering the execution time of the terminal command I’m trying to use.
When I click the html button, it executes the following terminal command:

sudo nmap -F -sS X.X.X.0/24 --stats-every 2s | grep --line-buffered ‘Ping Scan Timing’ | awk ‘{print $5}’

This outputs the execution time of the command every 2s to my os.stdout terminal. The output looks something like this:
10%
15%
30%
.
.
.

But now I have modified my code because I am trying to redirect this output to my webpage, and render it in realtime, so that it I can see how much progress is left for the execution to be completed since this command can take a while to load.

The problem I am running into is that I cannot properly use the stdoutpipe function to redirect the output to my webpage. Here is a snippet of my code for the handler that gets triggered when I click the button in my webpage:

func viewHandler(w http.ResponseWriter, r *http.Request) {

    conn, ok := w.(http.Flusher)

    if !ok {
            http.Error(w, "Oops", http.StatusInternalServerError)
            return
    }

    w.Header().Set("Content-Type", "text/event-stream")
    w.WriteHeader(http.StatusOK)


    nmap_chain := "nmap -F -sS X.X.X.0/24 -oX output.xml --stats-every 2s | grep --line-buffered 'Ping Scan Timing' | awk '{print $5}'"
    cmd := exec.Command("bash","-c",nmap_chain)
    stdout, err := cmd.StdoutPipe()
    if err != nil {
            log.Fatal(err)
    }
    if err := cmd.Start(); err != nil {
            log.Fatal(err)
    }

    in := bufio.NewScanner(stdout)

    for in.Scan() {
            fmt.Fprintf(w,"%d\n\n",in) 
            conn.Flush()
            time.Sleep(time.Second)
    }

    if err := in.Err(); err != nil {
            log.Printf("error: %s", err)
    }

}

My html file looks like the following as well:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>NMAP Demo</title>
    <link rel="stylesheet" type="text/css" href="layout/mystyle.css">
    <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
    <script type="text/javascript">
	$(document).ready(function() {
	    $("#exec").on("click", function() {
		$('#exec').prop('disabled', true);
                $.ajax({
		    url: "/exec",
		    method: "GET",
		    success: function(data) {
		    $("#response").html(data);
		    $('#exec').prop('disabled', false);
		    },
	        });
	    });
	});
    </script>
</head>
<body>
    <div id="headerBar"></div>
    <div>
        <button id="exec">Execute Command</button>
    </div>
    <div id="results">
        <textarea readonly id="response" rows="50" cols="50" style="resize"></textarea>
    </div>
</body>
</html>

Right now I cannot see any output in my html textarea when this handler is executed. I’ve also tried creating a basic function that would execute the nmap command mentioned, and redirect the output to a log instead of os.stdout using stdoutpipe. But still I cannot see any output.

Any suggestions would be helpful.

Thanks,

Hey again @Chris_S,

I haven’t fully read through everything you wrote above, but I quickly noticed what is probably causing your error.

You are not correctly piping your commands so you would not be receiving the correct output even if everything else was correct.

For example, these would both return an error:

exec.Command("ls", "-la", "| grep .go").Output()
exec.Command("ls", "-la", "grep .go").Output()

To pipe commands correctly, you would want to use something similar to this rather than trying to pass multiple commands as an argument to your original bash -c command, since passing other commands as arguments to a command doesn’t work as a pipe:

package main

import (
	"log"
	"os"
	"os/exec"
)

func main() {
	var err error

	// Create an ls command.
	ls := exec.Command("ls", "-la")

	// Create a grep command that searches for anything
	// that contains .go in it's filename.
	grep := exec.Command("grep", "\\.go")

	// Set grep's stdin to the output of the ls command.
	grep.Stdin, err = ls.StdoutPipe()
	if err != nil {
		log.Fatalln(err)
	}

	// Set grep's stdout to os.Stdout
	grep.Stdout = os.Stdout

	// Start the grep command first. (The order will be last command first)
	if err := grep.Start(); err != nil {
		log.Fatalln(err)
	}

	// Run the ls command. (Run calls start and also calls wait)
	if err := ls.Run(); err != nil {
		log.Fatalln(err)
	}

	// Wait for the grep command to finish.
	if err := grep.Wait(); err != nil {
		log.Fatalln(err)
	}
}

Edit: After re-reading it only hit me that you were actually using bash -c so the command would work fine so ignore my post :stuck_out_tongue:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.