SFTP in Windows Service - c#

I need to execute the SFTP process through C# code. I am using Windows service. Below is the code I am using.
WebCash.writeToFile(WebCash.outerbatchpath, command);
WebCash.writeToFile(WebCash.outerbatchpath, "exit");
ProcessStartInfo psi = new ProcessStartInfo("cmd.exe", "/c "+WebCash.outerbatchpath);
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.WindowStyle = ProcessWindowStyle.Hidden;
Process p = new Process();
p.StartInfo = psi;
p.Start();
p.WaitForExit();
psi = null;
WebCash.writeToFile(WebCash.GetErrorLog(), DateTime.Now.ToString() + "- Running SFTP end - " + p.ExitCode);
return p.ExitCode;
While trying to debug the above code the debugger is not able to pass p.WaitforExit() function(i.e) service is not executing after p.WaitforExit().
I am running the below command(SFTP) in my outerbatch.
psftp -b D:\Source_Data\SFTP\innerbatch.bat -l username -i D:\privatekey.ppk #servername
Inner batch contains the below command
put D:\Source_Data\SFTP\FileToTransfer.xml
I need the exit code also to get the error. Pls help.

I assume the program you are running (the "outer batch") never finishes.
When you redirect a process output, you must be reading it. There's a limited buffer for the redirected output. When it fills, the child process stops on the next attempt to produce an output and waits until the parent process reads some data from the buffer. As you never do and you wait for the process to exit, deadlock occurs.
See the MSDN for the ProcessStartInfo.RedirectStandardOutput property :
Synchronous read operations introduce a dependency between the caller reading from the StandardOutput stream and the child process writing to that stream. These dependencies can cause deadlock conditions. When the caller reads from the redirected stream of a child process, it is dependent on the child. The caller waits for the read operation until the child writes to the stream or closes the stream. When the child process writes enough data to fill its redirected stream, it is dependent on the parent. The child process waits for the next write operation until the parent reads from the full stream or closes the stream. The deadlock condition results when the caller and child process wait for each other to complete an operation, and neither can continue. You can avoid deadlocks by evaluating dependencies between the caller and child process.
It's always recommended to use the p.StandardOutput.ReadToEnd() instead of the p.WaitForExit() in this case. Both commands wait for a process to exit, but the first ensures the deadlock does not occur.
On the other hand, as you are transferring only one file, I'm not sure the psftp output is actually large enough to fill the buffer. If the above suggestion does not help, I'd recommend you to simplify the code by directly running the psftp.exe, removing unnecessary layers of the cmd.exe and the outer batch file.
EDIT: If you never run psftp under a service account, it is possibly prompting for host key confirmation.
The server's host key is not cached in the registry. You
have no guarantee that the server is the computer you
think it is.
The server's dss key fingerprint is:
ssh-dss 1024 0b:77:8b:68:f4:45:b1:3c:87:ad:5c:be:3b:c5:72:78
If you trust this host, enter "y" to add the key to
PuTTY's cache and carry on connecting.
If you want to carry on connecting just once, without
adding the key to the cache, enter "n".
If you do not trust this host, press Return to abandon the
connection.
Store key in cache? (y/n)
You should use the -hostkey switch to explicitly specify a fingerprint of the trusted hostkey.
Alternatively use some native SFTP .NET library.
Side note: Naming the psftp script .bat is confusing. It's not a (Windows) batch file.

Using WinSCP.
For SshHostKeyFingerprint see Server and Protocol Information Dialog
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Sftp,
HostName = "000.00.00.000",
UserName = "xxxxxx",
Password = "xxxxxx",
PortNumber= 22,
SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxxxxxxxxxxx"
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Upload files
//TransferOptions transferOptions = new TransferOptions();
//transferOptions.TransferMode = TransferMode.Binary;
//TransferOperationResult transferResult;
var results = session.ListDirectory("/path");
foreach (RemoteFileInfo item in results.Files)
{
Console.WriteLine(item.Name);
}
// Throw on any error
//transferResult.Check();
// Print results
//foreach (TransferEventArgs transfer in transferResult.Transfers)
//{
// Console.WriteLine("Upload of {0} succeeded", transfer.FileName);
//}
}

