LIBSPT(3)

NAME
Libspt Programmer's Manual

INTRODUCTION

On many platforms terminal emulators need some privilege for some functionalities. Especially, they need root privilege on BSDs to prevent other users from peeking user's input. With libspt you can drop all privileges of your terminal emulator. Additionally libspt saves terminal emulator writers many portability related headaches. (Yes, there is many headaches.)

From libspt-0.2 it also provides a minimal termios(termio/sgtty) wrapper functionality. It does not support any customization nor complex settings but enables you to implement easiest functionalities portably with least work.

Libspt invokes one helper program "sptagent". It is a setuid root program. Its job is chown of pty and utmp logging. It is similar to SysV pt_chmod or Linux utempter but much more intelligent.

DESCRIPTION AND SYNOPSIS

typedef void (*spt_cleanup_proc) (void *arg);

int
spt_open_pty(spt_handle **pphandle, int *pmasterfd,
    spt_cleanup_proc cleanup, void *cleanuparg);

The spt_open_pty opens the master side of a PTY and launches sptagent. If you need to perform some cleanup (especially to close file descripters) in the child process (i.e. after fork(2) and before exec(2)), specify cleanup and cleanuparg. Otherwise, pass them NULL.

When this function returned SPT_E_NONE or SPT_E_CHOWN_FAIL, a new PTY is successfully opened and sptagent is successfully launched. Then a new handle and new PTY are stored to respectively *phandle and *pmasterfd.

Otherwise, some fatal error has happened. In ths case *phandle is not changed.

RETURN VALUE
    SPT_E_NONE, SPT_E_NOMEM, SPT_E_UNKNOWN, SPT_E_AGENT_VERSION, SPT_E_AGENT_EXEC, SPT_E_AGENT_INST, SPT_E_AGENT_FATAL, SPT_E_AGENT_DEAD, SPT_E_CHOWN_FAIL, SPT_E_RESOURCE

int
spt_close_pty(spt_handle *phandle);

The spt_close_pty closes the master side of a PTY and terminates sptagent. You need to close slave side manually.

Even if this function returns SPT_E_CLEANUP_FAIL, all possible cleanups are performed. This value is just a warning.

RETURN VALUE
    SPT_E_NONE, SPT_E_CLEANUP_FAIL

void
spt_detach_handle(spt_handle *phandle);

After you invoke fork(2) - probablly for the purpose is to create new session and launch a shell, whose controlling terminal is newly opened slave PTY -, you must invoke the spt_detach_handle in your child process.

File descriptors opened by the spt_open_pty to communicate with sptagent are duplicated by fork(2). They might be inherited to your shell and its children. This is why you must invoke the spt_detach_handle.

The spt_detach_handle does not close the master side of PTY allocated by the spt_open_pty. You probablly need to close it manually.

int
spt_get_slavename(const spt_handle *phandle, char **pname);

The spt_get_slavename returns the slave side of which PTY is available. You must free *pname with the spt_free_obj. Alternatively you can open the slave with the spt_open_slave without knowledge of its name.

In both case you invoke the spt_init_slavefd against returned file descripter.

RETURN VALUE
    SPT_E_NONE, SPT_E_NOMEM

int
spt_init_slavefd(const spt_handle **phandle, int fd);

You must invoke the spt_init_slavefd against the slave side of PTY before invocation of fork(2) to create new session. This function is intended to wrap ioctl(2)s on platforms where PTYs are implemented over STREAMS framework such as SysVs.

If this function returns SPT_E_TTYINIT_FAIL it is probablly due to your programming error or portability problem in libspt. Please send me an email if you are sure that fd is the slave.

RETURN VALUE
    SPT_E_NONE, SPT_E_TTYINIT_FAIL

int
spt_utmp_set_host(spt_handle *phandle, const char *host);

The spt_utmp_set_host stores "hostname" to fill corresponding field of utmp/wtmp/lastlog later. Your invocation need to precede with the spt_login_utmp, spt_login_wtmp and spt_update_lastlog.

RETURN VALUE
    SPT_E_NONE, SPT_E_NOMEM

int
spt_utmp_set_pid(spt_handle *phandle, pid_t pid);

The spt_utmp_set_pid stores "pid" to fill corresponding field of utmp/wtmp/lastlog later. Your invocation need to precede with the spt_login_utmp, spt_login_wtmp and spt_update_lastlog.

