C# SSH Create.Command Newline Syntax - c#

I'm definitely new to C# but learning it to pickup where another left things at work so be gentle if I ask silly questions (I think this is an easy one). I'm using SSH.NET and the current code isn't wrong but the way the previous guy wrote it can leave open ssh connections on the other side.
Current working code opens the SSH session as normal but uses something like this to run commands on the remote system:
Stream.Writeline("part of the command")
Stream.Readline();
Stream.Writeline("second part of command")
Stream.Readline();
Stream.Writeline("last part of command")
Stream.Readline();
This works and gathers the data exactly as it should but I've seen repeated times where the SSH connection stays open afterwards on the remote system. What I would like to do is send this all within a single command via create.command like:
command1 = client.CreateCommand("full command to run")
result = command1.Execute()
My problem is that the command I'd like to run on the remote system requires returns to run, it's a database command:
mysql -u user <<EOF
select COUNT(*) from table1
go
EOF
What I'd like to do:
command1 = client.CreateCommand("mysql -u user <<EOF\nselect COUNT(*) from table1\ngo\nEOF\n")
How/can one insert returns into the Create.Command? I'm assuming so, see no reason why not but I'm having a hard time finding the correct syntax to send a database command that requires returns after each line like above via the Create.Command. I've tried using \n for newlines but 99% sure I'm using it incorrectly.
And just to be clear I used mysql as the DB example, it's actually a sybase database but uses that syntax to connect.

Why do you need the breaks in the first place? They're just to nicely format the query for humans. MySQL isn't human. It couldn't care less. why not just do it this?
mysql -u user -e 'select count(*) from table1'
you don't need a go command, period.
If you DO want to send multiple commands, then you still don't need any line breaks, just a ;:
mysql -u user -e 'command1; command2; command3; ....'

USE \r\n like in here:
command1 = client.CreateCommand("mysql -u user <<EOF\r\nselect COUNT(*) from table1\r\ngo\r\nEOF\n")

Related

SSH.NET Expect() Unexpected Prompts