Related

Detect Single Windows Process Exit Event C#

Okay so ive got another one, Right now im working on a button, called installbtn in code.
Right now i have in my code.
private void installbtn_Click(object sender, EventArgs e)
{
// Access the internal exe resource pcc - the pcc is Path Copy Copy, which i am using as a
// requirement to use this software
byte[] pccFile = Properties.Resources.pcc;
// The resource pcc.exe as a binary called pcc which is then used as a byte called pccFile
string pccExe = Path.Combine(Path.GetTempPath(), "pcc.exe");
// The Executable and its filename + Extenstion
using (FileStream exeFile = new FileStream(pccExe, FileMode.Create))
exeFile.Write(pccFile, 0, pccFile.Length);
// Write the file to users temp dir
Process.Start(pccExe);
// Start the Installer
installbtn.Text = "Installing...";
StatusLabel1.Text = "Installing PCC Now...";
// Indicate on the form, the current process status.
// Here i want the application to check if pccExe has closed
// after the user has installed the component and its process "pcc.exe" exits
MessageBox.Show("Module Installed /r/nPlease Start the Application", "Application Module Installed");
installbtn.Text = "Restart Now!";
StatusLabel1.Text = "Please Restart the Application";
// and if it has then show a message box and reflect in the form
// i want it to quit and restart the application after the message box is closed
}
now when the installer finishes, i would like to be able to detect when the installer closes ("pcc.exe") after install and then reload the application once it has. Not sure if its possible but i would appreciate the help.
Thanks Shaun.
You could use Process.WaitForExit()
The WaitForExit() overload is used to make the current thread wait
until the associated process terminates. This method instructs the
Process component to wait an infinite amount of time for the process
and event handlers to exit
Process process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
process.StartInfo = startInfo;
startInfo.FileName = exeToRun;
process.Start();
process.WaitForExit();
Start returns a Process object, on which you can wait (or start a thread that waits, etc.).

Getting output from Powershell script

