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 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
|
README
lpty
a library that allows lua to start processes and control them via pty
Author: Gunnar Zötl , 2010-2013.
Released under MIT/X11 license. See file LICENSE for details.
Introduction
This is a simple interface to pty functionality, providing the ability
to fork a process and run it under pty control. It does not try to
mimic the posix API but instead focuses on the function of running and
controlling a program. The interface is very bare bones, no additional
functionality is provided, especially nothing like expect, which should
be a different package.
This has been developed on Linux and tested on MacOS X. It should
compile and run on any platform supporting the Unix 98 interface to
pty's. Support for additional Unixen might follow in the future,
depending on demand (especially my own ;) ). No support for windows is
planned, as ptys are a Unix thing. It might work with cygwin.
Installing
This uses only stuff that comes with your system, so you do not need to
install any additional libraries to satisfy any dependencies.
There are 2 options for building and installing this module, the first
and most obvious being luarocks. Normally, calling:
sudo luarocks install lpty
or when you have an unpacked source folder:
sudo luarocks make
should do the right thing.
There is also a Makefile in the distribution, that has been created for
use with Linux and MacOS X. It may work for other platforms, or you may
have to edit it by hand. Calling:
make && make install
will build the module and install it to some standard location for your
version of lua.
Using
Load the module with:
lpty = require "lpty"
Note: since version 1.0, lpty no longer registers a global table lpty
with lua.
Constructor
pty = lpty.new([options])
Creates and fully initializes the master side of a pty, and
returns an object to interact with the pty. Options for the pty
may be passed as key - value pairs in a table. These options
are:
throw_errors
If true, errors are thrown, otherwise they are returned as
a standard nil, "error message" pair. Default is false.
no_local_echo
If true, stuff sent to the parent side of the pty will not
be echoed back to the parent side, otherwise it will be
echoed back. Default is false.
Beware that this may not always work as expected.
use_path
If true, the pty:startproc() method for the created pty
will search for an executable file if the specified
filename does not contain a slash (/) character. The
search path is the path specified in the parents
environment by the PATH variable, no default will be used.
If false, the path to the executable must be explicitly
specified. Default is true.
If you do not want to pass any options, you may omit the options
table altogether.
Process handling
pty:startproc(command, arg1, arg2, ...)
Starts a process with the slave side of the pty as its
controlling terminal. The child processes stdin, stdout and
stderr are set to use the slave side of th pty. The command and
its arguments must be separate arguments to the startproc
method. If the pty has been created with use_path=true (the
default), then this uses the PATH environment variable of the
calling process (the one using lpty) to find any executables, so
in that case specifying a full path to the command is not always
necessary. Returns true if the call to fork() was successful. If
anything goes wrong whilst starting the new process, an error is
signalled (that is, thrown or returned as two values nil, "error
message", depending on the setting of the throw_errors flag.
If there is already an active process attached to the pty, the
new process is not started and the method returns false.
The pty:startproc() method can not check whether running the
command in the child process or basically anything that happens
in the child process after the fork is successful. If
pty:startproc() returns true, this means that setting up the
client side of the tty and fork()ing went well and yielded a
running child process. You may want to check wether it is still
running when you need it using the pty:hasproc() method,
especially if you're dealing with an interactive program. If the
program the child was supposed to start is not running when you
expect it to, you will have to call the pty:exitstatus() method
and maybe even check the contents of the pty for the reason.
pty:endproc([kill])
Terminates the process running with the pty as its controlling
terminal. If the optional parameter kill is false or omitted,
send the child process a SIGTERM, otherwise send a SIGKILL. It
is not an error to call this on a pty object that has no active
child processes. You do not need to call it when you have
already terminated the child process using more civilized means
(like, for example, sending it a quit command it understands).
After the child process has been terminated, a new one can be
started using the same PTY.
pty:hasproc()
returns true if the pty has an active child process, false if
not (none has been created, the child has terminated, ...).
pty:exitstatus()
Determines the reason why a previous child process has
terminated. Returns a the kind of termination and the code.
Possible combinations are:
false, nil
if the pty has an active child process or never had one,
'exit', exitcode
if the child process has exited normally,
'sig', signum
if the child process was terminated by a signal,
'unk', 0
if the reason for the death of the child process can not
be determined any more.
The pty:exitstatus() method works by having a fixed length
ringbuffer, in which the signal handler responsible for reaping
child processes places what waitpid() returns. Because of the
fixed size, when you create a lot of processes, the ring buffer
might wrap before you retrieve the exit status of your process,
in which case the 'unk', 0 values will be returned, 'unk'
meaning "unknown". If you hit this a lot, you may want to check
the exit status earlier, or if that does not help, change the
value for EXITSTATUS_BUFSIZ in lpty.c.
Process Environments
pty:getenviron()
Returns a table containing the environment a child process of
the pty will be executed in. If no custom environment has been
set using the pty:setenviron() method, this will be the
environment the parent process is running in.
pty:setenviron(env)
Sets a custom environment for the child process to be executed
within the pty. The argument must be a table containing a
complete environment for the child process. If you want to
modify the current environment, obtain it with the
pty:getenviron() method, modify it and pass it into this method
as a new environment.
Calling this function with nil as its argument resets the
environment to the environment the parent process is running in.
Communication
pty:readok([timeout])
returns true if the pty has data available to be read, false
otherwise. If a timeout value is specified, the call will wait
at most that amount of seconds for data to become available to
read. No value is equivalent to a timeout of 0 seconds.
pty:read([timeout])
reads data from the pty. This is a blocking read, so you can
specify an optional timeout in seconds after which the read
should return to the caller whether there was data or not. If
the timeout is omitted, the read will block until there is
something to read. Returns the data read, or nil if the request
timed out or was interrupted (failed with EINTR or ECHILD). If
an error occurred during the read attempt, it is signalled to
lua.
Note: unless you specified no_local_echo=true when creating the
pty, this reads data output from the client process as well as
data sent to the client process from the controlling skript.
The pty:read() method reads its data into a buffer. The size of
this buffer determines how much data can be returned from an
individual read. It is set by a #define in the module source and
by default is set to 4096. Considering the C-typical terminating
zero, this will leave a maximum of 4095 bytes to be read in one
go. In order to fetch all available data, just loop while
pty:readok() returns true and concatenate the results.
pty:sendok([timeout])
returns true if the pty can accept data sent to it, false
otherwise. If a timeout value is specified, the call will wait
at most that many seconds for the pty to accept data, No value
is equivalent to a timeout of 0 seconds.
pty:send(data [, timeout])
sends data to the pty. This will then be available to the child
process on its stdin. As sending data may block, there is an
optional argument for a timeout in seconds on the send
operation. If the timeout argument is omitted, pty:send() will
block until the pty can accept the data. Returns the number of
bytes written, or nil if the send timed out or was interrupted
(failed with EINTR or ECHILD). If an error occurred during the
attempt to write it is signalled to lua.
The pty:send() method may not send all data you gave it in one
go. For small amounts of data this is quite improbable, but in
industrial strength applications you should probably check the
return value of the pty:send() method against the length of the
data you intended to send.
pty:flush([mode])
flushes data from the pty. mode is an optional string argument.
If mode is "i", flush data received but not read. If mode is
"o", flush data written but not transmitted. If mode is anything
else or omitted, flush both sides.
Note that the mode "o" is hardly ever useful, because when
send() returns, the data it claims to have sent will already be
transmitted to the client side.
Information
pty:ttyname()
return the tty name of the slave side of the pty.
pty:getfd()
return the file handle of the master side of the pty. This is
intended for use with luasocket's select() or luaposix's poll()
and similar functions.
Additional Details
The local echo thing
As written in the description of the no_local_echo flag, this may not
always work as expected. Programs are free to set their terminals to
whatever settings they like, and indeed some programs do, or even
completely roll their own local echo policy. The most notable piece of
code to do this is Gnu readline, though it seems that this does not
happen for all versions of it. On Mac OS X, it certainly does, whereas
on my Ubuntu Box it does not. So if you write something using lpty that
is supposed to be used on different unixen, you better not rely on
no_local_echo to do what you think it should.
Communication and process handling
If you have a pty without a running child process, you can still write
to it. Anything you write to the pty will be available as input to a
child process that you start later.
You can also read from a pty without a child process, but only what you
have written to it - unless you specified no_local_echo=true for pty
creation, in which case there is nothing to read from it. This means
that if you (or the last active child process) have nothing written to
it, a read with timeout will always time out, and a read without
timeout will block. Also, if you read stuff back from the master side
of a pty without a child, that you have written there, it will not be
removed from a subsequent child process's input.
Starting a process takes some time, so after you have called
pty:startproc() it may take a while for data to become available from
the child process. So don't be surprised if a pty:readok() fails
directly after a pty:startproc().
Similar things are true for calls to pty:hasproc(). If you call
pty:hasproc() directly after pty:startproc(), chances are it will
return true whatever the outcome of starting the child process was,
because it just may not have had enough time to crash. Calling
pty:hasproc() will yield a much saner result if you do it after, for
example, a pty:read() timed out when you didn't expect it to, or
similar occurrences.
Garbage disposal
When a lpty object is garbage collected, its master side pty handle is
closed, and if it has a child process, that is terminated with a
SIGKILL. So it is reasonably safe to let pty's with active processes
become garbage, it just isn't very friendly towards the child
processes.
Example
A very simple example for lpty follows. In a real application you
should probably do some checking for errors and stuff output by your
child process. This uses bc to compute the sum of 2 numbers:
lpty = require "lpty" p = lpty.new() p:startproc("lua") p:read() --
skip startup message p:send("=111+234\n") r = p:read() print("Result is
"..r) p:send("os.exit()\n") -- terminate lua all friendly-like
print("Done.")
Also, see the samples folder in the distribution archive.
References
Read up on ptys on your local friendly linux system:
/www.kernel.org/doc/man-pages/online/pages/man7/pty.7.html>man 7 pty
and friends. This link is linux specific, but as lpty is implemented
using Unix 98 pseudo ttys, which Linux is using, the information is
probably valid whereever lpty runs.
|