The original Unix implementation simply did something like this:
ssh [batchname] < [inputfilename] > [outputfilename]
I need to be able to do the same thing from a windows machine running an application written in C#. I've tried using SharpSsh, which includes input and output streams, but they don't seem to work.
How can I pipe the input and output files/streams using SharpSsh (or any other .Net library)?
Figured it out. Either the application running or SSH itself expects a ctrl-d to signal the end of the input. At that point, the output stream can be read without hanging.
Related
I have successfully established a connection to a remote Linux server using the SSH.NET package with the following code (I am using a ShellStream because I have to use sudo su):
using (var client = new SshClient(server, username, password))
{
client.Connect();
List<string> commands = new List<string>();
commands.Add("sudo su - user");
commands.Add("vi test.properties");
ShellStream shellStream = client.CreateShellStream("xterm", 80, 24, 800, 600, 1024);
// Execute commands under root account
foreach (string command in commands) {
WriteStream(command, shellStream);
}
client.Disconnect();
}
private static void WriteStream(string cmd, ShellStream stream)
{
stream.WriteLine(cmd + "; echo this-is-the-end");
while (stream.Length == 0)
Thread.Sleep(500);
}
I am trying to edit the test.properties file that is in the remote Linux server using a C# function that I created.
My code (using C# in Visual Studio) that is used to modify the text file is uses System.IO.File.ReadAllText, but it does not recognize the path of the remote server, for example:
The text file in the Linux server is in this location: /home/user/test.properties, so I am using this in my code:
System.IO.File.ReadAllText("/home/user/test.properties")
I am getting the following error:
Could not find a part of the path 'C:\home\user\test.properties'
For some reason it tries to look in my local file system instead of the remote server.
Is there a different approach I should be taking?
Thanks in advance!
In general, to modify remote files, use SFTP. In SSH.NET that's what SftpClient is for.
Though as you seem to need to use elevated privileges (su) – an important factor that your question title fails to mention – it's way more difficult. The right solution is to avoid the need for su. See somewhat related:
Allowing automatic command execution as root on Linux using SSH.
Another option would be to try to execute the SFTP server under su. Though that would require modification of SSH.NET code. See related Java question:
Using JSch to SFTP when one must also switch user
If you want to keep your current shell approach with su, you are stuck with simulating shell commands. Note that connecting to SSH server won't make other .NET classes (like the File) magically be able to work with remote files (even if SFTP was possible, let only when it is not, due to the su requirement).
The easiest way to read remote file using shell is using the cat command:
cat /home/user/test.properties
Alright, so after finishing the task, this is what I did since asking the question here:
1.After your(#Martin Prikryl) response I've tried using a combination of SSH and WinSCP:
WinSCP to download the file.
.NET to modify the locally downloaded file.
WinSCP to upload the file(and deleting it from the local folder afterwards).
SSH to move the file to its appropriate location in the server.
I discarded this solution because it worked pretty well in the lower environment,
but in the production I had permissions issues so I couldn't even download it, let alone that it might be a security issue(its a sensitive file).
2.My next solution was using only SSH to simulate shell commands, as you previously mentioned, I was limited to that because I was stuck using sudo su.
I connected to the server with SSH and used the 'sed' command to only show lines that contain specific words(instead of using cat to get the whole file).
I then used my .NET code to pull the values that I needed for my GET operation
For the POST operation I used 'sed' again to replace lines.
I created a program using Renci SSH.NET library. Its sending all the commands and reading the result normally. However, when I send the command below:
client.RunCommand("cli");
The program hangs on this line indefinitely.
Any explanation of what is happening?
The cli is a command is used on Juniper switches/routers.
AFAIK, cli is a kind of a shell/interactive program. So I assume you have tried to do something like:
client.RunCommand("cli");
client.RunCommand("some cli subcommand");
That's wrong. cli will keep waiting for subcommands and never exit, until you explicitly close it with a respective command (like exit). And after it exits, the server will try to execute the cli subcommand as a separate top-level command, failing too.
You have to feed the "cli subcommand" to the input of the cli command. But SSH.NET unfortunately does not support providing an input with the SshClient.RunCommand/SshClient.CreateCommand interface. See Allow writing to SshCommand.
There are two solutions:
Use the appropriate syntax of the server's shell to generate the input on the server, like:
client.RunCommand("echo \"cli subcommand\" | cli");
Or use a shell session (what is otherwise a not recommended approach for automating a command execution).
Use SshClient.CreateShellStream or SshClient.CreateShell and send the commands to its input:
"cli\n" + "cli subcommand\n"
For a sample code see Providing subcommands to a command (sudo/su) executed with SSH.NET SshClient.CreateShellStream or C# send Ctrl+Y over SSH.NET.
I have a web service that uses SSH.NET to call a shell script on a Unix box.
If I run the script normally, it works fine, does its work correctly on the Informix DB.
Just some background:
I call a script that executes a .4gl (cant show this as its business knowledge).
The g4l is giving the following error back in a log, when I execute it with SSH.NET:
fglgo: error while loading shared libraries: libiffgisql.so: cannot open shared object file: No such file or directory
file_load ended: 2017-09-21 15:37:01
C# code to execute SSH.NET script
sshclients = new SshClient(p, 22, username, password);
sshclients.Connect();
sshclients.KeepAliveInterval = new TimeSpan(0, 0, 1);
sshclients.RunCommand("sh " + Script_dir);
I added the KeepAliveInterval, to see, if it helps.
My question is the error I am getting from Unix/4gl.
Why is this happening and who can I get the script to execute correctly?
The SshClient.RunCommand uses SSH "exec" channel internally. It, by default, (rightfully) does not allocate a pseudo terminal (PTY) for the session. As a consequence a different set of startup scripts is (might be) sourced. And/or different branches in the scripts are taken, based on absence/presence of the TERM environment variable. So the environment might differ from the interactive session, you use with your SSH client.
So, in your case, the PATH is probably set differently; and consequently the shared object cannot be found.
To verify that this is the root cause, disable the pseudo terminal allocation in your SSH client. For example in PuTTY, it's Connection > SSH > TTY > Don't allocate a pseudo terminal. Then, go to Connection > SSH > Remote command and enter your g4l command. Check Session > Close window on exit > Never and open the session. You should get the same "No such file or directory" error.
Ways to fix this, in preference order:
Fix the scripts not to rely on a specific environment.
Fix your startup scripts to set the PATH the same for both interactive and non-interactive sessions.
If the command itself relies on a specific environment setup and you cannot fix the startup scripts, you can change the environment in the command itself. Syntax for that depends on the remote system and/or the shell. In common *nix systems, this works:
sshclients.RunCommand("PATH=\"$PATH;/path/to/g4l\" && sh ...");
Another (not recommended) approach is to force the pseudo terminal allocation for the "exec" channel.
Though SSH.NET does not support this. You would have to modify its code issue SendPseudoTerminalRequest request in .RunCommand implementation (I didn't test this).
You can also try to use "shell" channel using .CreateShell method. For it, SSH.NET does support pseudo terminal allocation.
Though, using the pseudo terminal to automate a command execution can bring you nasty side effects. See for example Is there a simple way to get rid of junk values that come when you SSH using Python's Paramiko library and fetch output from CLI of a remote machine?
For a similar issues, see
Renci SSH.NET - no result string returned for opmnctl
Certain Unix commands fail with "... not found", when executed through Java using JSch
Commands executed using JSch behaves differently than in SSH terminal (bypasses confirm prompt message of "yes/"no")
JSch: Is there a way to expose user environment variables to "exec" channel?
Have seen similar questions asked by Informix-4gl developers as they transition to FourJs Genero and using its Web Services functionality. The question I'll put to them is "who owns the fglgo/fglrun process that the Genero Application Server has launched, where is it running from, and what is its environment". If needed, I'll illustrate with a simple program that does something like ...
MAIN
RUN "env > /tmp/myname.txt"
RUN "who >> /tmp/myname.txt"
RUN "pwd >> /tmp/myname.txt"
END MAIN
... and say compare with when program is running from command line. It is normally a case like in the earlier answer of configuring so that the environment is set correctly before the 4gl program is executed.
I'm trying to write some software in C# VS which is going to be used to connect through SSH to network devices ( Cisco Switches / Routers ) and download their configuration periodically. I've already tried SharpSSH, SSH.NET. The former is outdated and bugged, the latter just spams exceptions. What I did manage to do is use plink through the Process class to log into the device. Using redirected IO I even managed to print out the configuration. Unfortunately it doesn't print out in full. After a couple of lines a control line stops the output and waits for input
R1#show running-config
Building configuration...
Current configuration : 890 bytes
!
version 12.4
service timestamps debug datetime msec
service timestamps log datetime msec
no service password-encryption
!
hostname R1
!
boot-start-marker
boot-end-marker
!
enable secret 5 $1$Or0j$2.HDFSXaC9jh/KGxhyl6I1
!
no aaa new-model
memory-size iomem 5
ip cef
!
!
!
!
no ip domain lookup
--More--
Like so. It seems like it's a console issue since I turned the feature to output without waiting on on the device. Sending anything at that moment i just like sending a command to the device so I can't actually ask for MORE output ( or at least I haven't found a way ). This may be either the console output issue ( asking whether to output more ) or the StreamWriter going full. My question is: is it possible to turn waiting OFF in a console application? If no - what would you suggest I do? Any ideas are welcome.
kravvcu
Of course you can turn off " --More--". This command will help you (on routers and switches):
R1#terminal length 0
and this one (on Cisco ASA)
terminal pager 0
Use a lightweight TFTP library and use that instead of console downloads?
http://www.codeproject.com/Articles/19314/A-simple-TFTP-client-using-C
If you are set on the console version, just process start a plink connection and read your config off of the process i/o.
http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html
I've been trying very hard to automatically ssh into a Linux server. What's crazy, is that I can create a .bat script, that will do it, but I have to be there physically, to type in the password.
I've tried automating this using System.Diagnostics.Process object in c# to no end. There is no way, I've found, to make this object allow you to see the password prompt. It out puts every line up until that point, and then doesn't output any more lines.
cmd.exe, does really well at allowing you to jump from process to process in a script; It consolidates everything into one screen, and just prompts you for things such as passwords, and then you type them in.
Is there any simple way, in C#, to make cmd.exe think you are a human being typing in it, so I can simulated this programmatically? Otherwise, System.Diagnostics.Process, doesn't seem to offer a way to interacted with a process that outputs a password prompt. You never get to see the prompt that clearly happens when you run the same thing in cmd.exe
cmd.exe is not the SSH client; it's just a shell for one. Explore the capabilities of the actual client. In PuTTY (very popular!) there's an autologon feature, if you have an SSH key.
You could use
Windows.Forms.SendKeys()
to send password as keystrokes
Reference Material
Without knowing which commandline tool you are using to ssh to the server, isn't there a commandline parameter to provide a password.
C:\> ssh user#ip -p password
Or have you tried input redirection:
ssh user#ip <
Yourpassword
EOF
In looking into your earlier question, what is the trouble you are having in using the programmer interface?
Are you sure you need a full fledged ssh client, or do you just want to execute a command remotely. If it is the latter you might want to check out plink.
plink.exe -ssh user#host -2 -i user.ppk -m command_line.txt
How to create the ppk file: http://theillustratednetwork.mvps.org/Ssh/Private-publicKey.html