System Interface Guide

Appendix A Full Code Examples

The following program is equivalent to the priocntl(1) -l utility invocation. It gets and prints the range of valid priorities for the time-sharing and real-time scheduler classes


Example A-1 Use of priocntl(2) to Display Valid Priorities

 /*
 * Get scheduler class IDs and priority ranges.
 */

#include <sys/types.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
#include <sys/tspriocntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
main ()
{
	pcinfo_t			pcinfo;
	tsinfo_t			*tsinfop;
	rtinfo_t		*	rtinfop;
	short			maxtsupri, maxrtpri;

	/* time sharing */
		(void) strcpy (pcinfo.pc_clname, "TS");
		if (priocntl (0L, 0L, PC_GETCID, &pcinfo) == -1L) {
			perror ("PC_GETCID failed for time-sharing class");
			exit (1);
		}
		tsinfop = (struct tsinfo *) pcinfo.pc_clinfo;
		maxtsupri = tsinfop->ts_maxupri;
		(void) printf("Time sharing: ID %ld, priority range -%d
					 through %d\n",
			pcinfo.pc_cid, maxtsupri, maxtsupri);

	/* real time */
		(void) strcpy(pcinfo.pc_clname, "RT");
		if (priocntl (0L, 0L, PC_GETCID, &pcinfo) == -1L) {
			perror ("PC_GETCID failed for realtime  class");
			exit (2);
		}
	rtinfop = (struct rtinfo *) pcinfo.pc_clinfo;
	maxrtpri = rtinfop->rt_maxpri;
	(void) printf("Real time: ID %ld, priority range 0 through
%d\n",
			pcinfo.pc_cid, maxrtpri);
	return (0);
}

The following screen shows the output of this program, called getcid in this example.


$ getcid 
Time sharing: ID 1, priority range -20 through 20 
Real time: ID 2, priority range 0 through 59 

The following example uses PC_GETCLINFO to get the class name of a process based on the process ID.


Example A-2 Use of priocntl(2) to Get a Class Name

/* Get scheduler class name given process ID. */
#include <sys/types.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
#include <sys/tspriocntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

main (argc, argv)
	int argc;
	char *argv[];
{
	pcinfo_t			pcinfo;
	id_t			pid, classID;
	id_t			getclassID();

	if ((pid = atoi(argv[1])) <= 0) {
		perror ("bad pid");
		exit (1);
	}
	if ((classID = getclassID(pid)) == -1) {
		perror ("unknown class ID");
		exit (2);
	}
	pcinfo.pc_cid = classID;
	if (priocntl (0L, 0L, PC_GETCLINFO, &pcinfo) == -1L) {
		perror ("PC_GETCLINFO failed");
		exit (3);
	}
	(void) printf("process ID %d, class %s\n", pid,
	 pcinfo.pc_clname);
}

/*
 * Return scheduler class ID of process with ID pid.
 */

getclassID (pid)
	id_t pid;
{
	pcparms_t			pcparms;

	pcparms.pc_cid = PC_CLNULL;
	if (priocntl(P_PID, pid, PC_GETPARMS, &pcparms) == -1) {
		return (-1);
	}
	return (pcparms.pc_cid);
}

The following program takes a process ID as input, makes the process a real-time process with the highest valid priority minus 1, and gives it the default time slice for that priority. The program calls the schedinfo function to get the real-time class ID and maximum priority.


Example A-3 Use of priocntl(2) to Convert a Specified Process to Real-time

/*
 * Input arg is proc ID. Make process a realtime
 * process with highest priority minus 1.
 */
#include <sys/types.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
#include <sys/tspriocntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

main (argc, argv)
	int argc;
	char *argv[];
{
	pcparms_t		pcparms;
	rtparms_t		*rtparmsp;
	id_t		pid, rtID;
	id_t		schedinfo();
	short		maxrtpri;
	if ((pid = atoi(argv[1])) <= 0) {
		perror ("bad pid");
		exit (1);
	}

	/* Get highest valid RT priority. */
	if ((rtID = schedinfo ("RT", &maxrtpri)) == -1) {
		perror ("schedinfo failed for RT");
		exit (2);
	}

	/* Change proc to RT, highest prio - 1, default time slice */
	pcparms.pc_cid = rtID;
	rtparmsp = (struct rtparms *) pcparms.pc_clparms;
	rtparmsp->rt_pri = maxrtpri - 1;
	rtparmsp->rt_tqnsecs = RT_TQDEF;

	if (priocntl(P_PID, pid, PC_SETPARMS, &pcparms) == -1) {
		perror ("PC_SETPARMS failed");
		exit (3);
	}
}

/*
 * Return class ID and maximum priority.
 * Input argument name is class name.
 * Maximum priority is returned in *maxpri.
 */

id_t
schedinfo (name, maxpri)
	char *name;
	short *maxpri;
{
	pcinfo_t			info;
	tsinfo_t			*tsinfop;
	rtinfo_			*rtinfop;

	(void) strcpy(info.pc_clname, name);
	if (priocntl (0L, 0L, PC_GETCID, &info) == -1L) {
		return (-1);
	}
	if (strcmp(name, "TS") == 0) {
		tsinfop = (struct tsinfo *) info.pc_clinfo;
		*maxpri = tsinfop->ts_maxupri;
	} else if (strcmp(name, "RT") == 0) {
		rtinfop = (struct rtinfo *) info.pc_clinfo;
		*maxpri = rtinfop->rt_maxpri;
	} else {
		return (-1);
	}
	return (info.pc_cid);
}

Here is a situation where priocntlset(2) is useful: suppose a program had both real-time and time-sharing processes that ran under a single user ID. If the program wanted to change the priority of only its real-time processes without changing the time-sharing processes to real-time processes, it could do so as shown in Example A-4.


Example A-4 Use of priocntlset(2) to Change a Process Priority

/*
 * Change real-time priorities of this uid
 * to highest realtime priority minus 1.
 */
#include <sys/types.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
#include <sys/tspriocntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

main (argc, argv)
	int argc;
	char *argv[];
{
	procset_t				procset;
	pcparms_t				pcparms;
	struct rtparms		*rtparmsp;
	id_t				rtclassID;
	id_t				schedinfo();
	short				maxrtpri;

	/* left set: select processes with same uid as this process */
	procset.p_lidtype = P_UID;
	procset.p_lid = getuid();

	/* get info on realtime class */
	if ((rtclassID = schedinfo ("RT", &maxrtpri)) == -1) {
		perror ("schedinfo failed");
		exit (1);
	}

...
}

/*
 * Return class ID and maximum priority.
 * Input argument name is class name.
 * Maximum priority is returned in *maxpri.
 */

id_t
schedinfo (name, maxpri)
	char *name;
	short *maxpri;
{
	pcinfo_t			info;
	tsinfo_t			*tsinfop;
	rtinfo_			*rtinfop;

	(void) strcpy(info.pc_clname, name);
	if (priocntl (0L, 0L, PC_GETCID, &info) == -1L) {
		return (-1);
	}
	if (strcmp(name, "TS") == 0) {
		tsinfop = (struct tsinfo *) info.pc_clinfo;
		*maxpri = tsinfop->ts_maxupri;
	} else if (strcmp(name, "RT") == 0) {
		rtinfop = (struct rtinfo *) info.pc_clinfo;
		*maxpri = rtinfop->rt_maxpri;
	} else {
		return (-1);
	}
	return (info.pc_cid);
}


Example A-5 Sample Program to Illustrate msgget(2)

