STREAMS Programming Guide

Transparent ioctl Examples

Following are three examples of transparent ioctl(2) processing. Example 8–8 and Example 8–9 illustrate how to use M_COPYIN to copy data from user space. Example 8–10 illustrates how to use M_COPYOUT to copy data to user space. Example 8–11 is a more complex example showing state transitions that combine M_COPYIN and M_COPYOUT.

In these examples the message blocks are reused to avoid the overhead of allocating, copying, and releasing messages. This is standard practice.

The stream head guarantees that the size of the message block containing an iocblk(9S) structure is large enough to also hold the copyreq(9S) and copyresp(9S) structures.

M_COPYIN Example


Note –

Please see the copyin section in Writing Device Drivers for information on the 64–bit data structure macros.


Example 8–8 illustrates the processing of a transparent ioctl(2) request only (nontransparent request processing is not shown). In this example, the contents of a user buffer are to be transferred into the kernel as part of an ioctl call of the form


ioctl(fd, SET_ADDR, (caddr_t) &bufadd);

where bufadd is a struct address whose elements are:


struct address {	
     int				ad_len;;		/* buffer length in bytes */
     caddr_t		ad_addr;		/* buffer address */
};

This requires two pairs of messages (request and response) following receipt of the M_IOCTL message: the first copyin(9F), shown in Example 8–8, copies the structure (address) , and the second copyin(9F), shown in Example 8–9, copies the buffer (address.ad.addr). Two states are maintained and processed in this example: GETSTRUCT is for copying the address structure and GETADDR for copying the ad_addr of the structure.

The transparent part of the SET_ADDR M_IOCTL message processing requires that the address structure be copied from user address space. To accomplish this, the M_IOCTL message processing issues an M_COPYIN request to the stream head.


