/*
** 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);
}
