STREAMS Programming Guide

autopush(1M) Facility

The autopush(1M) facility configures the list of modules for a STREAMS device. It automatically pushes a prespecified list (/etc/iu.ap) of modules onto the stream when the STREAMS device is opened and the device is not already open.

The STREAMS Administrative Driver (SAD) (see the sad(7D) man page) provides an interface to the autopush mechanism. System administrators can open the SAD driver and set or get autopush(1M) information on other drivers. The SAD driver caches the list of modules to push for each driver. When the driver is opened the stream head checks the SAD's cache to determine if the device is configured to have modules pushed automatically. If an entry is found, the modules are pushed. If the device has been opened but not been closed, another open does not cause the list of the prespecified modules to be pushed again.

Four options configure the module list:

When the configuration list is cleared, a range of minor devices has to be cleared as a range and not in parts.

Application Interface

The SAD driver is accessed through the /dev/sad/admin or /dev/sad/user node. After the device is initialized, a program can perform any autopush configuration. The program should open the SAD driver; read a configuration file to find out what modules need to be configured for which devices, format the information into strapush structures; and make the SAD_SAP ioctl(2) calls. See the sad(7D) amn page for more information.

All autopush operations are performed through ioctl(2) commands to set or get autopush information. Only root can set autopush information, but any user can get the autopush information for a device.

The ioctl is a form of ioctl(fd, cmd, arg), where fd is the file descriptor of the SAD driver, cmd is either SAD_SAP (set autopush information) or SAD_GAP (get autopush information), and arg is a pointer to the structure strapush.

The strapush structure is:

/*
 * maximum number of modules that can be pushed on a
 * Stream using the autopush feature should be no greater
 * than nstrpush
 */
#define MAXAPUSH 8

/* autopush information common to user and kernel */

struct apcommon {
   uint     apc_cmd;          /* command - see below */
   major_t  apc_major;        /* major device number */
   minor_t  apc_minor;        /* minor device number */
   minor_t  apc_lastminor;    /* last minor dev # for range */
   uint     apc_npush;        /* number of modules to push */
};

/* ap_cmd - various options of autopush */
#define SAP_CLEAR       0 /* remove configuration list */
#define SAP_ONE         1 /* configure one minor device */
#define SAP_RANGE       2 /* config range of minor devices */
#define SAP_ALL         3 /* configure all minor devices */

/* format of autopush ioctls */
struct strapush {
		struct apcommon sap_common;
		char sap_list[MAXAPUSH] [FMNAMESZ + 1]; /* module list */
};

#define sap_cmd           sap_common.apc_cmd
#define sap_major         sap_common.apc_major
#define sap_minor         sap_common.apc_minor
#define sap_lastminor     sap_common.apc_lastminor
#define sap_npush         sap_common.apc_npush

A device is identified by its major device number, sap_major. The SAD_SAP ioctl(2) has the following options:

SAP_ONE

Configures a single minor device, sap_minor, of a driver.

SAP_RANGE

Configures a range of minor devices from sap_minor to sap_lastminor, inclusive.

SAP_ALL

Configures all minor devices of a device.

SAP_CLEAR

Clears the previous settings by removing the entry with the matching sap_major and sap_minor fields.

The list of modules is specified as a list of module names in sap_list. MAXAPUSH defines the maximum number of modules to push automatically.

A user can query the current configuration status of a given major/minor device by issuing the SAD_GAP ioctl(2) with sap_major and sap_minor values of the device set. On successful return from this system call, the strapush structure is filled in with the corresponding information for the device. The maximum number of entries the SAD driver can cache is determined by the tunable parameter NAUTOPUSH found in the SAD driver's master file.

The following is an example of an autopush configuration file in /etc/iu.ap:


#	major      minor         lastminor      modules

	wc         0             0              ldterm ttcompat
	zs         0             1              ldterm ttcompat
	ptsl       0             15             ldterm ttcompat

The first line configures a single minor device whose major name is wc and minor numbers start and end at 0, creating only one minor number. The modules automatically pushed are ldterm and ttcompat. The second line configures the zs driver whose minor device numbers are 0 and 1, and automatically pushes the same modules. The last line configures the ptsl driver whose minor device numbers are from 0 to 15, and automatically pushes the same modules.

STREAMS Anchors

An anchor is a lock that prevents the removal of a STREAMS module with an I_POP call. You place an anchor in a stream on the module you want to lock. All modules at or below the anchor are locked, and can only be popped by a privileged process.

Anchors and Data Flow

Anchors do not affect the flow of data in the stream or any other properties of the stream other than to lock down its plumbing. Although any process can place an anchor on a stream, once placed, it can only be removed by a privileged process.

An anchor is a per-stream entity; that is, there is exactly one per stream, and the anchor is moved upstream or downstream as needed. When a stream is created, the anchor is conceptually at the driver, and therefore has no effect on the stream. By issuing the I_ANCHOR ioctl on a stream, a process places the anchor at the STREAMS module directly below the stream head. This means that a process can move an existing anchor upstream by pushing additional STREAMS modules and calling I_ANCHOR again.

Although anchors conceptually exist at a specific location in the stream, they are not a data processing element and, as such, do not physically exist in the stream (for example, you will not find them walking q_next pointers.) This means that anchors will not appear in ioctls such as I_LOOK, and they are not included in the module count on the stream.