Example 8–8 M_COPYIN: Copy the address Structure

	struct address {		/* same members as in user space */
		int		ad_len;	/* length in bytes */
		caddr_t	ad_addr;	/* buffer address */
	};

	/* state values (overloaded in private field) */
	#define GETSTRUCT		0		/* address structure */
	#define GETADDR		1		/* byte string from ad_addr */

	static void xxioc(queue_t *q, mblk_t *mp);

	static int
	xxwput(q, mp)
		queue_t *q;		/* write queue */
		mblk_t *mp;
	{
		struct iocblk *iocbp;
		struct copyreq *cqp;

		switch (mp->b_datap->db_type) {
			.
			.
			.
			case M_IOCTL:
				/* Process ioctl commands */
				iocbp = (struct iocblk *)mp->b_rptr;
				switch (iocbp->ioc_cmd) {
					case SET_ADDR;
						if (iocbp->ioc_count != TRANSPARENT) {
						  /* do non-transparent processing here
						   *       (not shown here) */
						} else {
						 /* ioctl command is transparent 
						  * Reuse M_IOCTL block for first M_COPYIN 
						  * request of address structure */
						 cqp = (struct copyreq *)mp->b_rptr;
						 /* Get user space structure address from linked 
						  * M_DATA block */
						 cqp->cq_addr = *(caddr_t *) mp->b_cont->b_rptr;
						 cqp->cq_size = sizeof(struct address);
						 /* MUST free linked blks */
						 freemsg(mp->b_cont);
						 mp->b_cont = NULL;

						 /* identify response */
						 cqp->cq_private = (mblk_t *)GETSTRUCT;

						 /* Finish describing M_COPYIN message */
						 cqp->cq_flag = 0;
						 mp->b_datap->db_type = M_COPYIN;
						 mp->b_wptr = mp->b_rptr + sizeof(struct copyreq);
						 qreply(q, mp);
					  break;
					default: /* M_IOCTL not for us */
					  /* if module, pass on */
					  /* if driver, nak ioctl */
					  break;
				} /* switch (iocbp->ioc_cmd) */
				break;
			case M_IOCDATA:
				/* all M_IOCDATA processing done here */
				xxioc(q, mp);
				break;
		}
		return (0);
	}

xxwput() verifies that the SET_ADDR is TRANSPARENT to avoid confusion with an I_STR ioctl(2), which uses a value of ioc_cmd equivalent to the command argument of a transparent ioctl(2).

The if else statement checks whether the size count is equal to TRANSPARENT. If it is equal, the message was not generated from an I_STR ioctl(2) and the else clause of the if else executes.

	if (iocbp->ioc_count != TRANSPARENT) {
			/* do non-transparent processing here (not shown here) */
	} else {

The mblk is reused and mapped into a copyreq(9S) structure. The user space address of bufadd is contained in the b_cont of the M_IOCTL mblk. This address and its size are copied into the copyreq(9S) message. The b_cont of the copy request mblk is not needed, so it is freed and then filled with NULL.

	cqp = (struct copyreq *)mp->b_rptr;
			/* Get user space structure address from linked M_DATA block */
			cqp->cq_addr = *(caddr_t *) mp->b_cont->b_rptr;
			cqp->cq_size = sizeof(struct address);
			/* MUST free linked blks */
			freemsg(mp->b_cont);
			mp->b_cont = NULL;

Caution – Caution –

The layout of the iocblk, copyreq, and copyresp structures is different between 32–bit and 64–bit kernels. Be careful not to overload any data structure in the cp_private or the cq_filler fields because alignment has changed.



Example 8–9 M_COPYIN: Copy the Buffer Address

	xxioc(queue_t *q, mblk_t *mp)			/* M_IOCDATA processing */
	{
		struct iocblk *iocbp;
		struct copyreq *cqp;
		struct copyresp *csp;
		struct address *ap;

		csp = (struct copyresp *)mp->b_rptr;
		iocbp = (struct iocblk *)mp->b_rptr;

		/* validate this M_IOCDATA is for this module */
		switch (csp->cp_cmd) {
			case SET_ADDR:
				if (csp->cp_rval){ /* GETSTRUCT or GETADDR fail */
					freemsg(mp);
					return;
				}
				switch ((int)csp->cp_private){ /* determine state */
					case GETSTRUCT:		/* user structure has arrived */
					  /* reuse M_IOCDATA block */
					  mp->b_datap->db_type = M_COPYIN;
					  mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
					  cqp = (struct copyreq *)mp->b_rptr;
					  /* user structure */
					  ap = (struct address *)mp->b_cont->b_rptr;
					  /* buffer length */
					  cqp->cq_size = ap->ad_len;
					  /* user space buffer address */
					  cqp->cq_addr = ap->ad_addr;
					  freemsg(mp->b_cont);
					  mp->b_cont = NULL;
					  cqp->cq_flag = 0;
					  cqp->cp_private=(mblk_t *)GETADDR;  /*nxt st*/
					  qreply(q, mp);
					  break;

					case GETADDR:				/* user address is here */
					  /* hypothetical routine */
					  if (xx_set_addr(mp->b_cont) == FAILURE) {
						  mp->b_datap->db_type = M_IOCNAK;
						  iocbp->ioc_error = EIO;
					  } else {
						  mp->b_datap->db_type=M_IOCACK;/*success*/
						  /* can have been overwritten */
						  iocbp->ioc_error = 0;
						  iocbp->ioc_count = 0;
						  iocbp->ioc_rval = 0;
					  }
					  mp->b_wptr=mp->b_rptr + sizeof (struct ioclk);
					  freemsg(mp->b_cont);
					  mp->b_cont = NULL;
					  qreply(q, mp);
					  break;

					default: /* invalid state: can't happen */
					  freemsg(mp->b_cont);
					  mp->b_cont = NULL;
					  mp->b_datap->db_type = M_IOCNAK;
					  mp->b_wptr = mp->rptr + sizeof(struct iocblk);
					  /* can have been overwritten */
					  iocbp->ioc_error = EINVAL;
					  qreply(q, mp);
					  break;
				}
				break;						/* switch (cp_private) */

			default: /* M_IOCDATA not for us */
				/* if module, pass message on */
				/* if driver, free message */
				break;

On receipt of the M_IOCDATA message for the SET_ADDR command, xxioc() checks cp_rval. If an error occurred during the copyin operation, cp_rval is set. The mblk is freed and, if necessary, xxioc() cleans up from previous M_IOCTL requests, freeing memory, resetting state variables, and so on. The stream head returns the appropriate error to the user.

              if (csp->cp_rval){ /* GETSTRUCT or GETADDR fail */
					freemsg(mp);
					return;

If no error occurred during the copyin operation, the switch statement determines whether to process the user structure, GETSTRUCT, or user address, GETADDR.

             switch ((int)csp->cp_private){  /*determine state*/

The cp_private field set to GETSTRUCT indicates that the linked b_cont mblk contains a copy of the user's address structure. The example then copies the actual address specified in address.ad_addr. The program issues another M_COPYIN request to the stream head, but this time cq_private contains GETADDR to indicate that the M_IOCDATA response will contain a copy of address.ad_addr. The stream head copies the information at the requested user address and sends it downstream in another, final M_IOCDATA message.

				  case GETSTRUCT:		/* user structure has arrived */
					  /* reuse M_IOCDATA block */
					  mp->b_datap->db_type = M_COPYIN;
					  mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
					  cqp = (struct copyreq *)mp->b_rptr;
					  /* user structure */
					  ap = (struct address *)mp->b_cont->b_rptr;
					  /* buffer length */
					  cqp->cq_size = ap->ad_len;
					  /* user space buffer address */
					  cqp->cq_addr = ap->ad_addr;
					  freemsg(mp->b_cont);
					  mp->b_cont = NULL;
					  cqp->cq_flag = 0;
					  cqp->cp_private=(mblk_t *)GETADDR;  /*nxt st*/
					  qreply(q, mp);
					  break;

The final M_IOCDATA message arrives from the stream head. cp_private contains GETADDR. The ad_addr data is contained in the b_cont link of the mblk. If the address is successfully processed by xx_set_addr() (not shown here), the message is acknowledged with an M_IOCACK message. If xx_set_addr() fails, the message is rejected with an M_IOCNAK message]. xx_set_addr() processes the user address from the ioctl(2).

After the final M_IOCDATA message is processed, the module acknowledges the ioctl(2) to let the stream head know that processing is complete. This is done by sending an M_IOCACK message upstream if the request was successfully processed. Always set ioc_error to zero, otherwise an error code could be passed to the user application. Set ioc_rval and ioc_count to zero to reflect that a return value of 0 and no data is to be passed upstream. If the request cannot be processed, either an M_IOCNAK or M_IOCACK can be sent upstream with an appropriate error number. When sending an M_IOCNAK or M_IOCACK, freeing the linked M_DATA block is not mandatory. It is more efficient to use the stream head handle to free the linked M_DATA block.

If ioc_error is set in an M_IOCNAK or M_IOCNACK message, this error code will be returned to the user. If no error code is set in an M_IOCNAK message, EINVAL will be returned to the user.

                case GETADDR:			/* user address is here */
						/* hypothetical routine */
						if (xx_set_addr(mp->b_cont) == FAILURE) {
							mp->b_datap->db_type = M_IOCNAK;
							iocbp->ioc_error = EIO;
						} else {
							mp->b_datap->db_type=M_IOCACK;/*success*/
							/* can have been overwritten */
							iocbp->ioc_error = 0;
							iocbp->ioc_count = 0;
							iocbp->ioc_rval = 0;
						}
						mp->b_wptr=mp->b_rptr + sizeof (struct ioclk);
						freemsg(mp->b_cont);
						mp->b_cont = NULL;
						qreply(q, mp);
						break;

M_COPYOUT Example


Note –

Please see the copyout section in Writing Device Drivers for information on the 64–bit data structure macros.


The following code excerpts return option values for the STREAMS device by placing them in the user's options structure. This is done by a transparent ioctl(2) call of the form


struct options optadd;

ioctl(fd, GET_OPTIONS,(caddr_t) &optadd) 

or by a nontransparent I_STR call

	struct strioctl opts_strioctl;
	structure options optadd;

	opts_strioctl.ic_cmd = GET_OPTIONS;
	opts_strioctl.ic_timeout = -1
	opts_strioctl.ic_len = sizeof (struct options);
	opts_strioctl.ic_dp = (char *)&optadd;
	ioctl(fd, I_STR, (caddr_t) &opts_strioctl) 

In the nontransparent I_STR case, opts_strioctl.ic_dp points to the options structure optadd.

Example 8–7 illustrates support of both the I_STR and transparent forms of ioctl(2). The transparent form requires a single M_COPYOUT message following receipt of the M_IOCTL to copy out the contents of the structure. xxwput() is the write-side put procedure of module or driver xx.


Example 8–10 M_COPYOUT

	struct options {			/* same members as in user space */
		int			op_one;
		int			op_two;
		short			op_three;
		long			op_four;
	};

	static int
	xxwput (queue_t *q, mblk_t *mp)
	{
		struct iocblk *iocbp;
		struct copyreq *cqp;
		struct copyresp *csp;
		int transparent = 0;

		switch (mp->b_datap->db_type) {
			.
			.
			.
			case M_IOCTL:
				iocbp = (struct iocblk *)mp->b_rptr;
				switch (iocbp->ioc_cmd) {
					case GET_OPTIONS:
						if (iocbp->ioc_count == TRANSPARENT) {
						  transparent = 1;
						  cqp = (struct copyreq *)mp->b_rptr;
						  cqp->cq_size = sizeof(struct options);
						  /* Get struct address from
								linked M_DATA block */
						  cqp->cq_addr = (caddr_t) 
								*(caddr_t *)mp->b_cont->b_rptr;
						  cqp->cq_flag = 0;
						  /* No state necessary - we will only ever 
							* get one M_IOCDATA from the Stream head 
							* indicating success or failure for 
							* the copyout */
						}
						if (mp->b_cont)
							freemsg(mp->b_cont);
						if ((mp->b_cont = 
									allocb(sizeof(struct options), 
												BPRI_MED)) == NULL) {
						  mp->b_datap->db_type = M_IOCNAK;
						  iocbp->ioc_error = EAGAIN;
						  qreply(q, mp);
						  break;
						}
						/* hypothetical routine */
						xx_get_options(mp->b_cont);
						if (transparent) {
						  mp->b_datap->db_type = M_COPYOUT;
						  mp->b_wptr = mp->b_rptr + sizeof(struct copyreq);
						} else {
						  mp->b_datap->db_type = M_IOCACK;
						  iocbp->ioc_count = sizeof(struct options);
						}
						qreply(q, mp);
						break;

					default: /* M_IOCTL not for us */
						/*if module, pass on;if driver, nak ioctl*/
						break;
				} /* switch (iocbp->ioc_cmd) */
				break;

			case M_IOCDATA:
				csp = (struct copyresp *)mp->b_rptr;
				/* M_IOCDATA not for us */
				if (csp->cmd != GET_OPTIONS) {
					/*if module/pass on, if driver/free message*/
					break;
				}
				if ( csp->cp_rval ) {
					freemsg(mp);	/* failure */
					return (0);
				}
				/* Data successfully copied out, ack */

				/* reuse M_IOCDATA for ack */
				mp->b_datap->db_type = M_IOCACK;
				mp->b_wptr = mp->b_rptr + sizeof(struct iocblk);
				/* can have been overwritten */
				iocbp->ioc_error = 0;
				iocbp->ioc_count = 0;
				iocbp->ioc_rval = 0;
				qreply(q, mp);
				break;
				.
				.
				.
			} /* switch (mp->b_datap->db_type) */
			return (0);

xxwput() first checks whether the ioctl(2) command is transparent. If it is, the message is reused as an M_COPYOUT copy request message. The pointer to the receiving buffer is in the linked message and is copied into cq_addr. Because only a single copy out is being done, no state information needs to be stored in cq_private. The original linked message is freed, in case it isn't big enough to hold the request.

if (iocbp->ioc_count == TRANSPARENT) {
		transparent = 1;
		cqp = (struct copyreq *)mp->b_rptr;
		cqp->cq_size = sizeof(struct options);
		/* Get struct address from linked M_DATA block */
		cqp->cq_addr = (caddr_t) 
									*(caddr_t *)mp->b_cont->b_rptr;
		cqp->cq_flag = 0;
		/* No state necessary - we will only ever get one 
		 * M_IOCDATA from the Stream head indicating 
		 * success or failure for the copyout */
		}
		if (mp->b_cont)
			freemsg(mp->b_cont);

As an optimization, the following code checks the size of the message for reuse:

mp->b_cont->b_datap->db_lim 
- mp->b_cont->b_datap->db_base >= sizeof (struct options)


Note –

Hardening Information. After message reuse, make sure to retain the relation:


db_base <= b_rptr <= b_wptr <= db_lim

A new linked message is allocated to hold the option request. When using the transparent ioctl(2) M_COPYOUT command, data contained in the linked message is passed to the stream head. The stream head will copy the data to the user's address space and issue an M_IOCDATA in response to the M_COPYOUT message, which the module must acknowledge in an M_IOCACK message.

            /* hypothetical routine */
				xx_get_options(mp->b_cont);
				if (transparent) {
					mp->b_datap->db_type = M_COPYOUT;
					mp->b_wptr = mp->b_rptr + sizeof(struct copyreq);
				} else {
					mp->b_datap->db_type = M_IOCACK;
					iocbp->ioc_count = sizeof(struct options);
				}

If the message is not transparent (is issued through an I_STR ioctl(2)), the data is sent with the M_IOCACK acknowledgement message and copied into the buffer specified by the strioctl data structure. ioc_error, ioc_count, and ioc_rval are cleared to prevent any stale data from being passed back to the stream head.

           /* reuse M_IOCDATA for ack */
				mp->b_datap->db_type = M_IOCACK;
				mp->b_wptr = mp->b_rptr + sizeof(struct iocblk);
				/* can have been overwritten */
				iocbp->ioc_error = 0;
				iocbp->ioc_count = 0;
				iocbp->ioc_rval = 0;
				qreply(q, mp);
				break;

Bidirectional Data Transfer Example

Example 8–11 illustrates bidirectional data transfer between the kernel and application during transparent ioctl(2) processing. It also shows how to use more complex state information.

The user wants to send and receive data from user buffers as part of a transparent ioctl(2) call of the form:



	ioctl(fd, XX_IOCTL, (caddr_t) &addr_xxdata) 


Example 8–11 Bidirectional Data Transfer

struct xxdata {             /* same members in user space */
   int         x_inlen;     /* number of bytes copied in */
   caddr_t     x_inaddr;    /* buf addr of data copied in */
   int         x_outlen;    /* number of bytes copied out */
   caddr_t     x_outaddr;   /* buf addr of data copied out */
};
/* State information for ioctl processing */
struct state {
		int         st_state;    /* see below */
		struct xxdata		st_data;		/* see above */
};
/* state values */

#define GETSTRUC     0   /* get xxdata structure */
#define GETINDATA    1   /* get data from x_inaddr */
#define PUTOUTDATA   2   /* get response from M_COPYOUT */

static void xxioc(queue_t *q, mblk_t *mp);

static int
xxwput (queue_t *q, 	mblk_t *mp) {
		struct iocblk *iocbp;
		struct copyreq *cqp;
		struct state *stp;
		mblk_t *tmp;

		switch (mp->b_datap->db_type) {
			.
			.
			.
			case M_IOCTL:
				iocbp = (struct iocblk *)mp->b_rptr;
				switch (iocbp->ioc_cmd) {
				case XX_IOCTL:
				/* do non-transparent processing. (See I_STR ioctl
				 * processing discussed in previous section.)
				 */
				/*Reuse M_IOCTL block for M_COPYIN request*/

				cqp = (struct copyreq *)mp->b_rptr;

				/* Get structure's user address from
				 * linked M_DATA block */

				cqp->cq_addr = (caddr_t)
				 *(long *)mp->b_cont->b_rptr;
				freemsg(mp->b_cont);
				mp->b_cont = NULL;

				/* Allocate state buffer */

				if ((tmp = allocb(sizeof(struct state),
				 BPRI_MED)) == NULL) {
						mp->b_datap->db_type = M_IOCNAK;
						iocbp->ioc_error = EAGAIN;
						qreply(q, mp);
						break;
				}
				tmp->b_wptr += sizeof(struct state);
				stp = (struct state *)tmp->b_rptr;
				stp->st_state = GETSTRUCT;
				cqp->cq_private = tmp;

				/* Finish describing M_COPYIN message */

				cqp->cq_size = sizeof(struct xxdata);
				cqp->cq_flag = 0;
				mp->b_datap->db_type = M_COPYIN;
				mp->b_wptr=mp->b_rptr+sizeof(struct copyreq);
				qreply(q, mp);
				break;

			default: /* M_IOCTL not for us */
				/* if module, pass on */
				/* if driver, nak ioctl */
				break;

			} /* switch (iocbp->ioc_cmd) */
			break;

	case M_IOCDATA:
			xxioc(q, mp);   /*all M_IOCDATA processing here*/
			break;
			.
			.
			.
	} /* switch (mp->b_datap->db_type) */
}

Three pairs of messages are required following the M_IOCTL message:

  1. case GETSTRUCT copies the structure into the message buffer.

  2. case GETINDATA copies the user buffer into the message buffer.

  3. case PUTOUTDATA copies the second message buffer into the user buffer.

xxwput() is the write-side put procedure for module or driver xx. xxwput allocates a message block to contain the state structure and reuses the M_IOCTL to create an M_COPYIN message to read in the xxdata structure.

M_IOCDATA processing is done in xxioc() as shown in the following example:


Example 8–12 M_IOCDATA Processing

xxioc(					/* M_IOCDATA processing */
	queue_t *q,
	mblk_t *mp)
{
	struct iocblk *iocbp;
	struct copyreq *cqp;
	struct copyresp *csp;
	struct state *stp;
	mblk_t *xx_indata();

	csp = (struct copyresp *)mp->b_rptr;
	iocbp = (struct iocblk *)mp->b_rptr;
	switch (csp->cp_cmd) {

	case XX_IOCTL:
			if (csp->cp_rval) { /* failure */
				if (csp->cp_private) /* state structure */
						freemsg(csp->cp_private);
				freemsg(mp);
				return;
			 }
			stp = (struct state *)csp->cp_private->b_rptr;
			switch (stp->st_state) {

			case GETSTRUCT:			/* xxdata structure copied in */
										/* save structure */

				stp->st_data =
				 *(struct xxdata *)mp->b_cont->b_rptr;
				freemsg(mp->b_cont);
				mp->b_cont = NULL;
				/* Reuse M_IOCDATA to copyin data */
				mp->b_datap->db_type = M_COPYIN;
				cqp = (struct copyreq *)mp->b_rptr;
				cqp->cq_size = stp->st_data.x_inlen;
				cqp->cq_addr = stp->st_data.x_inaddr;
				cqp->cq_flag = 0;
				stp->st_state = GETINDATA; /* next state */
				qreply(q, mp);
				break;

			case GETINDATA: /* data successfully copied in */
				/* Process input, return output */
				if ((mp->b_cont = xx_indata(mp->b_cont))
				 == NULL) { /* hypothetical */
							/* fail xx_indata */
							mp->b_datap->db_type = M_IOCNAK;
							mp->b_wptr = mp->b_rptr +
								sizeof(struct iocblk);
						iocbp->ioc_error = EIO;
						qreply(q, mp);
						break;
				}
				mp->b_datap->db_type = M_COPYOUT;
				cqp = (struct copyreq *)mp->b_rptr;
				cqp->cq_size = min(msgdsize(mp->b_cont),
				 stp->st_data.x_outlen);
				cqp->cq_addr = stp->st_data.x_outaddr;
				cqp->cq_flag = 0;
				stp->st_state = PUTOUTDATA; /* next state */
				qreply(q, mp);
				break;

			case PUTOUTDATA: /* data copied out, ack ioctl */
				freemsg(csp->cp_private); /*state structure*/
				mp->b_datap->db_type = M_IOCACK;
				mp->b_wtpr = mp->b_rptr + sizeof (struct iocblk);
            /* can have been overwritten */
				iocbp->ioc_error = 0;
				iocbp->ioc_count = 0;
				iocbp->ioc_rval = 0;
				qreply(q, mp);
				break;

			default: /* invalid state: can't happen */
				freemsg(mp->b_cont);
				mp->b_cont = NULL;
				mp->b_datap->db_type = M_IOCNAK;
				mp->b_wptr=mp->b_rptr + sizeof (struct iocblk);
				iocbp->ioc_error = EINVAL;
				qreply(q, mp);
				break;
			} /* switch (stp->st_state) */
			break;
	default: /* M_IOCDATA not for us */
			/* if module, pass message on */
			/* if driver, free message */
			break;
	} /* switch (csp->cp_cmd) */
}

At case GETSTRUCT, the user xxdata structure is copied into the module's state structure (pointed to by cp_private in the message) and the M_IOCDATA message is reused to create a second M_COPYIN message to read the user data.

At case GETINDATA, the input user data is processed by xx_indata (not supplied in the example), which frees the linked M_DATA block and returns the output data message block. The M_IOCDATA message is reused to create an M_COPYOUT message to write the user data.

At case PUTOUTDATA, the message block containing the state structure is freed and an acknowledgement is sent upstream.

Care must be taken at the “can't happen” default case since the message block containing the state structure (cp_private) is not returned to the pool because it might not be valid. This might result in a lost block. The ASSERT helps find errors in the module if a “can't happen” condition occurs.

I_LIST ioctl(2)Example

The I_LIST ioctl(2) lists the drivers and module in a stream.


Example 8–13 List a Stream's Drivers and Modules

#include <stdio.h>
#include <string.h>
#include <stropts.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/socket.h>

main(int argc, const char **argv)
{
		int               s, i;
		int               mods;
		struct str_list   mod_list;
		struct str_mlist *mlist;

		/* Get a socket... */
		if((s = socket(AF_INET, SOCK_STREAM, 0)) <= 0) {
			perror("socket: ");
			exit(1);
		}

		/* Determine the number of modules in the stream. */
		if((mods = ioctl(s, I_LIST, 0)) < 0){
			perror("I_LIST ioctl");
		}
		if(mods == 0) {
			printf("No modules\n");
			exit(1);
		} else {
			printf("%d modules\n", mods);
		}
		/* Allocate memory for all of the module names... */
		mlist = (struct str_mlist *) calloc(mods, sizeof(struct str_mlist));
		if(mlist == 0){
			perror("malloc failure");
			exit(1);
		}
		mod_list.sl_modlist = mlist;
		mod_list.sl_nmods = mods;

		/* Do the ioctl and get the module names. */
		if(ioctl(s, I_LIST, &mod_list) < 0){
			perror("I_LIST ioctl fetch");
			exit(1);
		}

		/* Print out the name of the modules */
		for(i = 0; i < mods; i++) {
			printf("s: %s\n", mod_list.sl_modlist[i].l_name);
		}

		free(mlist);

		exit(0);
}