I am trying to figure out how to run a bash command from C# running on IIS 7/.Net 4.5.
I've been searching the web and a lot of answers presume you have certain things installed/in place.
I already have Git 1.9.4.msysgit.2 installed with Git Bash and Git Giu. I'm looking for some help as to what else I need installed to run even the simplest of bash commands. And how to run it.
I've looked at posts like bash pipes - I am trying to call script from c# but that uses cygwin. Can I do the same without it and if so, how do I go about it?
Goal
If what I'm asking above doesn't make sense or seems to ask separate questions, here my ultimate goal. I'm trying to write my own server-side git hook. When a developer pushes their commits to our GitHub repo, I want GitHub to call our callback url. I want my callback url to run a git pull command to update our staging server with what was just pushed.
I got to this question based on a previous question I asked at GitHub - setup auto deployment with remote server. based on answers there I'm trying to run a simple command, either but hard coding the command, or putting it in a script and running it, e.g.: cd $REPO_DIR && git pull origin $branch_name.
I am aware of Jenkins and other software, but I want to perform these commands myself vs. installing another software.
If further information is needed please feel free to ask.
Update 1
So based on a few answers below I've come up with the following
using System.Diagnostics;
Process process = new Process();
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
processStartInfo.FileName = #"C:\Program Files (x86)\Git\bin\bash.exe";
processStartInfo.WorkingDirectory = #"C:\myrepo\mysite";
processStartInfo.Arguments = "git status";
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
processStartInfo.UseShellExecute = false;
process.StartInfo = processStartInfo;
process.Start();
String error = process.StandardError.ReadToEnd();
String output = process.StandardOutput.ReadToEnd();
ViewBag.Error = error;
ViewBag.Ouput = output;
With the code above I am getting "C:/Program Files (x86)/Git/bin/bash.exe": git: No such file or directory. I know the exe is there. What's am I doing wrong?
Update 2
As per #SurgeonofDeath comment I followed this post http://blog.countableset.ch/2012/06/07/adding-git-to-windows-7-path/ and added the paths of Git to my environmental variables. However I still am getting the same issues. Any ideas?
Thanks.
Instead of calling the bash.exe, simply call git and pass the status as argument:
processStartInfo.FileName = "git";
processStartInfo.Arguments = "status";
perhaps i misunderstood your question but what about execve?
here is an excerpt of it's man page.
NAME
execve - execute program
SYNOPSIS
#include <unistd.h>
int execve(const char *filename, char *const argv[],
char *const envp[]);
DESCRIPTION
execve() executes the program pointed to by filename. filename must > be
either a binary executable, or a script starting with a line of > the
form:
#! interpreter [optional-arg]
Check your PATH environment variable and update it
C:/Program Files (x86)/Git/bin/bash.exe": git: No such file or directory
means that it's git which is not found by bash.
1. Check the PATH environment variable in bash (which is and should remain different from Windows one)
Adjust this
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
to make the terminal visible.
In the terminal you will create with the Process.Start()
Type:
echo ${PATH}
2. Update your path
You could update the global path of windows (which requires a restart)
You could update the user path of windows (which should require a logoff, but I'm not sure).
You just set Path to what you like with System.Environment.SetEnvironmentVariable before starting the Process
Additional note:
If you have like me several versions of bash, command interpreters, git and so on, it could be really messy if you try to concatenate all the paths and hope to find the ideal order. You could see some weird behavior of you beloved commands until you realize it's not the one you intend to run ... think of FIND.exe... And I didn't even think of the user-friendly interface of windows to edit environment variables ...
Related
I have to start a VPN connection (Fortinet) by code.
I have a cmd file that establish the connection.
If I call the cmd file on the shell it works pretty fine.
When I call it via Process.Start it does nothing.
It doesn't throw any exception, it seems to execute but VPN does not connect.
On the standard output I can read the echo I put on the cmd file (so it is executing the right file).
I launched a ping -d to see when the vpn goes up, when I call it via shell it goes up in a few seconds, via C# it is not.
I also tried a sleep(30000) but nothing.
My cmd (ConnectFile.cmd):
#echo off
#echo Connecting to VPN
"C:\Program Files (x86)\Fortinet\SslvpnClient\FortiSSLVPNclient.exe" connect -s "vpn myvpn"
My code (connectFile and disconnectFile are strings that contain the full path of the cmd files):
try
{
var startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = connectFile;
startInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(connectFile) ?? "";
System.Diagnostics.Process process = System.Diagnostics.Process.Start(startInfo);
System.Threading.Thread.Sleep(30000);
base.GetFiles(folder);
}
finally
{
System.Diagnostics.Process.Start(disconnectFile);
}
You have to separate the parameters. FileName is for the exe file, not the whole shell command (you're explicitly saying not to use the shell). So put the parameters to the Arguments property in ProcessStartInfo.
In your case, the parameters should be:
FileName - C:\Program Files (x86)\Fortinet\SslvpnClient\FortiSSLVPNclient.exe (no quotes)
Arguments - connect -s "vpn myvpn" (again, no quoting)
Second, you have to read the standard output if you capture it. If the output buffer gets full before you read it, the called process will stop working - it has to wait for the buffer to be emptied. If you're sure the application will actually finish at some point, simply remove the Thread.Sleep and call ReadToEnd right away. Otherwise, use eg. asynchronous reading to get the data.
Also, it's usually a good idea to set WorkingDirectory. Even if the application doesn't need any data from the working directory, it's safer, since only admins can change Program Files - this helps against DLL inject hacks.
I'm running a batch from my ASP.NET c# page. I'm trying to use the System.Diagnostics.ProcessStartInfo method described in the link below. When I click my button to run the batch, the page just hangs with "Waiting for ".
The original code I'm using
Here are some similar problems with solutions:
Possible solution 1
Possible solution 2
Unfortunately, I don't understand solution 1 at all and solution 2 does seem to indicate there is a problem with the paths I'm trying to use, but I'm a little unclear on this as well. Any help is greatly appreciated.
My code:
protected void RunPkg_Click(object sender, EventArgs e)
{
// Get the full file path
string strFilePath = "C:\\inetpub\\wwwroot\\DecisionSupport\\CMSBenPerfUpload\\RunPackage.bat";
// Create the ProcessInfo object
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo("cmd.exe");
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardError = true;
psi.WorkingDirectory = "C:\\inetpub\\wwwroot\\DecisionSupport\\CMSBenPerfUpload\\";
// Start the process
System.Diagnostics.Process proc = System.Diagnostics.Process.Start(psi);
// Open the batch file for reading
System.IO.StreamReader strm = System.IO.File.OpenText(strFilePath);
// Attach the output for reading
System.IO.StreamReader sOut = proc.StandardOutput;
// Attach the in for writing
System.IO.StreamWriter sIn = proc.StandardInput;
// Write each line of the batch file to standard input
while(strm.Peek() != -1)
{
sIn.WriteLine(strm.ReadLine());
}
strm.Close();
// Exit CMD.EXE
string stEchoFmt = "# {0} run successfully. Exiting";
sIn.WriteLine(String.Format(stEchoFmt, strFilePath));
sIn.WriteLine("EXIT");
// Close the process
proc.Close();
// Read the sOut to a string.
string results = sOut.ReadToEnd().Trim();
// Close the io Streams;
sIn.Close();
sOut.Close();
// Write out the results.
string fmtStdOut = "<font face=courier size=0>{0}</font>";
this.Response.Write(String.Format(fmtStdOut,results.Replace(System.Environment.NewLine, "<br>")));
UPDATE: I changed the paths to C:\Temp\ just to see if it made a difference, but it didn't. I opened my security wide open on the dirs I'm using, no go.
UPDATE 2: Running this on my dev box is the same, but I actually get feedback shown in the block below. If I manually perform this in cmd, it executes no problem. If I manually run the batch itself, there's no problem.
> Microsoft Windows [Version 6.1.7601]
>Copyright (c) 2009 Microsoft Corporation. All rights reserved.
>
>C:\inetpub\wwwroot\DecisionSupport\CMSBenPerfUpload>dtexec /f "ACO-SHS-PatDB.dtsx"
>Microsoft (R) SQL Server Execute Package Utility
>Version 10.50.1600.1 for 32-bit
>Copyright (C) Microsoft Corporation 2010. All rights reserved.
>
>Option "#" is not valid.
>
>C:\inetpub\wwwroot\DecisionSupport\CMSBenPerfUpload>
Your process identity doesn't have right to the directory.
Another queston where do you plan to publish this. A sane administrator would prvent any website to run executables or batch file on the server
I'm going to put an answer in here because I hate leaving things unresolved. I never could get this to work exactly right in this form. I had a network admin check permissions just to make sure I wasn't overlooking something, and they couldn't find anything that made a difference either. All permissions seemed to be properly granted.
Somewhere between the version differences in my dev PC and the server for all parts, Windows, SQL Server, Visual Studio, somewhere in there the problem kept itself well-hidden. Kudos to Microsoft for making products that fail to work properly from one machine to the next without wasting far too many hours hunting for a resolution.
Either way, I ended up going a different route. I built very simple SSIS packages on my SQL 2012 server that could be executed from Stored Procedures built on the server. This kept permission and version issues at bay. I then used C# scripting to write out my XML results files to the directories needed. The one issue to overcome was that C# executescalar will not handle XML larger than 2033 characters so I had to use ExecuteXmlReader and loop through the results to built my string before writing it to file.
This problem, though ending differently than it began, is resolved.
Part of a project I'm working on (Windows, C#, MVC4 WebAPI) requires some integration with git. None of the existing C# git libraries supported remote cloning, so I wound up porting the parts of the JavaGit project we needed (checkout, fetch, status), and writing clone myself. All it really is is a wrapper to the git command line executable. The relevant code it calls is here:
public static void RunGitCommand(string repositoryPath, string gitArguments)
{
// GitCommand is the full path to git.exe (currently using Github for Windows)
if (null == GitCommand || String.IsNullOrWhiteSpace(GitCommand))
{
throw new FileNotFoundException("Unable to find git.exe on your system PATH.");
}
// gitArguments contains the command to run (e.g. "clone -- git#repo:projectName c:\repositories\repo_a8c0dd321f")
var startInfo = new ProcessStartInfo(GitCommand, gitArguments)
{
WorkingDirectory = (null != repositoryPath && Directory.Exists(repositoryPath)) ? repositoryPath : String.Empty,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardInput = true,
RedirectStandardError = true
};
using (var p = new Process
{
EnableRaisingEvents = true,
StartInfo = startInfo
})
{
p.OutputDataReceived += (sender, args) => Log.Debug(args.Data);
p.ErrorDataReceived += (sender, args) => Log.Debug(args.Data);
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
p.WaitForExit();
}
}
Which would be called by the code as:
// names changed to protect the innocent
string localRepo = #"c:\repositories\repo_a8c0dd321f";
string gitArgs = "clone -- git#repo:projectName c:\repositories\repo_a8c0dd321f";
GitConfiguration.RunGitCommand(localRepo, gitArgs);
From within the MVC API, we use impersonation to make sure it's running as a user with a valid git login and key (no passphrase). The command above works perfectly from a command line as myself, and in an quick unit test, as well (I know it's really an integration test).
However, when it's actually called from the API as shown above, it hangs. Looking in Task Manager shows git.exe running, with the command line showing the full path to git.exe followed by the arguments above. It's not using any processor time, and only 2604K of RAM, but it claims to be running. Likewise, there is an ssh.exe process running, also with no processor usage, and 1212K of RAM, with the command line:
ssh git#repo "git-upload-pack 'projectName'"
Both processes are listed as running under my username, so it appears impersonation is working correctly.
Looking in the localRepo directory, it creates the .git directory, then hangs, leaving around 13K worth of git files in there, but none of our code. Thinking it was due to our repo being huge, I let it run overnight. Still no movement as of this morning.
Brought up LINQPad, ran:
Process.GetProcessById($gitPID).Dump()
Did the same for the SSH process as well. The threads showed them as being in the Wait state, and the WaitReason was Executive (waiting for thread scheduler). I initially assumed it was waiting for a passphrase, as my key had one. I switched to a working key without a passphrase, same result.
Git/SSH versions (from latest GitHub for Windows):
git version
git version 1.7.11.msysgit.1
ssh -v
OpenSSH_4.6p1, OpenSSL 0.9.8e 23 Feb 2007
The only idea I have left is that maybe it can't communicate with ssh-agent, which is running. It is impersonating me properly, though, so I don't know why it wouldn't work from the WebApi framework, but works fine from the "unit" test and Git Shell. I tried making sure the HOME, PLINK_PROTOCOL, TERM, SSH_AUTH_SOCK, and SSH_AGENT_PID environment variables were set, after looking through the Github for Windows setup scripts, just to make sure I wasn't missing anything.
I'm at a total loss. Here's a snippet of the log file, with some comments afterward:
2012-11-20 13:42:59.5898 Info Initializing repo at path: c:\repositories\repo_a8c0dd321f
2012-11-20 13:42:59.5898 Debug Working Directory: c:\repositories\repo_a8c0dd321f
2012-11-20 13:42:59.6053 Debug C:\Users\christian.doggett\AppData\Local\GitHub\PortableGit_8810fd5c2c79c73adcc73fd0825f3b32fdb816e7\bin\git.exe status --branch
2012-11-20 13:42:59.6209 Debug HOME=H:\
2012-11-20 13:42:59.6209 Debug PLINK_PROTOCOL=ssh
2012-11-20 13:42:59.6365 Debug TERM=msys
2012-11-20 13:42:59.6365 Debug SSH_AGENT_PID=58416
2012-11-20 13:42:59.6365 Debug SSH_AUTH_SOCK=/tmp/ssh-IgTHj19056/agent.19056
2012-11-20 13:42:59.6521 Info git status --branch
Exit code: 128
2012-11-20 13:42:59.6521 Error
2012-11-20 13:42:59.6677 Info Cloning repo from origin: git#repo:projectName
2012-11-20 13:43:01.8674 Debug Cloning into 'c:\repositories\repo_a8c0dd321f'...
2012-11-20 13:43:03.2090 Debug Could not create directory 'h/.ssh'.
2012-11-20 13:43:03.2870 Debug Warning: Permanently added 'repo,359.33.9.234' (RSA) to the list of known hosts.
2012-11-20 13:44:41.4593 Debug fatal: The remote end hung up unexpectedly
I always get the "Could not create directory 'h/.ssh'" and "Warning: Permanently added * to the list of known hosts." messages, even on the command line. My H:.ssh\known_hosts remains empty, but my keys are in that directory, and git finds those just fine. The "remote end hung up unexpectedly" error was when I killed the git and ssh processes.
I may wind up switching to LibGit2Sharp for most of my needs, but that still doesn't solve my clone problem. Is something screwed up with my key setup, which again, works perfectly outside of the w3wp.exe process? Does it need to be able to communicate with ssh-agent.exe, and is not able to? Has anyone cloned a remote git repository via System.Diagnostics.Process and lived to tell the tale?
UPDATE (11/25/2012 6:54PM):
mvp was correct in pointing out that impersonation and mapped network drives don't play well together. I added the following before starting the process:
var userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
Environment.SetEnvironmentVariable("HOME", userProfile);
I ran it again, and am now at least seeing a new git process in Task Manager that's taking 0 processor time, but is using an increasing amount of memory, I believe as part of the clone process. The command line was:
git index-pack --stdin --fix-thin "--keep=fetch-pack 6024 on MACHINENAME"
It just finally finished after 10 minutes (It's a huge repository), and threw the exception:
fatal: git checkout: updating paths is incompatible with switching branches.
Did you intend to checkout 'origin/8b243b8d9a5140673fc552ef7da8f0dfe9039d50' which can not be resolved as commit?
Looks like the clone worked after changing into the directory, though! The other problem is something to do with immediately calling checkout after the clone operation finishes, but is unrelated to the hanging problem.
I just need to verify my/mvp's solution on a production server, then I'll award the bounty.
I believe your main problem is that home directory for impersonated account is not what you think it is, mostly because network mapped drives for impersonated accounts don't really work.
As a workaround, you should set HOME environment variable for impersonated user to point to some local directory (say on drive C:) which should contain your ssh keys (without passphrases). You should test this by running git clone manually (while having fake HOME in effect) and accept known_host keys, so it would not prevent background git command from working automatically.
Home being h:\ is a little broad, but other than that, I would say your next step is to create a .ssh directory (h:.ssh) with the correct permissions, should be read only for the web user and no access for any other user.
In my current project, i need to call a Unix shell script from the C# application. I also need to get the response back whether the script has been execute successfully or any error has occurred.
The C# program is running on a Windows machine. I need to connect to a Unix machine and execute the script.
Can anyone let me know how this can be done using C#?
Will this solve your problem?
sharpSsh - A Secure Shell (SSH) library for .NET
Update
Refer to the developer's site for SharpSSH for more information on how to use the tool.
Update 2
change link of developer site to archived link.
A straight forward way of preforming this using System.Diagnostics.Process
// Start the child process.
Process p = new Process();
// Redirect the error stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.FileName = "Write500Lines.exe";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected error stream.
// p.WaitForExit();
// Read the error stream first and then wait.
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
Even i had the same problem, i have googled for solution for around 1 month.
Finally, i have decided to use plink.exe (command line version of putty.exe) to connect to unix box and execute a script there.
You have to use plink through c# process, i have tried it and this works amazingly.
But rite now the problem i am facing is when i am running a script from c# process i am unable to pass arguments to that script. Probably it would be rite to say that i do not know how to do that.
Regards
-Aakash
In my local network ,I have more than 10 pc.I need to take care all of the pc.I want to know all pc’s hardware informations.I also want to control those pc,Suppose ,at this moment I want to restart one of my client pc.Is it possible in C#.if have any question plz ask.Thanks in advance
I use bellow syntax to execute command.
try
{
// create the ProcessStartInfo using "cmd" as the program to be run,
// and "/c " as the parameters.
// Incidentally, /c tells cmd that we want it to execute the command that follows,
// and then exit.
System.Diagnostics.ProcessStartInfo procStartInfo =
new System.Diagnostics.ProcessStartInfo("cmd", "/c " + "shutdown /r /m \\172.16.1.3 /t 1 /");
// The following commands are needed to redirect the standard output.
// This means that it will be redirected to the Process.StandardOutput StreamReader.
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
// Do not create the black window.
procStartInfo.CreateNoWindow = true;
// Now we create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
string result = proc.StandardOutput.ReadToEnd();
// Display the command output.
Console.WriteLine(result);
}
catch (Exception objException)
{
// Log the exception
}
Using the above code I get the message "The network path was not found."
Pls check the url.
http://support.microsoft.com/kb/317371
If you want to make a program which u can able to get the remote system information. You have to use Microsoft's Remoting.Here we can able to create an object in the remote system and we can able to control it.
It is possible to get System's information by executing the System.Diagnostics.ProcessStartInfo.
It is possible to get system information using "systeminfo" .It is possible to take the output using C#
Pls chk the this.
I hope this will be useful for you.
I don't think this is a C# question, cause this can be done much more elegant with things like Group Policy Editor, System Management Server, System Center Operations Manager, etc.
To do some simple tasks on a remote machine you can take a look into the PsTools.
With those requirements my first stop would be WMI. There's for example the Win32_OperatingSystem class with its Reboot and Shutdown methods and the Win32_Processor with all kinds of information about the CPU.
This MSDN section shows you how to use it from .Net: Getting Started Accessing WMI Data
This MSDN section has quite a lot of short VBScript samples for doing various things using WMI, and even if the code is different, at least you can see which WMI classes/methods/properties you should be looking at: WMI Tasks for Scripts and Applications
Please note RB's comment though, you'll need to have the correct permissions for it to work.
Edit: Forgot that since you'll want to connect to other computers, this sample will be useful: How To: Connect to a Remote Computer