RETURN VALUE
    SPT_E_NONE, SPT_E_NOMEM

int
spt_utmp_set_exit(spt_handle *phandle, int exit_st);

The spt_utmp_set_exit stores "exit status" to fill corresponding field of utmp/wtmp later. Your invocation need to precede with the spt_logout_utmp and spt_logout_wtmp.

RETURN VALUE
    SPT_E_NONE, SPT_E_NOMEM

int
spt_login_utmp(spt_handle *phandle);

The spt_login_utmp adds an entry to utmp database. If and only if you are launching a login shell, you should register the user to utmp. You probablly want to update wtmp and lastlog.

You need to specify hostname and pid with the spt_utmp_set_host and spt_utmp_set_pid before.

RETURN VALUE
    SPT_E_NONE, SPT_E_NOMEM, SPT_E_UNKNOWN, SPT_E_AGENT_FATAL, SPT_E_AGENT_DEAD, SPT_E_UTMP_FAIL

int
spt_logout_utmp(spt_handle *phandle);

The spt_logout_utmp removes the entry created by the spt_login_utmp from utmp database. You need to specify exit status with the spt_utmp_set_exit before.

RETURN VALUE
    SPT_E_NONE, SPT_E_NOMEM, SPT_E_UNKNOWN, SPT_E_AGENT_FATAL, SPT_E_AGENT_DEAD, SPT_E_UTMP_FAIL

int
spt_login_wtmp(spt_handle *phandle);

The spt_login_wtmp appends an entry to wtmp database. If and only if you are launching a login shell, you should register the user to utmp. You probablly want to update utmp and lastlog.

You need to specify hostname and pid with the spt_utmp_set_host and the spt_utmp_set_pid before.

RETURN VALUE
    SPT_E_NONE, SPT_E_NOMEM, SPT_E_UNKNOWN, SPT_E_AGENT_FATAL, SPT_E_AGENT_DEAD, SPT_E_UTMP_FAIL

int
spt_logout_wtmp(spt_handle *phandle);

The spt_logout_wtmp marks the wtmp entry created by the spt_login_wtmp as "logged out". You need to specify exit status with the spt_utmp_set_exit before.

RETURN VALUE
    SPT_E_NONE, SPT_E_NOMEM, SPT_E_UNKNOWN, SPT_E_AGENT_FATAL, SPT_E_AGENT_DEAD, SPT_E_UTMP_FAIL

int
spt_update_lastlog(spt_handle *phandle);

The spt_update_lastlog updates lastlog entry of the user corresponding to current real uid. If and only if you are launching a login shell, you should update lastlog. You probablly want to update utmp and wtmp.

You need to specify hostname and pid with the spt_utmp_set_host and the spt_utmp_set_pid before.

RETURN VALUE
    SPT_E_NONE, SPT_E_NOMEM, SPT_E_UNKNOWN, SPT_E_AGENT_FATAL, SPT_E_AGENT_DEAD, SPT_E_UTMP_FAIL

void
spt_free_obj(void *pobj);

The spt_free_obj releases memory allocated to return data by libspt. Such functions are the spt_get_slavename and spt_termios_alloc for now.

const char
*spt_strerror(int errnum);

The spt_strerror converts libspt's error code to a descriptive string. This is similar to strerror(3).

void
spt_perror(const char *str, int errnum);

The spt_perror prints an error description corresponding to errnum. If str is non-NULL, str is prepended with a separator ":". This function is similar to perror(3) except that the spt_perror takes second argument errnum instead of global errno.

int
spt_open_slave(const spt_handle *phandle);

The spt_open_slave opens the slave side of PTY and returns its file descripter. This function is equivalent to following code except that SPT_E_NOMEM never occurs:
const char *slavlename;
int retval;
spt_get_slavename(phandle, &slavename); /* always succeed */
retval = open(slavename, O_RDWR | O_NOCTTY);
spt_free_obj(slavename);
return retval;
A negative return value indecates a failure of open(2). In this case you can diagnose it via errno.

void
spt_detach_ctty(void);

The spt_detach_ctty detaches the controlling terminal of the calling process. Then the calling process becomes the session leader of a new session. You can acquire another terminal, which may be opened by the spt_open_slave, as the new controlling terminal by the spt_set_ctty2.

RETURN VALUE
    SPT_E_NONE, SPT_E_CTTY_FAIL

