LIBSPT(3)

m4_include(manrules.m4)dnl

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) [(]ARGDEF(void,*,arg)[)];

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

The FUNC(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 VAR(cleanup) and VAR(cleanuparg). Otherwise, pass them NULL.

When this function returned ERR(SPT_E_NONE) or ERR(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 VAR(*phandle) and VAR(*pmasterfd).

Otherwise, some fatal error has happened. In ths case VAR(*phandle) is not changed. ERRDEF 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 FUNCDEF(int,,spt_close_pty,[[spt_handle,*,phandle]])

The FUNC(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 ERR(SPT_E_CLEANUP_FAIL), all possible cleanups are performed. This value is just a warning. ERRDEF SPT_E_NONE, SPT_E_CLEANUP_FAIL FUNCDEF(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 FUNC(spt_detach_handle) in your child process.

File descriptors opened by the FUNC(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 FUNC(spt_detach_handle).

The FUNC(spt_detach_handle) does not close the master side of PTY allocated by the FUNC(spt_open_pty). You probablly need to close it manually. FUNCDEF(int,,spt_get_slavename,[[const spt_handle,*,phandle],[char,**,pname]])

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

In both case you invoke the FUNC(spt_init_slavefd) against returned file descripter. ERRDEF SPT_E_NONE, SPT_E_NOMEM FUNCDEF(int,,spt_init_slavefd,[[const spt_handle,**,phandle],[int,,fd]])

You must invoke the FUNC(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 ERR(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 VAR(fd) is the slave. ERRDEF SPT_E_NONE, SPT_E_TTYINIT_FAIL FUNCDEF(int,,spt_utmp_set_host,[[spt_handle,*,phandle],[const char,*,host]])

The FUNC(spt_utmp_set_host) stores "hostname" to fill corresponding field of utmp/wtmp/lastlog later. Your invocation need to precede with the FUNC(spt_login_utmp), FUNC(spt_login_wtmp) and FUNC(spt_update_lastlog). ERRDEF SPT_E_NONE, SPT_E_NOMEM FUNCDEF(int,,spt_utmp_set_pid,[[spt_handle,*,phandle],[pid_t,,pid]])

The FUNC(spt_utmp_set_pid) stores "pid" to fill corresponding field of utmp/wtmp/lastlog later. Your invocation need to precede with the FUNC(spt_login_utmp), FUNC(spt_login_wtmp) and FUNC(spt_update_lastlog). ERRDEF SPT_E_NONE, SPT_E_NOMEM FUNCDEF(int,,spt_utmp_set_exit,[[spt_handle,*,phandle],[int,,exit_st]])

The FUNC(spt_utmp_set_exit) stores "exit status" to fill corresponding field of utmp/wtmp later. Your invocation need to precede with the FUNC(spt_logout_utmp) and FUNC(spt_logout_wtmp). ERRDEF SPT_E_NONE, SPT_E_NOMEM FUNCDEF(int,,spt_login_utmp,[[spt_handle,*,phandle]])

The FUNC(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 FUNC(spt_utmp_set_host) and FUNC(spt_utmp_set_pid) before. ERRDEF SPT_E_NONE, SPT_E_NOMEM, SPT_E_UNKNOWN, SPT_E_AGENT_FATAL, SPT_E_AGENT_DEAD, SPT_E_UTMP_FAIL FUNCDEF(int,,spt_logout_utmp,[[spt_handle,*,phandle]])

The FUNC(spt_logout_utmp) removes the entry created by the FUNC(spt_login_utmp) from utmp database. You need to specify exit status with the FUNC(spt_utmp_set_exit) before. ERRDEF SPT_E_NONE, SPT_E_NOMEM, SPT_E_UNKNOWN, SPT_E_AGENT_FATAL, SPT_E_AGENT_DEAD, SPT_E_UTMP_FAIL FUNCDEF(int,,spt_login_wtmp,[[spt_handle,*,phandle]])

The FUNC(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 FUNC(spt_utmp_set_host) and the FUNC(spt_utmp_set_pid) before. ERRDEF SPT_E_NONE, SPT_E_NOMEM, SPT_E_UNKNOWN, SPT_E_AGENT_FATAL, SPT_E_AGENT_DEAD, SPT_E_UTMP_FAIL FUNCDEF(int,,spt_logout_wtmp,[[spt_handle,*,phandle]])

The FUNC(spt_logout_wtmp) marks the wtmp entry created by the FUNC(spt_login_wtmp) as "logged out". You need to specify exit status with the FUNC(spt_utmp_set_exit) before. ERRDEF SPT_E_NONE, SPT_E_NOMEM, SPT_E_UNKNOWN, SPT_E_AGENT_FATAL, SPT_E_AGENT_DEAD, SPT_E_UTMP_FAIL FUNCDEF(int,,spt_update_lastlog,[[spt_handle,*,phandle]])

The FUNC(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 FUNC(spt_utmp_set_host) and the FUNC(spt_utmp_set_pid) before. ERRDEF SPT_E_NONE, SPT_E_NOMEM, SPT_E_UNKNOWN, SPT_E_AGENT_FATAL, SPT_E_AGENT_DEAD, SPT_E_UTMP_FAIL FUNCDEF(void,,spt_free_obj,[[void,*,pobj]])

The FUNC(spt_free_obj) releases memory allocated to return data by libspt. Such functions are the FUNC(spt_get_slavename) and FUNC(spt_termios_alloc) for now. FUNCDEF(const char,*,spt_strerror,[[int,,errnum]])

The FUNC(spt_strerror) converts libspt's error code to a descriptive string. This is similar to strerror(3). FUNCDEF(void,,spt_perror,[[const char,*,str],[int,,errnum]])

The FUNC(spt_perror) prints an error description corresponding to VAR(errnum). If VAR(str) is non-NULL, VAR(str) is prepended with a separator ":". This function is similar to perror(3) except that the FUNC(spt_perror) takes second argument VAR(errnum) instead of global errno. FUNCDEF(int,,spt_open_slave,[[const spt_handle,*,phandle]])

The FUNC(spt_open_slave) opens the slave side of PTY and returns its file descripter. This function is equivalent to following code except that ERR(SPT_E_NOMEM) never occurs:
const char *slavlename;
int retval;
spt_get_slavename(VAR(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. FUNCDEF(void,,spt_detach_ctty)

The FUNC(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 FUNC(spt_open_slave), as the new controlling terminal by the FUNC(spt_set_ctty2). ERRDEF SPT_E_NONE, SPT_E_CTTY_FAIL FUNCDEF(void,,spt_set_ctty2,[[int,,slavefd]])

The FUNC(spt_set_ctty2) sets the terminal VAR(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 FUNC(spt_detach_ctty). ERRDEF SPT_E_NONE, SPT_E_CTTY_FAIL

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

The FUNC(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 FUNC(spt_init_slavefd). Then the FUNC(spt_fork_pty) creates a child process whose controlling terminal is the slave side of the new PTY. Finally the FUNC(spt_fork_pty) registers calling user logging in from VAR(host) to utmp.

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

The FUNC(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 FUNC(spt_termios_*) and termios(4) interfaces. Then you need to close the slave in the parent process.

The implementation of FUNC(spt_fork_pty) itself gives a typical usage of libspt. ERRDEF 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 FUNCDEF(int,,spt_termios_alloc,[[spt_termios,**,ppterm]])

The FUNC(spt_termios_alloc) allocates an uninitialized TYPE(spt_termios) object and returns it. ERRDEF SPT_E_NONE, SPT_E_NOMEM FUNCDEF(void,,spt_termios_copy,[[spt_termios,*,dst],[const spt_termios,*,src]])

The FUNC(spt_termios_copy) copies the *VAR(src) into *VAR(dst). FUNCDEF(int,,spt_termios_get,[[spt_termios,*,pterm],[int,,fd]])

The FUNC(spt_termios_get) stores the current settings of the terminal specified by VAR(fd) into *VAR(pterm). It is a simple wrapper or portable alternative of tcgetattr(3). ERRDEF SPT_E_NONE, SPT_E_STTY_FAIL FUNCDEF(int,,spt_termios_set,[[const spt_termios,*,pterm],[int,,fd]])

The FUNC(spt_termios_set) sets the parameters associated with the terminal specified by VAR(fd) according to contents of *VAR(pterm). It is a simple wrapper or portable alternative of tcsetattr(3). ERRDEF SPT_E_NONE, SPT_E_INVAL, SPT_E_STTY_FAIL FUNCDEF(int,,spt_termios_sset,[[const spt_termios,*,pterm],[int,,fd],
    [const spt_termios,*,pold]])

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

It appears to be equivalent to this code:
if (spt_termios_set(pterm, fd))
    spt_termios_set(pold, fd);
But the FUNC(spt_termios_sset) makes every effort to restore previous terminal charactristics. In general the FUNC(spt_termios_sset) is safer way than the FUNC(spt_termios_set). ERRDEF SPT_E_NONE, SPT_E_INVAL, SPT_E_STTY_FAIL FUNCDEF(void,,spt_termios_default,[[spt_termios,*,pterm]])

The FUNC(spt_termios_default) fills *VAR(pterm) to set "sane" mode for a terminal with FUNC(spt_termios_set) or FUNC(spt_termios_sset). FUNCDEF(void,,spt_termios_raw,[[spt_termios,*,pterm]])

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

This is implemented as a simple wrapper of the cfmakeraw(3) if available. Otherwise the FUNC(spt_termios_raw) uses an alternative implementation. FUNCDEF(int,,spt_termios_fd_default,[[int,,fd]])

The FUNC(spt_termios_fd_default) sets "sane" mode for the terminal VAR(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 FUNC(spt_termios_default) and FUNC(spt_termios_sset). ERRDEF 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.