/*
 * msgget.c: Illustrate the msgget() function.
 * This is a simple exerciser of the msgget() function. It prompts
 * for the arguments, makes the call, and reports the results.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

extern void	 exit();
extern void	 perror();

main()
{
	key_t		key;		/* key to be passed to msgget() */
	int		msgflg,		/* msgflg to be passed to msgget() */
			msqid;		/* return value from msgget() */

	(void) fprintf(stderr,
		"All numeric input is expected to follow C conventions:\n");
	(void) fprintf(stderr,
		"\t0x... is interpreted as hexadecimal,\n");
	(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
	(void) fprintf(stderr, "\totherwise, decimal.\n");
	(void) fprintf(stderr, "IPC_PRIVATE == %#lx\n", IPC_PRIVATE);
	(void) fprintf(stderr, "Enter key: ");
	(void) scanf("%ol", &key);
	(void) fprintf(stderr, "\nExpected flags for msgflg argument are:\n");
	(void) fprintf(stderr, "\tIPC_EXCL =\t%#8.8o\n", IPC_EXCL);
	(void) fprintf(stderr, "\tIPC_CREAT =\t%#8.8o\n", IPC_CREAT);
	(void) fprintf(stderr, "\towner read =\t%#8.8o\n", 0400);
	(void) fprintf(stderr, "\towner write =\t%#8.8o\n", 0200);
	(void) fprintf(stderr, "\tgroup read =\t%#8.8o\n", 040);
	(void) fprintf(stderr, "\tgroup write =\t%#8.8o\n", 020);
	(void) fprintf(stderr, "\tother read =\t%#8.8o\n", 04);
	(void) fprintf(stderr, "\tother write =\t%#8.8o\n", 02);
	(void) fprintf(stderr, "Enter msgflg value: ");
	(void) scanf("%i", &msgflg);

	(void) fprintf(stderr, "\nmsgget: Calling msgget(%#lx, %#o)\n",
	key, msgflg);
	if ((msqid = msgget(key, msgflg)) == -1)
	{
		perror("msgget: msgget failed");
		exit(1);
	} else {
		(void) fprintf(stderr,
			"msgget: msgget succeeded: msqid = %d\n", msqid);
		exit(0);
	}
} 


Example A-6 Sample Program to Illustrate msgctl(2)

/*
 * msgctl.c:  Illustrate the msgctl() function.
 *
 * This is a simple exerciser of the msgctl() function.  It allows
 * you to perform one control operation on one message queue.  It
 * gives up immediately if any control operation fails, so be
careful
 * not to set permissions to preclude read permission; you won't
be
 * able to reset the permissions with this code if you do.
 */
#include			<stdio.h>
#include			<sys/types.h>
#include			<sys/ipc.h>
#include			<sys/msg.h>
#include			<time.h>

static void do_msgctl();
extern void exit();
extern void perror();
static char warning_message[] = "If you remove read permission
for \
					yourself, this program will fail frequently!";