In order to remove an anchor, a process pops the module at which the anchor was placed. The anchor will only allow a privileged process to pop modules at or below it, which provides the security. Once an anchor has been removed, the anchor is not reset to its previous location in the stream, but rather positioned at the STREAMS driver again. When an unprivileged process attempts to pop an anchored module, the ioctl returns with EPERM.

The I_ANCHOR ioctl is processed completely in the stream head, and is never sent downstream. If a module or driver sends an I_ANCHOR to the stream head, it is silently discarded.

Using Anchors

An anchor can be placed on a STREAMS module by adding an [anchor] flag to an autopush configuration file or by directly calling the I_ANCHOR ioctl.

For example, this configuration file specifies that autopush should place an anchor between foo and babar in the bb stream:


#	major      minor       lastminor     modules

	aa         0           0             foo babar
	bb         0           1             foo [anchor] babar
	bb         131072      131073        foo [anchor] babar

As an example of calling the I_ANCHOR ioctl directly, the following two program listings illustrate the use of anchors in a client/server setting in which file descriptors are being passed.

In this example, the server program, fd_server.c, opens a stream, pushes modules on to it, and places an anchor on rlmod. The client program, fd_client.c attempts to pop modules, but can only pop rlmod or any modules below it if the client is run as root. That is, if the client is run as non-root, the I_POP fails.

Another concept illustrated by this example is that once the module with the anchor on it is popped by the privileged root process, the anchor is destroyed (technically, it is moved back to the driver, where it has no effect), and subsequent attempts by the client to pop modules will succeed, even if the client is run as non-root.

Finally, this example also illustrates the effect of passing file descriptors, rather than copying modules or the stream as a whole. Specifically, because the stream is not duplicated, all instances of the client operate on the same stream. In this case, running the client repeatedly causes it to work down the list of modules, popping each one off in turn, until all modules have been removed from the stream.


Example 11-1 fd_server.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <stdio.h>
#include <stropts.h>
#include <sys/conf.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>

#define	SPIPE_PATH	"/tmp/mypipe"

int
main(void)
{
	int 			pipefd[2];
	struct strrecvfd	strrecvfd;
	int			streamfd;

	/*
	 * Open a stream to hand back to the client.  Since this
	 * is just an example, we don't really care what we open;
	 * make a rlmod<->udp<->ip stream.  Stick an anchor above
	 * rlmod so the client cannot I_POP rlmod unless it's root.
	 */
	streamfd = open("/dev/udp", O_RDWR);
	if (streamfd == -1) {
		perror("open");
		return (EXIT_FAILURE);
	}

	if (ioctl(streamfd, I_PUSH, "rlmod") == -1) {
		perror("ioctl (I_PUSH) rlmod");
		return (EXIT_FAILURE);
	}

	if (ioctl(streamfd, I_ANCHOR, 0) == -1) {
		perror("ioctl (I_ANCHOR)");
		return (EXIT_FAILURE);
	}

	/*
	 * Open ourselves for business by making a mounted stream.
	 */
	if (pipe(pipefd) == -1) {
		perror("pipe");
		return (EXIT_FAILURE);
	}

	if (ioctl(pipefd[1], I_PUSH, "connld") == -1) {
		perror("ioctl (I_PUSH) connld");
		return (EXIT_FAILURE);
	}

	(void) umask(0);
	(void) close(creat(SPIPE_PATH, 0666));

	if (fattach(pipefd[1], SPIPE_PATH) == -1) {
		perror("fattach");
		return (EXIT_FAILURE);
	}

	/*
	 * Accept clients (iterative server)
	 */
	for (;;) {

		if (ioctl(pipefd[0], I_RECVFD, &strrecvfd) == -1) {
			perror("ioctl (I_RECVFD)");
			return (EXIT_FAILURE);
		}

		/*
		 * Send the STREAMS descriptor back to the client.
		 */
		if (ioctl(strrecvfd.fd, I_SENDFD, streamfd) == -1) {
			perror("ioctl (I_SENDFD)");
			return (EXIT_FAILURE);
		}
	}
}


Example 11-2 fd_client.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <stdio.h>
#include <stropts.h>
#include <sys/conf.h>
#include <unistd.h>
#include <stdlib.h>

#define	SPIPE_PATH	"/tmp/mypipe"

int
main(void)
{
	int 			serverfd;
	struct strrecvfd	strrecvfd;

	/*
	 * Open a connection to the server.
	 */
	serverfd = open(SPIPE_PATH, O_RDWR);
	if (serverfd == -1) {
		perror("open");
		return (EXIT_FAILURE);
	}

	/*
	 * Receive the STREAMS descriptor from the server.
	 */
	if (ioctl(serverfd, I_RECVFD, &strrecvfd) == -1) {
		perror("ioctl (I_RECVFD)");
		return (EXIT_FAILURE);
	}

	(void) printf("received the STREAMS descriptor; attempting to pop "
	    "the top module\n");

	/*
	 * Try to remove the top module from the stream.
	 */
	if (ioctl(strrecvfd.fd, I_POP, 0) == -1)
		perror("ioctl (I_POP)");

	(void) printf("modules on the stream: ");
	(void) fflush(stdout);

	/*
	 * Print out what the stream currently looks like.
	 */
	(void) dup2(strrecvfd.fd, 0);
	(void) system("strconf | paste -s -d' ' -");

	return (EXIT_SUCCESS);
}