ParallelSSH / ssh2-python

Python bindings for libssh2 C library.
https://parallel-ssh.org
GNU Lesser General Public License v2.1
228 stars 72 forks source link

Example to open a FIFO pipe on remote #71

Closed delica1 closed 5 years ago

delica1 commented 5 years ago

Could you provide an example on how to open a special file object (FIFO pipe) on the remote machine for writing. I am going through the available examples but none of the provided ones cover this.

pkittenis commented 5 years ago

Hi there,

Thanks for the interest. What is different about opening a file? Even special files are used like regular files on posix systems, so what else other than cat <file> and .write on the channel would be needed for a specific example? PR welcome if there is anything unique about this.

delica1 commented 5 years ago

Paramiko for example is not able to open a FIFO pipe on Solaris for writing (via SFTP). Trying to do it always raises an OSError or IOError. I was hoping I would be able to do this with ssh2-python.

pkittenis commented 5 years ago

Short answer is try it and find out. What other libraries do or do not do is not relevant to this one.

Don't see anything special needed here - if there is some issue then can re-open with code to reproduce.

delica1 commented 5 years ago

Just tried it using the sftp example you provide. First issue is that LIBSSH2_FXF_APPEND is not implemented, so I cannot append to the input pipe, only write. The second and more serious problem is that I can open the FIFO pipe but cannot write anything to it. Any attempt to write throws an SFTPProtocolError. Writing to normal text files works fine.

import socket
from ssh2.session import Session
from ssh2.sftp import LIBSSH2_FXF_CREAT, LIBSSH2_FXF_WRITE, \
    LIBSSH2_SFTP_S_IRUSR, LIBSSH2_SFTP_S_IRGRP, LIBSSH2_SFTP_S_IWUSR, \
    LIBSSH2_SFTP_S_IROTH

USERNAME = 'user'
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('host', 22))
session = Session()
session.handshake(sock)
session.userauth_password(USERNAME, '')
chan = session.open_session()
chan.execute('ls -ltr')

sftp = session.sftp_init()
mode = LIBSSH2_SFTP_S_IRUSR | \
       LIBSSH2_SFTP_S_IWUSR | \
       LIBSSH2_SFTP_S_IRGRP | \
       LIBSSH2_SFTP_S_IROTH
f_flags = LIBSSH2_FXF_WRITE
remote_fh = sftp.open('input.inp', f_flags, mode)
remote_fh.write('string\n'.encode())

EDIT: just to note that the FIFO pipe is created with mkfifo input.inp

pkittenis commented 5 years ago

Seems to work for me. LIBSSH2_FXF_APPEND does exist in sftp module.

from ssh2.sftp import LIBSSH2_FXF_CREAT, LIBSSH2_FXF_WRITE, \
    LIBSSH2_SFTP_S_IRUSR, LIBSSH2_SFTP_S_IRGRP, LIBSSH2_SFTP_S_IWUSR, \
    LIBSSH2_SFTP_S_IROTH, LIBSSH2_FXF_APPEND

mode = LIBSSH2_SFTP_S_IWUSR
f_flags = LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE | LIBSSH2_FXF_APPEND
remote_fh = sftp.open('input.inp', f_flags, mode)
remote_fh.write('input\n')
(6, 6)
remote_fh.close()
0

Prints input on fifo stream.

Can also use chan.execute("echo 'input\n' >> input.inp") as above.

Protocol error is returned by server and indicates improper use of protocol by client code. Looks like create and append flags are needed in this case - the error was 'cannot open file for writing' without them.

delica1 commented 5 years ago

Appreciate the code @pkittenis, but even with the copy pasted code I still get the SFTPProtocolError. It could be that Solaris pipes are different in some way. It's hard to debug since it's such a general error that is raised. I'm not sure what to do next to resolve this. Is there some other info I could provide that would help?

chan.execute() works of course but I am writing large amounts of data into the pipe and would so really prefer to append directly to it, instead of using echo. My current setup is to write a text file in the same directory via sftp, and then cat file.text > input.inp. This works but is too clunky for my tastes. Another thing is, I need to wait until the entire text file is written over sftp before I can cat into the pipe. Writing directly to the pipe would mean the commands can start executing as soon as I start writing to the pipe.

pkittenis commented 5 years ago

What does session.last_error() return after protocol error? That should give some pointers as to why it is erroring. I do not have a solaris VM at hand but can test on it over weekend probably.

It can only be an issue with libssh2 if there is an issue, however, so most I can do is confirm it does or does not work - ssh2-python binds to libssh2, it does not change how that library behaves at all. Since it works in at least one case, either the SSH server does not implement the protocol correctly or libssh2 behaves differently on that particular server.

delica1 commented 5 years ago

Not super useful I am afraid, the output. The Solaris version is SunOS 5.11 11.3 sun4v sparc sun4v

session.last_error()
Out[21]: b''
pkittenis commented 5 years ago

I'd test with an OpenSSH server. From my regretful experiences with Solaris, its built in SSH server is.. lacking, to put it politely.

delica1 commented 5 years ago

Yes, it's a little different to work with :). Sun_SSH_2.4