main()
{
	struct msqid_ds	buf;					/* queue descriptor buffer for IPC_STAT
							   and IP_SET commands */
	int				cmd,		/* command to be given to msgctl() */
					msqid;		/* queue ID to be given to msgctl() */

	(void) fprintf(stderr,
		"All numeric input is expected to follow C conventions:\n");
	(void) fprintf(stderr,
		"\t0x... is interpreted as hexadecimal,\n");
	(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
	(void) fprintf(stderr, "\totherwise, decimal.\n");

	/* Get the msqid and cmd arguments for the msgctl() call. */
	(void) fprintf(stderr,
		"Please enter arguments for msgctls() as requested.");
	(void) fprintf(stderr, "\nEnter the msqid: ");
	(void) scanf("%i", &msqid);
	(void) fprintf(stderr, "\tIPC_RMID = %d\n", IPC_RMID);
	(void) fprintf(stderr, "\tIPC_SET = %d\n", IPC_SET);
	(void) fprintf(stderr, "\tIPC_STAT = %d\n", IPC_STAT);
	(void) fprintf(stderr, "\nEnter the value for the command: ");
	(void) scanf("%i", &cmd);

	switch (cmd) {
		case IPC_SET:
			/* Modify settings in the message queue control structure.
*/
			(void) fprintf(stderr, "Before IPC_SET, get current
values:");
			/* fall through to IPC_STAT processing */
		case IPC_STAT:
			/* Get a copy of the current message queue control
			 * structure and show it to the user. */
			do_msgctl(msqid, IPC_STAT, &buf);
			(void) fprintf(stderr, 
			"msg_perm.uid = %d\n", buf.msg_perm.uid);
			(void) fprintf(stderr,
			"msg_perm.gid = %d\n", buf.msg_perm.gid);
			(void) fprintf(stderr,
			"msg_perm.cuid = %d\n", buf.msg_perm.cuid);
			(void) fprintf(stderr,
			"msg_perm.cgid = %d\n", buf.msg_perm.cgid);
			(void) fprintf(stderr, "msg_perm.mode = %#o, ",
			buf.msg_perm.mode);
			(void) fprintf(stderr, "access permissions = %#o\n",
			buf.msg_perm.mode & 0777);
			(void) fprintf(stderr, "msg_cbytes = %d\n",
							buf.msg_cbytes);
			(void) fprintf(stderr, "msg_qbytes = %d\n",
							buf.msg_qbytes);
			(void) fprintf(stderr, "msg_qnum = %d\n", buf.msg_qnum);
			(void) fprintf(stderr, "msg_lspid = %d\n",
							buf.msg_lspid);
			(void) fprintf(stderr, "msg_lrpid = %d\n",
							buf.msg_lrpid);
			(void) fprintf(stderr, "msg_stime = %s", buf.msg_stime ?
			ctime(&buf.msg_stime) : "Not Set\n");
			(void) fprintf(stderr, "msg_rtime = %s", buf.msg_rtime ?
			ctime(&buf.msg_rtime) : "Not Set\n");
			(void) fprintf(stderr, "msg_ctime = %s",
							ctime(&buf.msg_ctime));
			if (cmd == IPC_STAT)
				break;
			/*  Now continue with IPC_SET. */
			(void) fprintf(stderr, "Enter msg_perm.uid: ");
			(void) scanf ("%hi", &buf.msg_perm.uid);
			(void) fprintf(stderr, "Enter msg_perm.gid: ");
			(void) scanf("%hi", &buf.msg_perm.gid);
			(void) fprintf(stderr, "%s\n", warning_message);
			(void) fprintf(stderr, "Enter msg_perm.mode: ");
			(void) scanf("%hi", &buf.msg_perm.mode);
			(void) fprintf(stderr, "Enter msg_qbytes: ");
			(void) scanf("%hi", &buf.msg_qbytes);
			do_msgctl(msqid, IPC_SET, &buf);
			break;
		case IPC_RMID:
		default:
			/* Remove the message queue or try an unknown command. */
			do_msgctl(msqid, cmd, (struct msqid_ds *)NULL);
			break;
	}
	exit(0);
}

/*
 * Print indication of arguments being passed to msgctl(), call
 * msgctl(), and report the results. If msgctl() fails, do not
 * return; this example doesn't deal with errors, it just reports
 * them.
 */
static void
do_msgctl(msqid, cmd, buf)
struct msqid_ds				*buf;		/* pointer to queue descriptor buffer */
int				cmd,		/* command code */
					msqid;		/* queue ID */
{
	register int	rtrn;		/* hold area for return value from msgctl() */

	(void) fprintf(stderr, "\nmsgctl: Calling msgctl(%d, %d, %s)\n",
			msqid, cmd, buf ? "&buf" : "(struct msqid_ds *)NULL");
	rtrn = msgctl(msqid, cmd, buf);
	if (rtrn == -1) {
		perror("msgctl: msgctl failed");
		exit(1);
	} else {
		(void) fprintf(stderr, "msgctl: msgctl returned %d\n",
						rtrn);
	}
}


Example A-7 Sample Program to Illustrate msgsnd(2) and msgrcv(2)

/*
 * msgop.c: Illustrate the msgsnd() and msgrcv() functions.
 *
 * This is a simple exerciser of the message send and receive
 * routines. It allows the user to attempt to send and receive 
 * as many messages as wanted to or from one message queue.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

static int	ask();
extern void	exit();
extern char	*malloc();
extern void	perror();

char	first_on_queue[] = "-> first message on queue",
	full_buf[] = "Message buffer overflow. Extra message text\
				  discarded.";

main()
{
	register int		c;			/* message text input */
	int					choice;	/* user's selected operation code */
	register int		i;			/* loop control for mtext */
	int					msgflg;	/* message flags for the operation */
	struct msgbuf 	*msgp;		/* pointer to the message buffer */
	int					msgsz;		/* message size */
	long				msgtyp;	/* desired message type */
	int					msqid,		/* message queue ID to be used */
						maxmsgsz,	/* size of allocated message buffer */
						rtrn;		/* return value from msgrcv or msgsnd */

	(void) fprintf(stderr,
		"All numeric input is expected to follow C conventions:\n");
	(void) fprintf(stderr,
		"\t0x... is interpreted as hexadecimal,\n");
	(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
	(void) fprintf(stderr, "\totherwise, decimal.\n");
	/* Get the message queue ID and set up the message buffer. */
	(void) fprintf(stderr, "Enter msqid: ");
	(void) scanf("%i", &msqid);
	/*
	 * Note that <sys/msg.h> includes a definition of struct
msgbuf
	 * with the mtext field defined as:
	 *		 char	mtext[1];
	 * therefore, this definition is only a template, not a structure
	 * definition that you can use directly, unless you want only to
	 * send and receive messages of 0 or 1 byte. To handle this,
	 * malloc an area big enough to contain the template - the size
	 * of the mtext template field + the size of the mtext field
	 * wanted. Then you can use the pointer returned by malloc as a
	 * struct msgbuf with an mtext field of the size you want. Note
	 * also that sizeof msgp->mtext is valid even though msgp isn't
	 * pointing to anything yet. Sizeof doesn't dereference msgp, but
	 * uses its type to figure out what you are asking about.
	 */
	(void) fprintf(stderr,
		"Enter the message buffer size you want:");
	(void) scanf("%i", &maxmsgsz);
	if (maxmsgsz < 0) {
		(void) fprintf(stderr, "msgop: %s\n",
				"The message buffer size must be >= 0.");
		exit(1);
	}
	msgp = (struct msgbuf *)malloc(sizeof(struct msgbuf)
				- sizeof (msgp->mtext) + maxmsgsz);
	if (msgp == NULL) {
		(void) fprintf(stderr, "msgop: %s %d byte messages.\n",
				"could not allocate message buffer for", maxmsgsz);
		exit(1);
	}
	/* Loop through message operations until the user is ready to
		quit. */
	while (choice = ask()) {
		switch (choice) {
		case 1: /* msgsnd() requested: Get the arguments, make the
				call, and report the results. */
			(void) fprintf(stderr, "Valid msgsnd message %s\n",
				"types are positive integers.");
			(void) fprintf(stderr, "Enter msgp->mtype: ");
			(void) scanf("%li", &msgp->mtype);
			if (maxmsgsz) {
				/* Since you've been using scanf, you need the loop
				   below to throw away the rest of the input on the
				   line after the entered mtype before you start
				   reading the mtext. */
				while ((c = getchar()) != '\n' && c != EOF);
				(void) fprintf(stderr, "Enter a %s:\n",
								"one line message");
				for (i = 0; ((c = getchar()) != '\n'); i++) {
					if (i >= maxmsgsz) {
						(void) fprintf(stderr, "\n%s\n", full_buf);
						while ((c = getchar()) != '\n');
						break;
					}
					msgp->mtext[i] = c;
				}
				msgsz = i;
			} else
				msgsz = 0;
			(void) fprintf(stderr,"\nMeaningful msgsnd flag is:\n");
			(void) fprintf(stderr, "\tIPC_NOWAIT =\t%#8.8o\n",
				IPC_NOWAIT);
			(void) fprintf(stderr, "Enter msgflg: ");
			(void) scanf("%i", &msgflg);
			(void) fprintf(stderr, "%s(%d, msgp, %d, %#o)\n",
				"msgop: Calling msgsnd", msqid, msgsz, msgflg);
			(void) fprintf(stderr, "msgp->mtype = %ld\n",
							msgp->mtype);
			(void) fprintf(stderr, "msgp->mtext = \"");
			for (i = 0; i < msgsz; i++)
				(void) fputc(msgp->mtext[i], stderr);
				(void) fprintf(stderr, "\"\n");
				rtrn = msgsnd(msqid, msgp, msgsz, msgflg);
				if (rtrn == -1)
					perror("msgop: msgsnd failed");
				else
					(void) fprintf(stderr,
								"msgop: msgsnd returned %d\n", rtrn);
				break;
		case 2: /* msgrcv() requested: Get the arguments, make the
				   call, and report the results. */
			for (msgsz = -1; msgsz < 0 || msgsz > maxmsgsz;
						(void) scanf("%i", &msgsz))
				(void) fprintf(stderr, "%s (0 <= msgsz <= %d): ",
								"Enter msgsz", maxmsgsz);
			(void) fprintf(stderr, "msgtyp meanings:\n");
			(void) fprintf(stderr, "\t 0 %s\n", first_on_queue);
			(void) fprintf(stderr, "\t>0 %s of given type\n",
				first_on_queue);
			(void) fprintf(stderr, "\t<0 %s with type <= |msgtyp|\n",
							first_on_queue);
			(void) fprintf(stderr, "Enter msgtyp: ");
			(void) scanf("%li", &msgtyp);
			(void) fprintf(stderr,
							"Meaningful msgrcv flags are:\n");
			(void) fprintf(stderr, "\tMSG_NOERROR =\t%#8.8o\n",
							MSG_NOERROR);
			(void) fprintf(stderr, "\tIPC_NOWAIT =\t%#8.8o\n",
							IPC_NOWAIT);
			(void) fprintf(stderr, "Enter msgflg: ");
			(void) scanf("%i", &msgflg);
			(void) fprintf(stderr, "%s(%d, msgp, %d, %ld, %#o);\n",
							"msgop: Calling msgrcv", msqid, msgsz,
							msgtyp, msgflg);
			rtrn = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
			if (rtrn == -1)
				perror("msgop: msgrcv failed");
			else {
				(void) fprintf(stderr, "msgop: %s %d\n",
								"msgrcv returned", rtrn);
				(void) fprintf(stderr, "msgp->mtype = %ld\n",
								msgp->mtype);
				(void) fprintf(stderr, "msgp->mtext is: \"");
				for (i = 0; i < rtrn; i++)
					(void) fputc(msgp->mtext[i], stderr);
				(void) fprintf(stderr, "\"\n");
			}
			break;
		default:
			(void) fprintf(stderr, "msgop: operation unknown\n");
			break;
		}
	}
	exit(0);
}

/*
 *	Ask the user what to do next. Return the user's choice code.
 *	Don't return until the user selects a valid choice.
 */
static
ask()
{
	int	response;	/* User's response. */

	do {
		(void) fprintf(stderr, "Your options are:\n");
		(void) fprintf(stderr, "\tExit =\t0 or Control-D\n");
		(void) fprintf(stderr, "\tmsgsnd =\t1\n");
		(void) fprintf(stderr, "\tmsgrcv =\t2\n");
		(void) fprintf(stderr, "Enter your choice: ");

		/* Preset response so "^D" will be interpreted as exit. */
		response = 0;
		(void) scanf("%i", &response);
	} while (response < 0 || response > 2);

	return(response);
} 


Example A-8 Sample Program to Illustrate semget(2)

/*
 * semget.c: Illustrate the semget() function.
 *
 * This is a simple exerciser of the semget() function. It prompts
 * for the arguments, makes the call, and reports the results.
*/

#include			<stdio.h>
#include			<sys/types.h>
#include			<sys/ipc.h>
#include			<sys/sem.h>

extern void				exit();
extern void				perror();

main()
{
	key_t		key;			/* key to pass to semget() */
	int		semflg;			/* semflg to pass to semget() */
	int		nsems;			/* nsems to pass to semget() */
	int		semid;			/* return value from semget() */

	(void) fprintf(stderr,
		"All numeric input must follow C conventions:\n");
	(void) fprintf(stderr,
		"\t0x... is interpreted as hexadecimal,\n");
	(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
	(void) fprintf(stderr, "\totherwise, decimal.\n");
	(void) fprintf(stderr, "IPC_PRIVATE == %#lx\n", IPC_PRIVATE);
	(void) fprintf(stderr, "Enter key: ");
	(void) scanf("%li", &key);

	(void) fprintf(stderr, "Enter nsems value: ");
	(void) scanf("%i", &nsems);
	(void) fprintf(stderr, "\nExpected flags for semflg are:\n");
	(void) fprintf(stderr, "\tIPC_EXCL = \t%#8.8o\n", IPC_EXCL);
	(void) fprintf(stderr, "\tIPC_CREAT = \t%#8.8o\n", IPC_CREAT);
	(void) fprintf(stderr, "\towner read = \t%#8.8o\n", 0400);
	(void) fprintf(stderr, "\towner alter = \t%#8.8o\n", 0200);
	(void) fprintf(stderr, "\tgroup read = \t%#8.8o\n", 040);
	(void) fprintf(stderr, "\tgroup alter = \t%#8.8o\n", 020);
	(void) fprintf(stderr, "\tother read = \t%#8.8o\n", 04);
	(void) fprintf(stderr, "\tother alter = \t%#8.8o\n", 02);
	(void) fprintf(stderr, "Enter semflg value: ");
	(void) scanf("%i", &semflg);
	(void) fprintf(stderr, "\nsemget: Calling semget(%#lx, %
				 %#o)\n",key, nsems, semflg);
	if ((semid = semget(key, nsems, semflg)) == -1) {
		perror("semget: semget failed");
		exit(1);
	} else {
		(void) fprintf(stderr, "semget: semget succeeded: semid = %d\n",
			semid);
		exit(0);
	}
}


Example A-9 Sample Program to Illustrate semctl(2)

/*
 * semctl.c:			Illustrate the semctl() function.
 *
 * This is a simple exerciser of the semctl() function. It lets you
 * perform one control operation on one semaphore set. It gives up
 * immediately if any control operation fails, so be careful not to
 * set permissions to preclude read permission; you won't be able to
 * reset the permissions with this code if you do.
 */

#include				<stdio.h>
#include				<sys/types.h>
#include				<sys/ipc.h>
#include				<sys/sem.h>
#include				<time.h>

struct semid_ds semid_ds;

static void		do_semctl();
static void 	do_stat();
extern char		*malloc();
extern void	 	exit();
extern void	 	perror();

char				warning_message[] = "If you remove read permission\
				for yourself, this program will fail frequently!";

main()
{
	union semun	arg;			/* union to pass to semctl() */
	int				cmd,			/* command to give to semctl() */
					i,				/* work area */
					semid,			/* semid to pass to semctl() */
					semnum;		/* semnum to pass to semctl() */

	(void) fprintf(stderr,
				"All numeric input must follow C conventions:\n");
	(void) fprintf(stderr,
				"\t0x... is interpreted as hexadecimal,\n");
	(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
	(void) fprintf(stderr, "\totherwise, decimal.\n");
	(void) fprintf(stderr, "Enter semid value: ");
	(void) scanf("%i", &semid);

	(void) fprintf(stderr, "Valid semctl cmd values are:\n");
	(void) fprintf(stderr, "\tGETALL = %d\n", GETALL);
	(void) fprintf(stderr, "\tGETNCNT = %d\n", GETNCNT);
	(void) fprintf(stderr, "\tGETPID = %d\n", GETPID);
	(void) fprintf(stderr, "\tGETVAL = %d\n", GETVAL);
	(void) fprintf(stderr, "\tGETZCNT = %d\n", GETZCNT);
	(void) fprintf(stderr, "\tIPC_RMID = %d\n", IPC_RMID);
	(void) fprintf(stderr, "\tIPC_SET = %d\n", IPC_SET);
	(void) fprintf(stderr, "\tIPC_STAT = %d\n", IPC_STAT);
	(void) fprintf(stderr, "\tSETALL = %d\n", SETALL);
	(void) fprintf(stderr, "\tSETVAL = %d\n", SETVAL);
	(void) fprintf(stderr, "\nEnter cmd: ");
	(void) scanf("%i", &cmd);
	
	/* Do some setup operations needed by multiple commands. */
	switch (cmd) {
		case GETVAL:
		case SETVAL:
		case GETNCNT:
		case GETZCNT:
			/* Get the semaphore number for these commands. */
			(void) fprintf(stderr, "\nEnter semnum value: ");
			(void) scanf("%i", &semnum);
			break;
		case GETALL:
		case SETALL:
			/* Allocate a buffer for the semaphore values. */
			(void) fprintf(stderr,
				"Get number of semaphores in the set.\n");
			arg.buf = &semid_ds;
			do_semctl(semid, 0, IPC_STAT, arg);
			if (arg.array =
				(ushort *)malloc((unsigned)
					(semid_ds.sem_nsems * sizeof(ushort)))) {
				/* Break out if you got what you needed. */
				break;
			}
			(void) fprintf(stderr,
				"semctl: unable to allocate space for %d values\n",
				semid_ds.sem_nsems);
			exit(2);
	}

	/* Get the rest of the arguments needed for the specified
	   command. */
	switch (cmd) {
		case SETVAL:
			/* Set value of one semaphore. */
			(void) fprintf(stderr, "\nEnter semaphore value: ");
			(void) scanf("%i", &arg.val);
			do_semctl(semid, semnum, SETVAL, arg);
			/* Fall through to verify the result. */
			(void) fprintf(stderr,
				"Do semctl GETVAL command to verify results.\n");
		case GETVAL:
			/* Get value of one semaphore. */
			arg.val = 0;
			do_semctl(semid, semnum, GETVAL, arg);
			break;
		case GETPID:
			/* Get PID of last process to successfully complete a
			   semctl(SETVAL), semctl(SETALL), or semop() on the
			   semaphore. */
			arg.val = 0;
			do_semctl(semid, 0, GETPID, arg);
			break;
		case GETNCNT:
			/* Get number of processes waiting for semaphore value to
			   increase. */
			arg.val = 0;
			do_semctl(semid, semnum, GETNCNT, arg);
			break;
		case GETZCNT:
			/* Get number of processes waiting for semaphore value to
			   become zero. */
			arg.val = 0;
			do_semctl(semid, semnum, GETZCNT, arg);
			break;
		case SETALL:
			/* Set the values of all semaphores in the set. */
			(void) fprintf(stderr,
							"There are %d semaphores in the set.\n",
							semid_ds.sem_nsems);
			(void) fprintf(stderr, "Enter semaphore values:\n");
			for (i = 0; i < semid_ds.sem_nsems; i++) {
				(void) fprintf(stderr, "Semaphore %d: ", i);
				(void) scanf("%hi", &arg.array[i]);
			}
			do_semctl(semid, 0, SETALL, arg);
			/* Fall through to verify the results. */
			(void) fprintf(stderr,
				"Do semctl GETALL command to verify results.\n");
		case GETALL:
			/* Get and print the values of all semaphores in the
			   set.*/
			do_semctl(semid, 0, GETALL, arg);
			(void) fprintf(stderr,
							"The values of the %d semaphores are:\n",
							semid_ds.sem_nsems);
			for (i = 0; i < semid_ds.sem_nsems; i++)
				(void) fprintf(stderr, "%d ", arg.array[i]);
			(void) fprintf(stderr, "\n");
			break;
		case IPC_SET:
			/* Modify mode and/or ownership. */
			arg.buf = &semid_ds;
			do_semctl(semid, 0, IPC_STAT, arg);
			(void) fprintf(stderr, "Status before IPC_SET:\n");
			do_stat();
			(void) fprintf(stderr, "Enter sem_perm.uid value: ");
			(void) scanf("%hi", &semid_ds.sem_perm.uid);
			(void) fprintf(stderr, "Enter sem_perm.gid value: ");
			(void) scanf("%hi", &semid_ds.sem_perm.gid);
			(void) fprintf(stderr, "%s\n", warning_message);
			(void) fprintf(stderr, "Enter sem_perm.mode value: ");
			(void) scanf("%hi", &semid_ds.sem_perm.mode);
			do_semctl(semid, 0, IPC_SET, arg);
			/* Fall through to verify changes. */
			(void) fprintf(stderr, "Status after IPC_SET:\n");
		case IPC_STAT:
			/* Get and print current status. */
			arg.buf = &semid_ds;
			do_semctl(semid, 0, IPC_STAT, arg);
			do_stat();
			break;
		case IPC_RMID:
			/* Remove the semaphore set. */
			arg.val = 0;
			do_semctl(semid, 0, IPC_RMID, arg);
			break;
		default:
			/* Pass unknown command to semctl. */
			arg.val = 0;
			do_semctl(semid, 0, cmd, arg);
			break;
	}
	exit(0);
}

/*
 * Print indication of arguments being passed to semctl(), call
 * semctl(), and report the results. If semctl() fails, do not
 * return; this example doesn't deal with errors, it just reports 
 * them.
 */
static void
do_semctl(semid, semnum, cmd, arg)
union semun	 arg;
int		cmd,
		semid,
		semnum;
{
	register int						i;			/* work area */

	void) fprintf(stderr, "\nsemctl: Calling semctl(%d, %d, %d,",
				semid, semnum, cmd);
	switch (cmd) {
		case GETALL:
			(void) fprintf(stderr, "arg.array = %#x)\n",
							arg.array);
			break;
		case IPC_STAT:
		case IPC_SET:
			(void) fprintf(stderr, "arg.buf = %#x)\n", arg.buf);
			break;
		case SETALL:
			(void) fprintf(stderr, "arg.array = [", arg.buf);
			for (i = 0;i < semid_ds.sem_nsems;) {
				(void) fprintf(stderr, "%d", arg.array[i++]);
				if (i < semid_ds.sem_nsems)
						(void) fprintf(stderr, ", ");
			}
			(void) fprintf(stderr, "])\n");
			break;
		case SETVAL:
		default:
			(void) fprintf(stderr, "arg.val = %d)\n", arg.val);
			break;
	}
	i = semctl(semid, semnum, cmd, arg);
	if (i == -1) {
		perror("semctl: semctl failed");
		exit(1);
	}
	(void) fprintf(stderr, "semctl: semctl returned %d\n", i);
	return;
}

/*
 *	Display contents of commonly used pieces of the status
structure.
 */
static void
do_stat()
{
	(void) fprintf(stderr, "sem_perm.uid = %d\n",
							semid_ds.sem_perm.uid);
	(void) fprintf(stderr, "sem_perm.gid = %d\n",
							semid_ds.sem_perm.gid);
	(void) fprintf(stderr, "sem_perm.cuid = %d\n",
							semid_ds.sem_perm.cuid);
	(void) fprintf(stderr, "sem_perm.cgid = %d\n",
							semid_ds.sem_perm.cgid);
	(void) fprintf(stderr, "sem_perm.mode = %#o, ",
							semid_ds.sem_perm.mode);
	(void) fprintf(stderr, "access permissions = %#o\n",
							semid_ds.sem_perm.mode & 0777);
	(void) fprintf(stderr, "sem_nsems = %d\n",
semid_ds.sem_nsems);
	(void) fprintf(stderr, "sem_otime = %s", semid_ds.sem_otime ?
						ctime(&semid_ds.sem_otime) : "Not Set\n");
	(void) fprintf(stderr, "sem_ctime = %s",
							ctime(&semid_ds.sem_ctime));
}


Example A-10 Sample Program to Illustrate semop(2)

/*
 * semop.c: Illustrate the semop() function.
 *
 * This is a simple exerciser of the semop() function. It lets you
 * to set up arguments for semop() and make the call. It then reports
 * the results repeatedly on one semaphore set. You must have read
 * permission on the semaphore set or this exerciser will fail. (It
 * needs read permission to get the number of semaphores in the set
 * and to report the values before and after calls to semop().)
 */

#include				<stdio.h>
#include				<sys/types.h>
#include				<sys/ipc.h>
#include				<sys/sem.h>

static int					 ask();
extern void		 		exit();
extern void				 free();
extern char				 *malloc();
extern void		 		perror();

static struct semid_ds		semid_ds;												/* status of semaphore set */

static char				error_mesg1[] = "semop: Can't allocate space for %d\
								semaphore values. Giving up.\n";
static char			error_mesg2[] = "semop: Can't allocate space for %d\
								sembuf structures. Giving up.\n";

main()
{
	register int				i;			/* work area */
	int				nsops;			/* number of operations to do */
	int				semid;			/* semid of semaphore set */
	struct sembuf				*sops;			/* ptr to operations to perform */

	(void) fprintf(stderr,
				"All numeric input must follow C conventions:\n");
	(void) fprintf(stderr,
				"\t0x... is interpreted as hexadecimal,\n");
	(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
	(void) fprintf(stderr, "\totherwise, decimal.\n");
	/* Loop until the invoker doesn't want to do anymore. */
	while (nsops = ask(&semid, &sops)) {
		/* Initialize the array of operations to be performed.*/
		for (i = 0; i < nsops; i++) {
			(void) fprintf(stderr,
					"\nEnter values for operation %d of %d.\n",
							i + 1, nsops);
			(void) fprintf(stderr,
					"sem_num(valid values are 0 <= sem_num < %d): ",
					semid_ds.sem_nsems);
			(void) scanf("%hi", &sops[i].sem_num);
			(void) fprintf(stderr, "sem_op: ");
			(void) scanf("%hi", &sops[i].sem_op);
			(void) fprintf(stderr,
					"Expected flags in sem_flg are:\n");
			(void) fprintf(stderr, "\tIPC_NOWAIT =\t%#6.6o\n",
					IPC_NOWAIT);
			(void) fprintf(stderr, "\tSEM_UNDO =\t%#6.6o\n",
					SEM_UNDO);
			(void) fprintf(stderr, "sem_flg: ");
			(void) scanf("%hi", &sops[i].sem_flg);
		}

		/* Recap the call to be made. */
		(void) fprintf(stderr,
					"\nsemop: Calling semop(%d, &sops, %d) with:",
					semid, nsops);
		for (i = 0; i < nsops; i++)
		{
			(void) fprintf(stderr, "\nsops[%d].sem_num = %d, ", i,
						sops[i].sem_num);
			(void) fprintf(stderr, "sem_op = %d, ", sops[i].sem_op);
			(void) fprintf(stderr, "sem_flg = %#o\n",
						sops[i].sem_flg);
		}

		/* Make the semop() call and report the results. */
		if ((i = semop(semid, sops, nsops)) == -1) {
			perror("semop: semop failed");
		} else {
			(void) fprintf(stderr, "semop: semop returned %d\n", i);
		}
	}
}

/*
 * Ask if user wants to continue.
 *
 * On the first call:
 *	Get the semid to be processed and supply it to the caller.
 * On each call:
 * 	1. Print current semaphore values.
 * 	2. Ask user how many operations are to be performed on the next
 * 	   call to semop. Allocate an array of sembuf structures
 * 	   sufficient for the job and set caller-supplied pointer to
that
 * 	   array. (The array is reused on subsequent calls if it is big
 * 	   enough. If it isn't, it is freed and a larger array is
 * 	   allocated.)
 */
static
ask(semidp, sopsp)
int						*semidp;		/* pointer to semid (used only the first time) */
struct	sembuf			**sopsp;
{
	static union semun	arg;			/* argument to semctl */
	int						i;				/* work area */
	static int			nsops = 0;	/* size of currently allocated sembuf array */
	static int			semid = -1;	/* semid supplied by user */
	static struct sembuf *sops;	 	/* pointer to allocated array */
	
	if (semid < 0) {
		/* First call; get semid from user and the current state of
		   the semaphore set. */
		(void) fprintf(stderr,
				"Enter semid of the semaphore set you want to use: ");
		(void) scanf("%i", &semid);
		*semidp = semid;
		arg.buf = &semid_ds;
		if (semctl(semid, 0, IPC_STAT, arg) == -1) {
			perror("semop: semctl(IPC_STAT) failed");
			/* Note that if semctl fails, semid_ds remains filled
			   with zeros, so later test for number of semaphores will
			   be zero. */
			(void) fprintf(stderr,
					"Before and after values are not printed.\n");
		} else {
			if ((arg.array = (ushort *)malloc(
				(unsigned)(sizeof(ushort) * semid_ds.sem_nsems)))
							== NULL) {
				(void) fprintf(stderr, error_mesg1,
							semid_ds.sem_nsems);
				exit(1);
			}
		}
	}
	/* Print current semaphore values. */
	if (semid_ds.sem_nsems) {
		(void) fprintf(stderr,
						"There are %d semaphores in the set.\n",
						semid_ds.sem_nsems);
		if (semctl(semid, 0, GETALL, arg) == -1) {
			perror("semop: semctl(GETALL) failed");
		} else {
			(void) fprintf(stderr, "Current semaphore values are:");
			for (i = 0; i < semid_ds.sem_nsems;
				(void) fprintf(stderr, " %d", arg.array[i++]));
			(void) fprintf(stderr, "\n");
		}
	}
	/* Find out how many operations are going to be done in the
next
	   call and allocate enough space to do it. */
	(void) fprintf(stderr,
					"How many semaphore operations do you want %s\n",
					"on the next call to semop()?");
	(void) fprintf(stderr, "Enter 0 or control-D to quit: ");
	i = 0;
	if (scanf("%i", &i) == EOF || i == 0)
		exit(0);
	if (i > nsops) {
		if (nsops)
			free((char *)sops);
		nsops = i;
		if ((sops = (struct sembuf *)malloc((unsigned)(nsops *
			sizeof(struct sembuf)))) == NULL) {
			(void) fprintf(stderr, error_mesg2, nsops);
			exit(2);
		}
	}
	*sopsp = sops;
	return (i);
}


Example A-11 Sample Program to Illustrate shmget(2)

/*
 * shmget.c: Illustrate the shmget() function.
 *
 *	This is a simple exerciser of the shmget() function. It
prompts
 * for the arguments, makes the call, and reports the results.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

extern void				exit();
extern void				perror();

main()
{
	key_t	key;			/* key to be passed to shmget() */
	int		shmflg;		/* shmflg to be passed to shmget() */
	int		shmid;			/* return value from shmget() */
	int		size;			/* size to be passed to shmget() */

	(void) fprintf(stderr,
		"All numeric input is expected to follow C conventions:\n");
	(void) fprintf(stderr,
				"\t0x... is interpreted as hexadecimal,\n");
	(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
	(void) fprintf(stderr, "\totherwise, decimal.\n");

	/* Get the key. */
	(void) fprintf(stderr, "IPC_PRIVATE == %#lx\n", IPC_PRIVATE);
	(void) fprintf(stderr, "Enter key: ");
	(void) scanf("%li", &key);

	/* Get the size of the segment. */
	(void) fprintf(stderr, "Enter size: ");
	(void) scanf("%i", &size);

	/* Get the shmflg value. */
	(void) fprintf(stderr,
				"Expected flags for the shmflg argument are:\n");
	(void) fprintf(stderr, "\tIPC_CREAT = \t%#8.8o\n",
IPC_CREAT);
	(void) fprintf(stderr, "\tIPC_EXCL = \t%#8.8o\n", IPC_EXCL);
	(void) fprintf(stderr, "\towner read =\t%#8.8o\n", 0400);
	(void) fprintf(stderr, "\towner write =\t%#8.8o\n", 0200);
	(void) fprintf(stderr, "\tgroup read =\t%#8.8o\n", 040);
	(void) fprintf(stderr, "\tgroup write =\t%#8.8o\n", 020);
	(void) fprintf(stderr, "\tother read =\t%#8.8o\n", 04);
	(void) fprintf(stderr, "\tother write =\t%#8.8o\n", 02);
	(void) fprintf(stderr, "Enter shmflg: ");
	(void) scanf("%i", &shmflg);

	/* Make the call and report the results. */
	(void) fprintf(stderr,
					"shmget: Calling shmget(%#lx, %d, %#o)\n",
					key, size, shmflg);
	if ((shmid = shmget (key, size, shmflg)) == -1) {
		perror("shmget: shmget failed");
		exit(1);
	} else {
		(void) fprintf(stderr,
					"shmget: shmget returned %d\n", shmid);
		exit(0);
	}
}


Example A-12 Sample Program to Illustrate shmctl(2)

/*
 * shmctl.c: Illustrate the shmctl() function.
 *
 * This is a simple exerciser of the shmctl() function. It lets you
 * to perform one control operation on one shared memory segment.
 * (Some operations are done for the user whether requested or
not.
 * It gives up immediately if any control operation fails. Be
careful
 * not to set permissions to preclude read permission; you won't
be
 *able to reset the permissions with this code if you do.)
*/

#include				<stdio.h>
#include				<sys/types.h>
#include				<sys/ipc.h>
#include				<sys/shm.h>
#include				<time.h>

static void			do_shmctl();
extern void			exit();
extern void			perror();

main()
{
	int					cmd;		/* command code for shmctl() */
	int					shmid;		/* segment ID */
	struct	shmid_ds	shmid_ds;	/* shared memory data structure to
										hold results */

	(void) fprintf(stderr,
		"All numeric input is expected to follow C conventions:\n");
	(void) fprintf(stderr,
					"\t0x... is interpreted as hexadecimal,\n");
	(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
	(void) fprintf(stderr, "\totherwise, decimal.\n");

	/* Get shmid and cmd. */
	(void) fprintf(stderr,
					"Enter the shmid for the desired segment: ");
	(void) scanf("%i", &shmid);
	(void) fprintf(stderr, "Valid shmctl cmd values are:\n");
	(void) fprintf(stderr, "\tIPC_RMID =\t%d\n", IPC_RMID);
	(void) fprintf(stderr, "\tIPC_SET =\t%d\n", IPC_SET);
	(void) fprintf(stderr, "\tIPC_STAT =\t%d\n", IPC_STAT);
	(void) fprintf(stderr, "\tSHM_LOCK =\t%d\n", SHM_LOCK);
	(void) fprintf(stderr, "\tSHM_UNLOCK =\t%d\n", SHM_UNLOCK);
	(void) fprintf(stderr, "Enter the desired cmd value: ");
	(void) scanf("%i", &cmd);

	switch (cmd) {
		case IPC_STAT:
			/* Get shared memory segment status. */
			break;
		case IPC_SET:
			/* Set owner UID and GID and permissions. */
			/* Get and print current values. */
			do_shmctl(shmid, IPC_STAT, &shmid_ds);
			/* Set UID, GID, and permissions to be loaded. */
			(void) fprintf(stderr, "\nEnter shm_perm.uid: ");
			(void) scanf("%hi", &shmid_ds.shm_perm.uid);
			(void) fprintf(stderr, "Enter shm_perm.gid: ");
			(void) scanf("%hi", &shmid_ds.shm_perm.gid);
			(void) fprintf(stderr,
				"Note: Keep read permission for yourself.\n");
			(void) fprintf(stderr, "Enter shm_perm.mode: ");
			(void) scanf("%hi", &shmid_ds.shm_perm.mode);
			break;
		case IPC_RMID:
			/* Remove the segment when the last attach point is
			   detached. */
			break;
		case SHM_LOCK:
			/* Lock the shared memory segment. */
			break;
		case SHM_UNLOCK:
			/* Unlock the shared memory segment. */
			break;
		default:
			/* Unknown command will be passed to shmctl. */
			break;
	}
	do_shmctl(shmid, cmd, &shmid_ds);
	exit(0);
}

/*
 * Display the arguments being passed to shmctl(), call shmctl(),
 * and report the results. If shmctl() fails, do not return; this
 * example doesn't deal with errors, it just reports them.
 */
static void
do_shmctl(shmid, cmd, buf)
int				shmid,			/* attach point */
					cmd;			/* command code */
struct shmid_ds *buf;			/* pointer to shared memory data structure */
{
	register int	rtrn;			/* hold area */

	(void) fprintf(stderr, "shmctl: Calling shmctl(%d, %d, buf)\n",
		shmid, cmd);
	if (cmd == IPC_SET) {
		(void) fprintf(stderr, "\tbuf->shm_perm.uid == %d\n",
					buf->shm_perm.uid);
		(void) fprintf(stderr, "\tbuf->shm_perm.gid == %d\n",
					buf->shm_perm.gid);
		(void) fprintf(stderr, "\tbuf->shm_perm.mode == %#o\n",
					buf->shm_perm.mode);
	}
	if ((rtrn = shmctl(shmid, cmd, buf)) == -1) {
		perror("shmctl: shmctl failed");
		exit(1);
	} else {
		(void) fprintf(stderr,
						"shmctl: shmctl returned %d\n", rtrn);
	}
	if (cmd != IPC_STAT && cmd != IPC_SET)
		return;

	/* Print the current status. */
	(void) fprintf(stderr, "\nCurrent status:\n");
	(void) fprintf(stderr, "\tshm_perm.uid = %d\n",
						buf->shm_perm.uid);
	(void) fprintf(stderr, "\tshm_perm.gid = %d\n",
						buf->shm_perm.gid);
	(void) fprintf(stderr, "\tshm_perm.cuid = %d\n",
						buf->shm_perm.cuid);
	(void) fprintf(stderr, "\tshm_perm.cgid = %d\n",
						buf->shm_perm.cgid);
	(void) fprintf(stderr, "\tshm_perm.mode = %#o\n",
						buf->shm_perm.mode);
	(void) fprintf(stderr, "\tshm_perm.key = %#x\n",
						buf->shm_perm.key);
	(void) fprintf(stderr, "\tshm_segsz = %d\n", buf->shm_segsz);
	(void) fprintf(stderr, "\tshm_lpid = %d\n", buf->shm_lpid);
	(void) fprintf(stderr, "\tshm_cpid = %d\n", buf->shm_cpid);
	(void) fprintf(stderr, "\tshm_nattch = %d\n", buf->shm_nattch);
	(void) fprintf(stderr, "\tshm_atime = %s",
			buf->shm_atime ? ctime(&buf->shm_atime) : "Not Set\n");
	(void) fprintf(stderr, "\tshm_dtime = %s",
			buf->shm_dtime ? ctime(&buf->shm_dtime) : "Not Set\n");
	(void) fprintf(stderr, "\tshm_ctime = %s",
						ctime(&buf->shm_ctime));
} 


Example A-13 Sample Program to Illustrate shmat(2) and shmdt(2)

/*
 * shmop.c: Illustrate the shmat() and shmdt() functions.
 *
 * This is a simple exerciser for the shmat() and shmdt() system
 * calls. It allows you to attach and detach segments and to
 * write strings into and read strings from attached segments.
 */

#include			<stdio.h>
#include			<setjmp.h>
#include			<signal.h>
#include			<sys/types.h>
#include			<sys/ipc.h>
#include			<sys/shm.h>

#define			MAXnap		4	/* Maximum number of concurrent attaches. */

static			ask();
static void		catcher();
extern void		exit();
static			good_addr();
extern void		perror();
extern char		*shmat();

static struct state	{	/* Internal record of currently attached segments. */
	int		shmid;				/* shmid of attached segment */
	char	*shmaddr;			/* attach point */
	int		shmflg;			/* flags used on attach */
}	ap[MAXnap];				/* State of current attached segments. */

static int		nap;		/* Number of currently attached segments. */
static jmp_buf	segvbuf;	/* Process state save area for SIGSEGV catching. */

main()
{
	register int			action;		/* action to be performed */
	char					*addr;			/* address work area */
	register int			i;				/* work area */
	register struct state	*p;			/* ptr to current state entry */
	void					(*savefunc)(); /* SIGSEGV state hold area */
	(void) fprintf(stderr,
		"All numeric input is expected to follow C conventions:\n");
	(void) fprintf(stderr,
		"\t0x... is interpreted as hexadecimal,\n");
	(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
	(void) fprintf(stderr, "\totherwise, decimal.\n");
	while (action = ask()) {
		if (nap) {
			(void) fprintf(stderr,
						"\nCurrently attached segment(s):\n");
			(void) fprintf(stderr, " shmid address\n");
			(void) fprintf(stderr, "------ ----------\n");
			p = &ap[nap];
			while (p-- != ap) {
				(void) fprintf(stderr, "%6d", p->shmid);
				(void) fprintf(stderr, "%#11x", p->shmaddr);
				(void) fprintf(stderr, " Read%s\n",
					(p->shmflg & SHM_RDONLY) ?
					"-Only" : "/Write");
			}
		} else
			(void) fprintf(stderr,
				"\nNo segments are currently attached.\n");
		switch (action) {
		case 1:			/* Shmat requested. */
			/* Verify that there is space for another attach. */
			if (nap == MAXnap) {
				(void) fprintf(stderr, "%s %d %s\n",
							"This simple example will only allow",
							MAXnap, "attached segments.");
				break;
			}
			p = &ap[nap++];
			/* Get the arguments, make the call, report the
				results, and update the current state array. */
			(void) fprintf(stderr,
				"Enter shmid of segment to attach: ");
			(void) scanf("%i", &p->shmid);

			(void) fprintf(stderr, "Enter shmaddr: ");
			(void) scanf("%i", &p->shmaddr);
			(void) fprintf(stderr,
				"Meaningful shmflg values are:\n");
			(void) fprintf(stderr, "\tSHM_RDONLY = \t%#8.8o\n",
				SHM_RDONLY);
			(void) fprintf(stderr, "\tSHM_RND = \t%#8.8o\n",
				SHM_RND);
			(void) fprintf(stderr, "Enter shmflg value: ");
			(void) scanf("%i", &p->shmflg);

			(void) fprintf(stderr,
				"shmop: Calling shmat(%d, %#x, %#o)\n",
				p->shmid, p->shmaddr, p->shmflg);
			p->shmaddr = shmat(p->shmid, p->shmaddr, p->shmflg);
			if(p->shmaddr == (char *)-1) {
				perror("shmop: shmat failed");
				nap--;
			} else {
				(void) fprintf(stderr,
					"shmop: shmat returned %#8.8x\n",
					p->shmaddr);
			}
			break;

		case 2:			/* Shmdt requested. */
			/* Get the address, make the call, report the results,
				and make the internal state match. */
			(void) fprintf(stderr,
				"Enter detach shmaddr: ");
			(void) scanf("%i", &addr);

			i = shmdt(addr);
			if(i == -1) {
				perror("shmop: shmdt failed");
			} else {
				(void) fprintf(stderr,
					"shmop: shmdt returned %d\n", i);
				for (p = ap, i = nap; i--; p++) {
					if (p->shmaddr == addr)
						*p = ap[--nap];
				}
			}
			break;
		case 3:	/* Read from segment requested. */
			if (nap == 0)
				break;

			(void) fprintf(stderr, "Enter address of an %s",
				"attached segment: ");
			(void) scanf("%i", &addr);

			if (good_addr(addr))
				(void) fprintf(stderr, "String @ %#x is `%s'\n",
					addr, addr);
			break;

		case 4:	/* Write to segment requested. */
			if (nap == 0)
				break;

			(void) fprintf(stderr, "Enter address of an %s",
				"attached segment: ");
			(void) scanf("%i", &addr);

			/* Set up SIGSEGV catch routine to trap attempts to
				write into a read-only attached segment. */
			savefunc = signal(SIGSEGV, catcher);

			if (setjmp(segvbuf)) {
				(void) fprintf(stderr, "shmop: %s: %s\n",
					"SIGSEGV signal caught",
					"Write aborted.");
			} else {
				if (good_addr(addr)) {
					(void) fflush(stdin);
					(void) fprintf(stderr, "%s %s %#x:\n",
						"Enter one line to be copied",
						"to shared segment attached @",
						addr);
					(void) gets(addr);
				}
			}
			(void) fflush(stdin);

			/* Restore SIGSEGV to previous condition. */
			(void) signal(SIGSEGV, savefunc);
			break;
		}
	}
	exit(0);
	/*NOTREACHED*/
}
/*
**	Ask for next action.
*/
static
ask()
{
	int		response;			/* user response */
	do {
			(void) fprintf(stderr, "Your options are:\n");
			(void) fprintf(stderr, "\t^D = exit\n");
			(void) fprintf(stderr, "\t 0 = exit\n");
			(void) fprintf(stderr, "\t 1 = shmat\n");
			(void) fprintf(stderr, "\t 2 = shmdt\n");
			(void) fprintf(stderr, "\t 3 = read from segment\n");
			(void) fprintf(stderr, "\t 4 = write to segment\n");
			(void) fprintf(stderr,
				"Enter the number corresponding to your choice: ");

			/* Preset response so "^D" will be interpreted as exit. */
			response = 0;
			(void) scanf("%i", &response);
	} while (response < 0 || response > 4);
	return (response);
}
/*
** Catch signal caused by attempt to write into shared memory
segment
** attached with SHM_RDONLY flag set.
*/
/*ARGSUSED*/
static void
catcher(sig)
{
	longjmp(segvbuf, 1);
	/*NOTREACHED*/
}
/*
** Verify that given address is the address of an attached
segment.
** Return 1 if address is valid; 0 if not.
*/
static
good_addr(address)
char	*address;
{
	register struct state										*p;			/* ptr to state of attached
segment */

	for (p = ap; p != &ap[nap]; p++)
			if (p->shmaddr == address)
				return(1);
	return(0);
}

The next example demonstrates how to insert an entry into a doubly linked list that is stored in a file of list element records. For the example, assume that the record after which the new record is to be inserted already has a read lock on it. The lock on this record must be changed or promoted to a write lock so that the record can be edited.

Promoting a lock (generally from read lock to write lock) is permitted if no other process is holding a read lock in the same section of the file. When processes with pending write locks are sleeping on the same section of the file, the lock promotion succeeds and the other (sleeping) locks wait. Changing a write lock to a read lock carries no restrictions. In either case, the lock is merely reset with the new lock type. Because the lockf(3C) function does not have read locks, lock promotion does not apply to that call.

The locks on these three records were all set to wait (sleep) if another process was blocking them from being set. This was done with the F_SETLKW command. If the F_SETLK command were used instead, the fcntl(2) functions would fail if blocked. The program would then have to be changed to handle the blocked condition in each of the error-return sections.


Example A-14 Example of Record Locking With Lock Promotion

	struct record {
	...					/* data portion of record */
	off_t prev;		/* index to previous record in the list */
	off_t next;		/* index to next record in the list */
};

/* Lock promotion using fcntl(2): When this routine is entered it is
 * assumed that there are read locks on "here" and "next." If write
 * locks on "here" and "next" are obtained;
 *	    Set a write lock on "this."
 *	    Return index to "this" record.
 * If any write lock is not obtained;
 *	    Restore read locks on "here" and "next."
 *	    Remove all other locks.
 *	    Return a -1.
 */

off_t
set3lock (this, here, next)
off_t this, here, next;
{
	struct flock lck;
	lck.l_type = F_WRLCK;	/* setting a write lock */
	lck.l_whence = 0;	/* offset l_start from beginning of file */
	lck.l_start = here;
	lck.l_len = sizeof(struct record);

	/* promote lock on "here" to write lock */
	if (fcntl(fd, F_SETLKW, &lck) < 0) {
		    return (-1);
	}
	/* lock "this" with write lock */
	lck.l_start = this;
	if (fcntl(fd, F_SETLKW, &lck) < 0) {
		/* "this" lock failed; demote "here" lock to read lock. */
		lck.l_type = F_RDLCK;
		lck.l_start = here;
		(void) fcntl(fd, F_SETLKW, &lck);
		return (-1);
	/* promote lock on "next" to write lock */
	lck.l_start = next;
	if (fcntl(fd, F_SETLKW, &lck) < 0) {
		/* "next" lock failed; demote lock on "here" to read lock, */
		lck.l_type = F_RDLCK;
		lck.l_start = here;
		(void) fcntl(fd, F_SETLK, &lck);
		/* and remove lock on "this". */
		lck.l_type = F_UNLCK;
		lck.l_start = this;
		(void) fcntl(fd, F_SETLK, &lck);
		return (-1);	/* cannot set lock, try again or quit */
	}

	return (this);
}


Example A-15 Record Write Locks With lockf(3C)

/* lockf(3C)
 * When this routine is entered, it is assumed that there are no
 * locks on "here" and "next". If locks are obtained: set a lock
 * on "this"; return index to "this" record. If any lock is not
 * obtained: remove all other locks; return a -1.
 */
#include <unistd.h>

long
set3lock (this, here, next)
long this, here, next;
{
	/* lock "here" */
	(void) lseek(fd, here, 0);
	if (lockf(fd, F_LOCK, sizeof(struct record)) < 0) {
		return (-1);
	}
	/* lock "this" */
	(void) lseek(fd, this, SEEK_SET);
	if (lockf(fd, F_LOCK, sizeof(struct record)) < 0) {
		/* Lock on "this" failed. Clear lock on "here". */
		(void) lseek(fd, here, 0);
		(void) lockf(fd, F_ULOCK, sizeof(struct record));
		return (-1);
	}
	/* lock "next" */
	(void) lseek(fd, next, 0);
	if (lockf(fd, F_LOCK, sizeof(struct record)) < 0) {
		/* Lock on "next" failed. Clear lock on "here". */
		(void) lseek(fd, here, 0);
		(void) lockf(fd, F_ULOCK, sizeof(struct record));
		/* and remove lock on "this". */
		(void) lseek(fd, this, 0);
		(void) lockf(fd, F_ULOCK, sizeof(struct record));
		return (-1);				/* cannot set lock, try again or quit */
	}
	return (this);
}