void
spt_set_ctty2(int slavefd);

The spt_set_ctty2 sets the terminal slavefd up to the controlling terminal of the calling process. The calling process must not have a controlling terminal. You can detach it with the spt_detach_ctty.

RETURN VALUE
    SPT_E_NONE, SPT_E_CTTY_FAIL

int
spt_fork_pty(spt_handle **pphandle, int *pmasterfd, int *pslavefd,
    pid_t *pidptr, const char *host);

The spt_fork_pty is a high level function to create a child with a new PTY.

First it opens the master/slave pair of a PTY and set "sane" mode for the terminal with spt_init_slavefd. Then the spt_fork_pty creates a child process whose controlling terminal is the slave side of the new PTY. Finally the spt_fork_pty registers calling user logging in from host to utmp.

The variable *pidptr in the parent indecates the child's PID. In the child *pidptr is set to 0.

The spt_fork_pty closes the master PTY in the child process, but does not close the slave PTY in the parent process. You can perform extra operations to the terminal from the parent with the spt_termios_* and termios(4) interfaces. Then you need to close the slave in the parent process.

The implementation of spt_fork_pty itself gives a typical usage of libspt.

RETURN VALUE
    SPT_E_NONE, SPT_E_NOMEM, SPT_E_UNKNOWN, SPT_E_AGENT_VERSION, SPT_E_AGENT_EXEC, SPT_E_AGENT_INST, SPT_E_AGENT_FATAL, SPT_E_AGENT_DEAD, SPT_E_CHOWN_FAIL, SPT_E_NOTTY, SPT_E_RESOURCE, SPT_E_CTTY_FAIL

int
spt_termios_alloc(spt_termios **ppterm);

The spt_termios_alloc allocates an uninitialized spt_termios object and returns it.

RETURN VALUE
    SPT_E_NONE, SPT_E_NOMEM

void
spt_termios_copy(spt_termios *dst, const spt_termios *src);

The spt_termios_copy copies the *src into *dst.

int
spt_termios_get(spt_termios *pterm, int fd);

The spt_termios_get stores the current settings of the terminal specified by fd into *pterm. It is a simple wrapper or portable alternative of tcgetattr(3).

RETURN VALUE
    SPT_E_NONE, SPT_E_STTY_FAIL

int
spt_termios_set(const spt_termios *pterm, int fd);

The spt_termios_set sets the parameters associated with the terminal specified by fd according to contents of *pterm. It is a simple wrapper or portable alternative of tcsetattr(3).

RETURN VALUE
    SPT_E_NONE, SPT_E_INVAL, SPT_E_STTY_FAIL

int
spt_termios_sset(const spt_termios *pterm, int fd, const spt_termios *pold);

The spt_termios_sset sets the parameters associated with the terminal specified by fd according to *pterm as the spt_termios_set does. If it fails the spt_termios_sset tries to restore old settings according to *pold as precisely as possible. You must specify the old settings to *pold.

It appears to be equivalent to this code:
if (spt_termios_set(pterm, fd))
    spt_termios_set(pold, fd);
But the spt_termios_sset makes every effort to restore previous terminal charactristics. In general the spt_termios_sset is safer way than the spt_termios_set.

RETURN VALUE
    SPT_E_NONE, SPT_E_INVAL, SPT_E_STTY_FAIL

void
spt_termios_default(spt_termios *pterm);

The spt_termios_default fills *pterm to set "sane" mode for a terminal with spt_termios_set or spt_termios_sset.

void
spt_termios_raw(spt_termios *pterm);

The spt_termios_raw fills *pterm to set "raw" mode for a terminal with spt_termios_set or spt_termios_sset.

This is implemented as a simple wrapper of the cfmakeraw(3) if available. Otherwise the spt_termios_raw uses an alternative implementation.

int
spt_termios_fd_default(int fd);

The spt_termios_fd_default sets "sane" mode for the terminal fd. If you don't need to restore the old state you can set "sane" mode more easily with this function than you invoke the spt_termios_default and spt_termios_sset.

RETURN VALUE
    SPT_E_NONE, SPT_E_INVAL, SPT_E_STTY_FAIL

BUGS
Only PTY APIs of SVR4, BSDs and Cygwin are tested. PTY APIs of other platforms (especially HP-UX and AIX) have been not tested yet. Please report any bugs.