redirecting cmd.exe to httpresponse

2007-10-16 @ 00:52#

i had to solve an interesting problem today and it involved the ability to fire off command line processes from a web UI on IIS. since this was a 'triage' situation, i had a very short time to work out the details. i remembered that .NET exposes the Process and ProcessStartInfo objects that give you quite a bit of control over spawning command shells. with a little poking about, i was able to create a simple ASP.NET page that does the trick.

NOTE:
i made sure to place the ASPX page in a folder that requires a successful Windows auth to make sure only auth'ed users can run the script. it's an internal site, so there's reduced risk of hacking, but it's still a bit dicey to do this.

here's a simple example that will run a small batch file:

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.IO" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    void Submit(object sender, EventArgs args)
    {
        string fmt_html = "<pre>{0}</pre>";
        string batch_cmd = @"cmd.exe";
        string working_dir = @"z:\jobs\";
        string batch_job = @"z:\jobs\dirlist.bat";
        string output = "";

        // set up process to redirect std in/out
        ProcessStartInfo psi = new ProcessStartInfo(batch_cmd);
        psi.WorkingDirectory = working_dir;
        psi.RedirectStandardOutput = true;
        psi.RedirectStandardInput = true;
        psi.RedirectStandardError = true;
        psi.UseShellExecute = false;

        try
        {
            // execute the job
            using (StreamReader batch_rdr = File.OpenText(batch_job))
            {
                using (Process proc = Process.Start(psi))
                {
                    using (StreamReader std_out = proc.StandardOutput)
                    {
                        using (StreamWriter std_in = proc.StandardInput)
                        {
                            // direct batch file to std_in
                            while (!batch_rdr.EndOfStream)
                            {
                                std_in.WriteLine(batch_rdr.ReadLine());
                            }
                            std_in.WriteLine("EXIT");

                            // stream std_out to a string.
                            output = std_out.ReadToEnd().Trim();
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            output = ex.Message;
        }

        // Write out the results.
        results.Text = String.Format(fmt_html, output);
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>simple batch job</title>
</head>
<body>
    <form runat="server">
    <div><asp:Button Text="Run" OnClick="Submit" runat="server" /></div>
    <div><asp:Label ID="results" runat="server" /></div>
    </form>
</body>
</html>

the output looks something like this:

Microsoft Windows [Version 5.2.3790]
(C) Copyright 1985-2003 Microsoft Corp.

Z:\jobs\>dir c:\ /B
AUTOEXEC.BAT
CONFIG.SYS
Documents and Settings
Inetpub
Program Files
WINDOWS


Z:\jobs>EXIT

code