I'm creating a MVC4 web application that will have the ability to create a console that logs the output from a Powershell script to a log window using SignalR. Currently the best I have gotten to work is Capturing Powershell output in C# after Pipeline.Invoke throws. Problem is that I am not able to pass a live stream of output to the client using this method. I am only able to feed the output once the script is finished being processed. Currently i am trying this but am not receiving any output.
var loggingHub = new LoggingHub();
string powerShellExeLocation = null;
var localKey = Registry.LocalMachine;
var subKey = localKey.OpenSubKey(#"SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine");
powerShellExeLocation = subKey.GetValue("ApplicationBase").ToString();
if (!Directory.Exists(powerShellExeLocation))
throw new Exception("Cannot locate the PowerShell dir.");
powerShellExeLocation = Path.Combine(powerShellExeLocation, "powershell.exe");
if (!File.Exists(powerShellExeLocation))
throw new Exception("Cannot locate the PowerShell executable.");
if (!File.Exists(scriptFile))
throw new Exception("Cannot locate the PowerShell script.");
var processInfo = new ProcessStartInfo
{
Verb = "runas",
UseShellExecute = false,
RedirectStandardOutput = true
};
processInfo.RedirectStandardOutput = true;
processInfo.WorkingDirectory = Environment.CurrentDirectory;
processInfo.FileName = powerShellExeLocation;
//processInfo.Arguments = "-NoLogo -OutputFormat Text -NonInteractive -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + scriptFile + "\" ";
processInfo.Arguments = build;
var powerShellProcess = new Process {StartInfo = processInfo};
powerShellProcess.Start();
while (!powerShellProcess.HasExited)
{
loggingHub.Send(DateTime.Now.ToString("h:mm:ss tt"), "info", powerShellProcess.StandardOutput.ReadLine());
}
One way to log the output as it is happening is to not only host the PowerShell engine but to also implement the host interfaces. You will then get called back for every "write" to the "host" as the PowerShell engine processes the script. Implement the host interfaces isn't trivial but it's not too hard either. Take a look at this MSDN link http://msdn.microsoft.com/en-us/windows/desktop/ee706584(v=vs.85)
From:
http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardoutput(v=vs.110).aspx
**When a Process writes text to its standard stream, that text is typically displayed on the console. By redirecting the StandardOutput stream, you can manipulate or suppress the output of a process. For example, you can filter the text, format it differently, or write the output to both the console and a designated log file.
Note
You must set UseShellExecute to false if you want to set RedirectStandardOutput to true. Otherwise, reading from the StandardOutput stream throws an exception.
The redirected StandardOutput stream can be read synchronously or asynchronously. Methods such as Read, ReadLine, and ReadToEnd perform synchronous read operations on the output stream of the process. These synchronous read operations do not complete until the associated Process writes to its StandardOutput stream, or closes the stream.
In contrast, BeginOutputReadLine starts asynchronous read operations on the StandardOutput stream. This method enables a designated event handler for the stream output and immediately returns to the caller, which can perform other work while the stream output is directed to the event handler.
Note
The application that is processing the asynchronous output should call the WaitForExit method to ensure that the output buffer has been flushed.
Synchronous read operations introduce a dependency between the caller reading from the StandardOutput stream and the child process writing to that stream. These dependencies can cause deadlock conditions. When the caller reads from the redirected stream of a child process, it is dependent on the child. The caller waits for the read operation until the child writes to the stream or closes the stream. When the child process writes enough data to fill its redirected stream, it is dependent on the parent. The child process waits for the next write operation until the parent reads from the full stream or closes the stream. The deadlock condition results when the caller and child process wait for each other to complete an operation, and neither can continue. You can avoid deadlocks by evaluating dependencies between the caller and child process.**
That seems to explain what's happening. Have you tried using the BeginOutputReadLine method instead of ReadLine?

Reading StdOut after process has been abnormally terminated using Process.kill

i am invoking an exe via c# Diagnostics.Process class and read output from it's StdOut. The process is forcefully terminated in case it doesn't automatically terminates in a specified time, something like:
process.StartInfo.FileName = #"D:\t.exe";
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = false;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = false;
process.WaitForExit(500);
if (!process.HasExited)
{
process.Kill();
process.WaitForExit();
}
string stdOutContents = process.StandardOutput.ReadToEnd();
Now the problem is the code works succesfully when the exe terminates normally. But in case it fails to terminate normally (usually the exe gets stuck in an infinite loop somewhere), stdOutContents is read as an empty string.
How can i read StdOut after the process is killed (without using process.OutputDataReceived event technique)? (It has been verified that the exe-in-question does always writes something onto StdOut even if it gets stuck somewhere).
Update 1
Details about Exe which is being invoked (refereed as 'native app' across this question)
It is a small utility implemented in c language and compiled using MS C++ compiler. It does its job while simultaneously outputting status information onto the StdOut (using putchar).
There are only two possible cases of operation:
It will run successfully while simultaneously printing some data onto the StdOut.
It will run normally to a certain point (simultaneously outputting data on StdOut) and then get stuck in an infinite loop. (This is an acceptable behavior).
Both scenarios have been verified using cmd.
Details about new attempts
i wrote a c# app (referred as dummy app) which mimics the native app behavior and this code works fine. However when run for the native app, i get nothing at all.
i don't understand why the code cant read the contents outputted by the native app?
i also tried using event handler for OutputDataReceived. It gets called only once with args.Data = null when the code tries to kill the process. Inspecting the behavior for dummy app revealed that when process.kill is called, the handler is invoked with args.Data = null. So this seems to be a standard behavior of sorts for both apps.
i also tried changing the newline characters for native app. Since it is implemented in c language, it uses \n for newline. i tried using both \r\n pair for newline but StdOut is still blank (for case 2).
I had the same interrogation and the doc of Process.Kill says
Data edited by the process or resources allocated to the process can be lost if you call Kill.
Which seems to indicate that you cannot rely on reading the StandardOutput of a process, although it is not clearly stated that the output / error streams are disposed.
I finally got inspired by this answer
How to spawn a process and capture its STDOUT in .NET?
and I use the following code :
var info = new ProcessStartInfo("some.exe");
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
using (var p = new Process())
{
p.StartInfo = info;
var output = new StringBuilder();
p.OutputDataReceived += (sender, eventArgs) =>
{
output.AppendLine(eventArgs.Data);
};
p.Start();
p.BeginOutputReadLine();
if (!p.WaitForExit(5000))
{
Console.WriteLine("Taking too long...");
p.Kill();
Console.WriteLine("Process killed, output :\n" + output);
}
}
Same pattern can be used with the ErrorDataReceived
Note that one could miss some unflushed output from the child process, however in my case I don't expect much from a process that requires to be killed, at most some information for debugging purposes.

How to NOT wait for exit using Impersonataion.RunProcess In c#/ASP.NET

What I want to know is if there is a way to NOT wait for a process when using impersonation. There's no overloads so I'm not sure how to do this.
// always waits for exit to continue
Impersonation.RunProcess(someProcessPath);
The reason I want to do this is that I have a long executing application that processes large files, but I want to deliver a success response to the client that the process has began before directing them to a status page. I cannot run the process with a normal process.Start() because the IUSER needs to impersonate an elevated user account.
I've even tried using process.StartInfo after Impersonation.Login() and supplying a un/pass, but I think the "logged in" status is part of the static Impersonation class only.
if (Impersonation.Logon(LoginCredentials.UserName, LoginCredentials.Password))
{
// this fails with an invalid privileges exit code
process.StartInfo.FileName = path;
process.StartInfo.UseShellExecute = false;
process.StartInfo.UserName = "Administrator";
SecureString secString = new SecureString();
foreach( char c in "mypassword" )
secString.AppendChar( c );
process.StartInfo.Password = secString;
process.StartInfo.Verb = "runas";
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.Start();
}
I've found a zillion posts on how to wait for a normally invoked process to end before continuing using Process.WaitForExit(). I've also successfully ignored the exit by using process.start() by itself inside console apps (already authenticated).
You could fork it of to a different thread:
// using ThreadPool for long running tasks is discouraged so create a new thread
var t = new Thread(() => Impersonation.RunProcess(someProcessPath)) { IsBackground = true };
t.Start();
You could set some flags inside the thread method to indicate when it was called and when it has finished.
Impersonation.RunProcess("/path/to/cmd.exe /C start "+someProcessPath);
This will return when cmd.exe ends, which is after starting someProcessPath

c# calling process "cannot find the file specified"

I'm a c# newbie so bear with me. I'm trying to call "pslist" from PsTools from a c# app, but I keep getting "The system cannot find the file specified". I thought I read somewhere on google that the exe should be in c:\windows\system32, so I tried that, still nothing. Even trying the full path to c:\windows\system32\PsList.exe is not working. I can open other things like notepad or regedit. Any ideas?
C:\WINDOWS\system32>dir C:\WINDOWS\SYSTEM32\PsList.exe
Volume in drive C has no label.
Volume Serial Number is ECC0-70AA
Directory of C:\WINDOWS\SYSTEM32
04/27/2010 11:04 AM 231,288 PsList.exe
1 File(s) 231,288 bytes
0 Dir(s) 8,425,492,480 bytes free
try
{
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
//This works
//p.StartInfo.FileName = #"C:\WINDOWS\regedit.EXE";
//This doesn't
p.StartInfo.FileName = #"C:\WINDOWS\system32\PsList.exe";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
p.WaitForExit();
// Read the output stream first and then wait.
s1 = p.StandardOutput.ReadToEnd();
p.WaitForExit();
}
catch (Exception ex)
{
Console.WriteLine("Exception Occurred :{0},{1}",
ex.Message, ex.StackTrace.ToString());
Console.ReadLine();
}
You shouldn't do this in the first place unless you really need to.
Instead, use the Process class.
You can get all of the currently executing processes by calling Process.GetProcesses
To get the memory use of a single process, check the WorkingSet64 property of the Process object.
To get the CPU usage of a single process, use Performance Counters.

Categories

Resources