File: api-popen.texi

package info (click to toggle)
guile-ssh 0.18.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,996 kB
  • sloc: ansic: 4,821; lisp: 4,171; makefile: 310; sh: 259
file content (171 lines) | stat: -rw-r--r-- 5,557 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
@c -*-texinfo-*-
@c This file is part of Guile-SSH Reference Manual.
@c Copyright (C) 2015-2024 Artyom V. Poptsov
@c See the file guile-ssh.texi for copying conditions.

@node Remote Pipes
@section Remote Pipes
@cindex remote pipes

@code{(ssh popen)} provides API for working with remote pipes, akin to
@code{(ice-9 popen)} procedures (@pxref{Pipes,,, guile, The GNU Guile
Reference Manual})

@var{mode} argument allows to specify what kind of pipe should be created.
Allowed values are: @code{OPEN_READ}, @code{OPEN_WRITE}, @code{OPEN_BOTH}.

There is an additional value, @code{OPEN_PTY}, that allows to request a
@abbr{PTY, Pseudo Terminal}.  The terminal is needed to run such commands as
@command{top}.  Thus, to run @command{top} on the remote side you need to open
a remote pipe with ``t'' flag set.

@strong{Note} that when a PTY is used, a server merges @code{stderr} and
@code{stdout} streams.

Values of the aforementioned constants:

@table @samp
@item OPEN_READ
      ``r''
@item OPEN_WRITE
      ``w''
@item OPEN_BOTH
      ``r+''
@item OPEN_PTY
      ``t''
@end table

@deffn {Scheme Procedure} open-remote-pipe session command mode
Execute a @var{command} on the remote host using a @var{session} with a pipe
to it.  Returns newly created channel port (@xref{Channels} for more info)
with the specified @var{mode}.
@end deffn

@deffn {Scheme Procedure} open-remote-pipe* session mode prog [args...]
Execute @var{prog} on the remote host with the given @var{args} using a
@var{session} with a pipe to it.  Returns newly created channel port with the
specified @var{mode}.

The behavior is the same as for @code{open-pipe*} (@pxref{Pipes,,, guile, The
GNU Guile Reference Manual}) -- the program name and each argument is quoted
with single quotes and joined together separated by spaces, then the resulting
string is executed on the remote side as a single command.
@end deffn

@deffn {Scheme Procedure} open-remote-input-pipe session command
@deffnx {Scheme Procedure} open-remote-input-pipe* session prog [args...]
Equivalent to @code{open-remote-pipe} and @code{open-remote-pipe*} respectively
with mode @code{OPEN_READ}.
@end deffn

@deffn {Scheme Procedure} open-remote-output-pipe session command
@deffnx {Scheme Procedure} open-remote-output-pipe* session prog [args...]
Equivalent to @code{open-remote-pipe} and @code{open-remote-pipe*} respectively
with mode @code{OPEN_WRITE}.
@end deffn

@c -----------------------------------------------------------------------------
@subsection Examples

@subsubsection Simple cases

Here's a self-explanatory little script that executes @code{uname -o} command
on the local host and prints the result:

@lisp
#!/usr/bin/env -S guile -L modules -e main -s
!#

(use-modules (ice-9 rdelim)             ; @{read,write@}-line
             ;; Guile-SSH
             (ssh session)
             (ssh auth)
             (ssh popen))               ; remote pipes

(define (main args)
  ;; Make an SSH session to the local machine and the current user.
  (let ((session (make-session #:host "localhost")))

    ;; Connect the session and perform the authentication.
    (connect! session)
    (authenticate-server session)
    (userauth-agent! session)

    ;; Execute the command on the remote side and get the input pipe
    ;; to it.
    (let ((channel (open-remote-input-pipe* session "uname" "-o")))
      ;; Read and display the result.
      (write-line (read-line channel)))))
@end lisp

@c -----------------------------------------------------------------------------
@subsubsection Executing a command with a pseudo terminal

Surely we aren't limited to one-line outputs; for example, we can watch
@code{top} command executing on a remote side locally, by reading data from
the channel in a loop:

@lisp
(define OPEN_PTY_READ (string-append OPEN_PTY OPEN_READ))

(let ((channel (open-remote-pipe* session
                                  OPEN_PTY_READ
                                  "top" "-u" "avp")))
  (let r ((line (read-line channel)))
    (unless (eof-object? line)
      (write-line line)
      (r (read-line channel)))))
@end lisp

Or we can do the same, but this time with streams:

@lisp
(use-modules (srfi srfi-41)             ; streams
             (ssh session)
             (ssh auth)
             (ssh popen))

(define (pipe->stream p)
  (stream-let loop ((c (read-char p)))
    (if (eof-object? c)
        (begin
          (close-input-port p)
          stream-null)
        (stream-cons c (loop (read-char p))))))

(define OPEN_PTY_READ (string-append OPEN_PTY OPEN_READ))

(define (main args)
  (let ((s (make-session #:host "example.org")))
    (connect! s)
    (userauth-agent! s)
    (let ((rs (pipe->stream (open-remote-pipe* s
                                               OPEN_PTY_READ
                                               "top" "-u" "avp"))))
      (stream-for-each display rs))))
@end lisp

@c -----------------------------------------------------------------------------
@subsubsection Controlling the pseudo terminal size

To set the size of a pseudo terminal, one may use @code{channel-set-pty-size!}
from @code{(ssh channel)}.  For example:

@lisp
(use-modules (ssh popen)
             (ssh auth)
             (ssh channel))

(define OPEN_PTY_READ (string-append OPEN_PTY OPEN_READ))

;; Opening of a Guile-SSH session goes here ...

(let ((p (open-remote-pipe* session OPEN_PTY_READ "top" "-u" "avp")))
  (channel-set-pty-size! p 80 50)
  ;; Reading output from a port ...
  )
@end lisp

@c Local Variables:
@c TeX-master: "guile-ssh.texi"
@c End: