README

lpty

a library that allows lua to start processes and control them via pty

Author: Gunnar Zötl , 2010-2023.
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 a full blown expect, which should be a different package. A rather simple version of expect is available, however.

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

Process handling

Process Environments

Communication

Where the following functions have a timeout argument, the timeout is specified in seconds. The value can be fractional, i.e. you can specify a timeout of 0.1 seconds. The resolution depends on the underlying system.

Information

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+234n")
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: 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.