I've written my own interface into Linux with my own translation of commands and Ansible extensions. I'm using Visual Studio 2019, connecting to CentOS, running the latest Ansible, while connecting using SSH.NET.
Because this is 1000's of lines of code, this is a very high level sudo code, I'm doing this:
const string Command_Prompt = #"[\?$#]|\[.*#(.*?)\][$%#]";
var promptRegex = new Regex(Command_Prompt);
while(_lastCommand != "exit") {
_lastCommand = Console.ReadLine();
_shellStream.WriteLine(_lastCommand);
var output = _shellStream.Expect(promptRegex);
Log.Verbose(output);
}
This picks up
[root#localhost ~]#
[root#localhost ~]$
#
$
What is your first name?
My Ansible ymls have promtps and this works perfectly if the prompt has a question mark. If I'm expecting something specific it works fine as I can pass into expect anything I like. However, there are times, I run a linux command and the command has a prompt that is unexpected, this is waiting for the expected information to come back. Is there no way to do what MRemote, Putty, or VSCode terminal does with unknown prompts? I can't really allow others to use my product if I don't know what their prompts are going to look like. What does MRemote, Putty and VSCode terminal do specifically that SSH.NET clearly doesn't do or am I'm not using SSH.NET correctly?
I ended up, just go with the basics.
.*\?:|[$#]|\[.*#(.*?)\][$%#]
Picks up the following
[root#localhost ~]#
[root#localhost ~]$
#
$
What is your first name?:

Switch user on ssh.net [duplicate]

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.

Command Line through C# -- commands not executing

I'm using C# and AWS's CLI for S3. I seem to be having the same or a similar issue in two different places. In the first, I'm trying to execute multiple commands with the same process. The first command is to set the environment variables for the access key and secret key for AWS S3. The next two commands are just a simple ls to see what's in the bucket already and a sync to update its contents. If I take the commands to set the environment variables out, it works fine (because I'm already configured on the machine I'm working on), but since I need this to work on other machines, the environment variables need to be set. However, if they are, the following commands do not execute. Here's code for this:
String commands = #"set AWS_ACCESS_KEY_ID=keykeykey set AWS_SECRET_ACCESS_KEY=secretsecretsecret";
commands += #" & aws s3 ls s3://bucket";
commands += #" & aws s3 sync C:\test s3://bucket/kaaaaay";
The issue reappears in a different method. There, I need to get the value from a registry key. I know the command is correct, because it works just fine in a command line window. Here's code for that:
String commands = #"Reg.exe QUERY HKEY_LOCAL_MACHINE\Software\GIJOE\HASAKEY";
Any help would be much appreciated, because I'm stumped.

SharpSSH with Persistent ShellExec connections

I'm using SharpSSH to connect to an SSH server and I've tried using both SshShell and SshExec.
I need to be able to take a series of commands and send them to the server in order, so SshShell doesn't really do what I need since I would have to wrangle streams the whole time and it seems that it would be a bit of a kludge. So I've tried SshExec but found one problem with it, every time I send a command it seems to be making a new connection and losing the context of the last command. For example if I ran the following commands:
pwd
cd .ssh
pwd
I would expect it to output
/home/adam
/home/adam/.ssh
But, instead it just ouputs "/home/adam" both times, meaning that the directory change was lost in between.
Is there a way I can configure this so that it maintains a constant connection to the SSH server until I tell it to disconnect?
Do this:
exec.RunCommand("pwd; cd Desktop; pwd")
I am not sure how to do advanced commands, but I tried that and it outputs:
/Users/MyUser
/Users/MyUser/Desktop
To cd to a hidden directory (any directory beginning with a dot (.) character), you need to enclose the value in quotes.
According to the documentation:
4) If the first component of the directory operand is dot or dot-dot, proceed to step 6.
6) Set curpath to the string formed by the concatenation of the value of PWD , a slash character, and the operand.
In short, cd '.ssh' should do the trick.

How do I run that database backup and restore scripts from my winform application?

I am developing a small business application which uses Sqlserver 2005 database.
Platform: .Net framework 3.5;
Application type: windows application;
Language: C#
Question:
I need to take and restore the backup from my application. I have the required script generated from SSME.
How do I run that particular script (or scripts) from my winform application?
You can run these scripts the same way you run a query, only you don't connect to the database you want to restore, you connect to master instead.
If the machine where your application is running has the SQL Server client tools installed, you can use sqlcmd.
If you want to do it programatically you can use SMO
Tutorial
Just use your connection to the database (ADO I presume?) and send your plain TSQL instructions to the server through this connection.
For the backup you probably want to use xp_sqlmaint. It has the handy ability to remove old backups, and it creates a nice log file. You can call it via something like:
EXECUTE master.dbo.xp_sqlmaint N''-S "[ServerName]" [ServerLogonDetails] -D [DatabaseName] -Rpt "[BackupArchive]\BackupLog.txt" [RptExpirationSchedule] -CkDB -BkUpDB "[BackupArchive]" -BkUpMedia DISK [BakExpirationSchedule]''
(replace the [square brackets] with suitable values).
Also for the backup you may need to backup the transaction log. Something like:
IF DATABASEPROPERTYEX((SELECT db_name(dbid) FROM master..sysprocesses WHERE spid=##SPID), ''Recovery'') <> ''SIMPLE'' EXECUTE master.dbo.xp_sqlmaint N'' -S "[ServerName]" [ServerLogonDetails] -D [DatabaseName] -Rpt "[BackupArchive]\BackupLog_TRN.txt" [RptExpirationSchedule] -BkUpLog "[BackupArchive]" -BkExt TRN -BkUpMedia DISK [BakExpirationSchedule]''
I'd recommend storing the actual commands you're using in a database table (1 row per command) and use some sort of template replacement scheme to handle the configurable values. This would allow for easy changes to the commands, without needing to deploy new code.
For the restore you will need to kill all connections except for internal sql server ones. Basically take the results of "exec sp_who" and for rows that match on dbname, and have a status that is not "background", and a cmd that is not one of "SIGNAL HANDLER", "LOCK MONITOR", "LAZY WRITER", "LOG WRITER", "CHECKPOINT SLEEP" do a "kill" on the spid (eg: ExecuteNonQuery("kill 1283")).
You'll want to trap and ignore any exceptions from the KILL command. There's nothing you can do about them. If the restore cannot proceed because of existing connections it will raise an error.
One danger with killing connections is ADO's connection pool (more for asp.net apps than windows apps). ADO assumes the a connection fetched from the connection pool is valid... and it does not react well to connections that have been killed. The next operation that occurs on that connection will fail. I can't recall the error... you might be able to trap just that specific error and handle it... also with 3.5 I think you can flush the connection pool (so... trap the error, flush the connection pool, open the connection, try the command again... ugly but might be doable).

Categories

Resources