[alt.sources] advise

ag@cbmvax.commodore.com (Keith Gabryelski) (10/17/90)

The included source code includes

	advise.c		# a user program to interact with
				# the advise device and module.

	advisedev.c		# the advise device driver.
				# (requests to attach to a users terminal
				#  are done through this device)

	advisemod.c		# the advise module.
				# (this module is pushed onto the advisee's
				#  tty stream so advisedev may attach to
				#  it.)

	advisemod.h		# useful header file.

	COPYING Makefile	# Other files.

Pax, Keith

Ps, This will only work under System V Release 4.0 streams ttys.

    With little effort it could be made to work under other streams
    tty subsystems.

    No amount of effort (save re-thinking and re-implimenting) will
    make this thing work on non-streams based ttys.

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	COPYING
#	Makefile
#	advise.c
#	advise.man
#	advisedev.c
#	advisemod.c
#	advisemod.h
# This archive created: Tue Oct 16 19:15:42 1990
export PATH; PATH=/bin:$PATH
if test -f 'COPYING'
then
	echo shar: will not over-write existing file "'COPYING'"
else
cat << \SHAR_EOF > 'COPYING'

		    Advise GENERAL PUBLIC LICENSE
		    (Clarified 11 Feb 1988)

 Copyright (C) 1988 Free Software Foundation, Inc.
 Everyone is permitted to copy and distribute verbatim copies
 of this license, but changing it is not allowed.  You can also
 use this wording to make the terms for other programs.

  The license agreements of most software companies keep you at the
mercy of those companies.  By contrast, our general public license is
intended to give everyone the right to share Advise.  To make sure that
you get the rights we want you to have, we need to make restrictions
that forbid anyone to deny you these rights or to ask you to surrender
the rights.  Hence this license agreement.

  Specifically, we want to make sure that you have the right to give
away copies of Advise, that you receive source code or else can get it
if you want it, that you can change Advise or use pieces of it in new
free programs, and that you know you can do these things.

  To make sure that everyone has such rights, we have to forbid you to
deprive anyone else of these rights.  For example, if you distribute
copies of Advise, you must give the recipients all the rights that you
have.  You must make sure that they, too, receive or can get the
source code.  And you must tell them their rights.

  Also, for our own protection, we must make certain that everyone
finds out that there is no warranty for Advise.  If Advise is modified by
someone else and passed on, we want its recipients to know that what
they have is not what we distributed, so that any problems introduced
by others will not reflect on our reputation.

  Therefore we (Richard Stallman and the Free Software Foundation,
Inc.) make the following terms which say what you must do to be
allowed to distribute or change Advise.


			COPYING POLICIES

  1. You may copy and distribute verbatim copies of Advise source code
as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy a valid copyright notice "Copyright
(C) 1988 Free Software Foundation, Inc." (or with whatever year is
appropriate); keep intact the notices on all files that refer to this
License Agreement and to the absence of any warranty; and give any
other recipients of the Advise program a copy of this License
Agreement along with the program.  You may charge a distribution fee
for the physical act of transferring a copy.

  2. You may modify your copy or copies of Advise or any portion of it,
and copy and distribute such modifications under the terms of
Paragraph 1 above, provided that you also do the following:

    a) cause the modified files to carry prominent notices stating
    that you changed the files and the date of any change; and

    b) cause the whole of any work that you distribute or publish,
    that in whole or in part contains or is a derivative of Advise or
    any part thereof, to be licensed at no charge to all third
    parties on terms identical to those contained in this License
    Agreement (except that you may choose to grant more extensive
    warranty protection to some or all third parties, at your option).

    c) You may charge a distribution fee for the physical act of
    transferring a copy, and you may at your option offer warranty
    protection in exchange for a fee.

Mere aggregation of another unrelated program with this program (or its
derivative) on a volume of a storage or distribution medium does not bring
the other program under the scope of these terms.

  3. You may copy and distribute Advise (or a portion or derivative of it,
under Paragraph 2) in object code or executable form under the terms of
Paragraphs 1 and 2 above provided that you also do one of the following:

    a) accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of
    Paragraphs 1 and 2 above; or,

    b) accompany it with a written offer, valid for at least three
    years, to give any third party free (except for a nominal
    shipping charge) a complete machine-readable copy of the
    corresponding source code, to be distributed under the terms of
    Paragraphs 1 and 2 above; or,

    c) accompany it with the information you received as to where the
    corresponding source code may be obtained.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form alone.)

For an executable file, complete source code means all the source code for
all modules it contains; but, as a special exception, it need not include
source code for modules which are standard libraries that accompany the
operating system on which the executable file runs.

  4. You may not copy, sublicense, distribute or transfer Advise
except as expressly provided under this License Agreement.  Any attempt
otherwise to copy, sublicense, distribute or transfer Advise is void and
your rights to use the program under this License agreement shall be
automatically terminated.  However, parties who have received computer
software programs from you with this License Agreement will not have
their licenses terminated so long as such parties remain in full compliance.

  5. If you wish to incorporate parts of Advise into other free programs
whose distribution conditions are different, write to the Free Software
Foundation at 675 Mass Ave, Cambridge, MA 02139.  We have not yet worked
out a simple rule that can be stated here, but we will often permit this.
We will be guided by the two goals of preserving the free status of all
derivatives of our free software and of promoting the sharing and reuse of
software.

Your comments and suggestions about our licensing policies and our
software are welcome!  Please contact the Free Software Foundation, Inc.,
675 Mass Ave, Cambridge, MA 02139, or call (617) 876-3296.

		       NO WARRANTY

  BECAUSE ADVISE IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY NO
WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE ADVISE "AS IS" WITHOUT
WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND
PERFORMANCE OF ADVISE IS WITH YOU.  SHOULD ADVISE PROVE DEFECTIVE, YOU
ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
WHO MAY MODIFY AND REDISTRIBUTE GNU SEND AS PERMITTED ABOVE, BE LIABLE TO
YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER
SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A
FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) GNU SEND, EVEN
IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR
ANY CLAIM BY ANY OTHER PARTY.
SHAR_EOF
fi # end of overwriting check
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
# Copyright (C) 1990 Keith Gabryelski (ag@amix.commodore.com)
#
# This file is part of advise.
#
# advise is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY.  No author or distributor
# accepts responsibility to anyone for the consequences of using it
# or for whether it serves any particular purpose or works at all,
# unless he says so in writing.  Refer to the advise General Public
# License for full details.
# 
# Everyone is granted permission to copy, modify and redistribute
# advise, but only under the conditions described in the
# advise General Public License.   A copy of this license is
# supposed to have been given to you along with advise so you
# can know your rights and responsibilities.  It should be in a
# file named COPYING.  Among other things, the copyright notice
# and this notice must be preserved on all copies.  */
# 
# Author:	Keith Gabryelski	(ag@amix.commodore.com)
#

CC=gcc
CFLAGS=-O

all: advise

advise.o: advisemod.h
SHAR_EOF
fi # end of overwriting check
if test -f 'advise.c'
then
	echo shar: will not over-write existing file "'advise.c'"
else
cat << \SHAR_EOF > 'advise.c'
/* Copyright (C) 1990 Keith Gabryelski (ag@amix.commodore.com)

This file is part of advise.

advise is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.  No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing.  Refer to the advise General Public
License for full details.

Everyone is granted permission to copy, modify and redistribute
advise, but only under the conditions described in the
advise General Public License.   A copy of this license is
supposed to have been given to you along with advise so you
can know your rights and responsibilities.  It should be in a
file named COPYING.  Among other things, the copyright notice
and this notice must be preserved on all copies.  */

/*
** Author:	Keith Gabryelski	(ag@amix.commodore.com)
*/

#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stropts.h>
#include <poll.h>
#include <sys/stream.h>
#include <errno.h>
#include <utmp.h>
#include <pwd.h>
#include <termios.h>
#include <string.h>
#include <ctype.h>
#include "advisemod.h"

extern char *optarg;

#define max(a,b) ((a)>(b)?(a):(b))

static struct module_list
{
    char *name;			/* name of module */
    struct module_list *next;	/* next module to be pushed */
} advise_module_list =
{
    "advise", NULL,
};

static void usage(void), advise(char *);
static char *strchar(char);
static struct module_list *list_modules(int, char *);

static int allow_deny_p, allow_deny;
static int allow_advise_p, allow_advise;
static int error_flag;
static int secret, spy;
static int meta_character = '~';
static char *progname;
static char *module = "ldterm";

int
main(int argc, char **argv)
{
    int c, error = 0;
    struct termios termios;
    struct module_list *modules;

    progname = *argv;

    while((c = getopt(argc, argv, "ADM:Sadm:s?")) != EOF)
    {
	switch(c)
	{
	case 's':
	    spy++;
	    break;

	case 'S':
	    if (!getuid())
		secret++;
	    break;

	case 'a':
	    allow_deny_p++;
	    allow_deny=ADVISE_ALLOW;
	    allow_advise_p++;
	    allow_advise++;
	    break;

	case 'd':
	    allow_advise_p++;
	    allow_advise=0;
	    break;

	case 'A':
	    allow_deny_p++;
	    allow_deny=ADVISE_ALLOW;
	    break;

	case 'D':
	    allow_deny_p++;
	    allow_deny=ADVISE_DENY;
	    break;

	case 'm':
	    meta_character = optarg[0];
	    break;

	case 'M':
	    module = optarg;
	    break;

	case '?':
	    error_flag++;
	    break;
	}

	if (error_flag)
	{
	    usage();
	}
    }

    if (allow_advise_p)
    {
	int status = ioctl(0, ADVISE_STATUS, &status);

	if (allow_advise && status)
	{
	    int advise_module_pushed = 0;

	    /* Push advise module on stream */
	    (void) ioctl(0, TCGETS, &termios);

	    modules = list_modules(0, module);

	    advise_module_list.next = modules;

	    for (modules = &advise_module_list;
		 modules != NULL;
		 modules = modules->next)
	    {

		if (!strcmp(modules->name, "advise"))
		{
		    if (advise_module_pushed)
			continue;

		    advise_module_pushed = 1;
		}

		if (ioctl(0, I_PUSH, modules->name))
		{
		    (void) fprintf(stderr, "%s: Couldn't I_PUSH: %s (%s).\n",
				   progname, modules->name, strerror(errno));
		    error++;
		}
	    }

	    (void) ioctl(0, TCSETS, &termios);
	}

	if (!allow_advise && !status)
	{
	    (void) ioctl(0, TCGETS, &termios);

	    modules = list_modules(0, "advise");

	    while (modules != NULL)
	    {
		if (strcmp(modules->name, "advise"))
		{
		    if (ioctl(0, I_PUSH, modules->name))
		    {
			(void) fprintf(stderr,
				       "%s: Couldn't I_PUSH: %s (%s).\n",
				       progname, modules->name,
				       strerror(errno));
			error++;
		    }
		}

		modules = modules->next;
	    }

	    (void) ioctl(0, TCSETS, &termios);
	}

	if (!allow_deny_p)
	    return error ? 1 : 0;
    }

    if (allow_deny_p)
    {
	if (ioctl(0, allow_deny, 0))
	{
	    if (errno == EINVAL)
	    {
		(void) fprintf(stderr, "%s: module \"advise\" not in stream.\n",
			       progname);
	    }
	    else
	    {
		(void) fprintf(stderr, "%s: Couldn't set advisory mode (%s).\n",
			       progname, strerror(errno));
	    }

	    return 1;
	}

	return 0;
    }

    /* All switches have been handled */

    argc -= optind;
    argv += optind;

    if (argc > 1)
    {
	usage();
    }

    if (argc == 0)
    {
	int status;

	/*
	** Status of advise.
	*/

	if (ioctl(0, ADVISE_STATUS, &status))
	{
	    printf("Module \"advise\" not pushed on stream.\n");
	}
	else
	{
	    printf("Advise access %s\n", status ? "allowed" : "denied");
	}

	return 0;
    }

    advise(*argv);

    return 0;
}

void
usage()
{
    (void) fprintf(stderr, "usage: %s [-ADad?] [-M module] | [-Ss] [-m char] [ device | username ]\n",
		   progname);
    exit(1);
}

static void
advise(char *who)
{
    int ret, fd, metad=0;
    char buf[1024], *device, *devname, *login_name, *tty_name;
    struct pollfd pfds[2];
    struct termios termios, oldtermios;
    struct stat stbuf;
    struct utmp *ut, uts;
    char username[sizeof(ut->ut_name) + 1];

    username[0] = '\0';

    if (*who == '/') /* full path name */
	device = who;
    else
    {
	/* Either this is /dev/ + who OR a username */

	setutent();

	while ((ut = getutent()) != NULL)
	{
	    if (!strncmp(who, ut->ut_name, sizeof(ut->ut_name)))
	    {
		device = (char *)malloc(sizeof("/dev/") +
					sizeof(ut->ut_name));

		if (device == NULL)
		{
		    (void) fprintf(stderr,
			    "%s: malloc failed (Out of Memory)\n",
			    progname);

		    exit(1);
		}

		strcpy(device, "/dev/");
		strncat(device, ut->ut_name, sizeof(ut->ut_name));
		device[sizeof("/dev/")+sizeof(ut->ut_name)] = '\0';

		strncpy(username, ut->ut_name, sizeof(ut->ut_name));
		username[sizeof(ut->ut_name)] = '\0';
		break;
	    }
	}

	if (ut == NULL) /* Is /dev/ + who */
	{
	    device = (char *)malloc(sizeof("/dev/") + strlen(who));

	    if (device == NULL)
	    {
		(void) fprintf(stderr, "%s: malloc failed (Out of Memory)\n",
				progname);

		exit(1);
	    }

	    strcpy(device, "/dev/");
	    strcat(device, who);
	}

	endutent();
    }

    devname = device + sizeof("/dev/") - 1;

    if (username[0] == '\0')
    {
	setutent();

	strncpy(uts.ut_line, devname, sizeof(uts.ut_line));

	if ((ut = getutline(&uts)) != NULL)
	{
	    strncpy(username, ut->ut_name, sizeof(ut->ut_name));
	    username[sizeof(ut->ut_name)] = '\0';
	}
	else
	{
	    strcpy(username, "unknown");
	}

	endutent();
    }

    if (stat(device, &stbuf) < 0)
    {
	if (errno == ENOENT)
	{
	    (void) fprintf(stderr, "%s: no advisee device: %s\n", progname,
			   device);
	}
	else
	{
	    (void) fprintf(stderr,
			   "%s: Couldn't stat() advisee device: %s (%s)\n",
			   progname, device, strerror(errno));
	}
	exit(1);
    }

    if ((fd = open("/dev/advise", O_RDWR)) < 0)
    {
	(void) fprintf(stderr,
		   "%s: Couldn't open advisory device: /dev/advise (%s)\n",
		   progname, strerror(errno));
	exit(1);
    }

    if (ioctl(fd, ADVISE_SETADVISEE, stbuf.st_rdev))
    {
	if (errno == EUNATCH)
	{
	    (void) fprintf(stderr,
			   "%s: module \"advise\" not in place for %s\n",
			   progname, device);
	} else if (errno == EACCES)
	{
	    (void) fprintf(stderr, "%s: permission denied.\n", progname);
	} else
	{
	    (void) fprintf(stderr,
		       "%s: Couldn't set advisee: %s (%lu, %lu) (%s)\n",
			progname, device, ((stbuf.st_rdev >> 16) &0xFFFF),
			(stbuf.st_rdev&0xFFFF), strerror(errno));
	}
	exit(1);
    }

    if (!secret)
    {
	char *str;
	struct passwd *pt;

	if ((login_name = getlogin()) == NULL)
	{
	    pt = getpwuid(getuid());

	    if (pt == NULL || pt->pw_name == NULL)
	    {
		login_name = "somebody";
	    }
	    else
	    {
		login_name = pt->pw_name;
	    }
	}

	if ((tty_name = ttyname(2)) != NULL)
	{
	    if (!strncmp(tty_name, "/dev/", sizeof("/dev/")-1))
		tty_name += sizeof("/dev/")-1;
	}
	else
	    tty_name = "somewhere";

	str = malloc(strlen(login_name) + strlen(tty_name) +
		     sizeof("[: advising :]\n\r") + strlen(username) +
		     strlen(devname));

	if (str)
	{
	    struct advise_message m;
	    struct strbuf ctl, data;

	    m.type = ADVISE_READDATA;

	    ctl.len = sizeof(m);
	    ctl.buf = (void *)&m;

	    sprintf(str, "[%s:%s %s %s:%s]\n\r", login_name,
		    tty_name, spy ? "spying" : "advising", username, devname);

	    data.len = strlen(str);
	    data.buf = str;

	    (void) putmsg(fd, &ctl, &data, 0);

	    free(str);
	}
    }


    if (!spy)
    {
	(void) ioctl(0, TCGETS, &termios);

	oldtermios = termios;
	termios.c_cc[VMIN] = 1;
	termios.c_cc[VTIME] = 0;
	termios.c_lflag &= ~(ISIG|ICANON|ECHO);

	(void) ioctl(0, TCSETS, &termios);
    }

    pfds[0].fd = fd;
    pfds[0].events = POLLIN;

    pfds[1].fd = 0;
    pfds[1].events = POLLIN;

    for (;;)
    {
	if (poll(pfds, 2, INFTIM) < 0)
	    continue;

	if ((pfds[0].revents&POLLIN) != 0) /* data from advisee ready */
	{
	    if ((ret = read(fd, buf, sizeof(buf))) > 0)
		write(1, buf, ret);
	}

	if ((pfds[1].revents&POLLIN) != 0) /* data from advisor ready */
	{
	    if ((ret = read(0, buf, sizeof(buf))) > 0)
	    {
		if (!spy)
		{
		    register int i;
		    register char *p = buf, *pp=buf;

		    for (i=0; i < ret; ++i, p++)
		    {
			if (metad)
			{
			    if (metad == 2)
			    {
				meta_character = *p;
				printf("The meta character is now: %s\n",
				       strchar(meta_character));
				pp++;
				metad = 0;
				continue;
			    }

			    switch (*p)
			    {
			    case '=':
				metad=2;
				pp++;
				break;

			    case '?':
			    {
				char *escstr = strchar(meta_character);

				printf("Help for meta character <%s>:\n",
				       escstr);
				printf("%s?\t-- This help message.\n", escstr);
				printf("%s~\t-- Send a single meta character.\n",
				       escstr);
				printf("%s.\t-- Disconnect advise session.\n",
				       escstr);
				printf("%s=C\t-- Change meta character to C.\n",
				       escstr);
				printf("%s^Z\t-- Suspend advise session.\n",
				       escstr);
				pp++;
				metad=0;
				break;
			    }

			    case '.':
			    {
				if (!secret)
				{
				    char *str;

				    str = malloc(strlen(login_name) +
					  strlen(tty_name) +
					  sizeof("[/ disconnecting from :]\n") +
				     	  strlen(username) + strlen(devname));

				    if (str)
				    {
					struct advise_message m;
					struct strbuf ctl, data;

					m.type = ADVISE_READDATA;

					ctl.len = sizeof(m);
					ctl.buf = (void *)&m;

					sprintf(str, "[%s/%s disconnecting from %s:%s]\n\r",
						login_name, tty_name, username,
						devname);

					data.len = strlen(str);
					data.buf = str;

					(void) putmsg(fd, &ctl, &data, 0);

					free(str);
				    }
				}

				close(fd);

				(void) ioctl(0, TCSETS, &oldtermios);

				exit(0);
			    }

			    case CTRL('Z'):
			    {
				(void) ioctl(0, TCSETS, &oldtermios);
				(void) signal(SIGTSTP, SIG_DFL);
				(void) kill(0, SIGTSTP);
				(void) ioctl(0, TCSETS, &termios);
				metad=0;
				break;
			    }

			    default:
				metad=0;
				break;
			    }
			}
			else
			{
			    if (*p == meta_character)
			    {
				int d = p - pp;

				metad=1;

				if (d)
				    write(fd, pp, d);

				pp += d + 1;
				i += d;
			    }
			}
		    }

		    if (p - pp)
		    {
			struct advise_message m;
			struct strbuf ctl, data;

			m.type = ADVISE_DATA;

			ctl.len = sizeof(m);
			ctl.buf = (void *)&m;

			data.len = p - pp;
			data.buf = p;

			(void) putmsg(fd, &ctl, &data, 0);
		    }
		}
	    }
	}
    }
}

static struct module_list *
list_modules(int fd, char *push_below)
{
    char lookbuf[max(FMNAMESZ+1,256)];
    struct module_list *mp, *mpp;

    mp = NULL;

    while (ioctl(fd, I_LOOK, lookbuf) == 0)
    {
	if (ioctl(fd, I_POP, 0))
	{
	    (void) fprintf(stderr, "%s: Couldn't I_POP: %s (%s).\n", progname,
			   lookbuf, strerror(errno));
	    return mp;
	}

	if ((mpp = malloc(sizeof(struct module_list))) == NULL ||
	    (mpp->name = malloc(strlen(lookbuf) + 1)) == NULL)
	{
	    (void) fprintf(stderr, "%s: Couldn't malloc (out of memory).\n",
			   progname);
	    return mp;
	}

	mpp->next = mp;
	mp = mpp;

	strcpy(mp->name, lookbuf);

	if (!strcmp(push_below, lookbuf))
	    break;
    }

    return mp;
}

static char *
strchar(char character)
{
    static char retbuf[4];
    char *p = retbuf;
    int capit = 0;

    if (!isascii(character))
    {
	*p++ = '~';
	capit = 1;
	character = toascii(character);
    }

    if (iscntrl(character))
    {
	*p++ = '^';
	capit = 1;
	character += '@';
    }

    if (capit)
	*p++ = toupper(character);
    else
	*p++ = character;

    *p = '\0';

    return retbuf;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'advise.man'
then
	echo shar: will not over-write existing file "'advise.man'"
else
cat << \SHAR_EOF > 'advise.man'
.TH advise 1
.SH NAME
advise \- Attach to another user.
.SH SYNOPSIS
.B advise
[-ADad?] [-M module] | [-Ss] [-m char] [ device | username ]
.SH DESCRIPTION
.B Advise
attaches a user (the advisor) to another user's (the advisee) terminal in
such a way the the advisor can type for the advisee and view what
the advisee's terminal is displaying.
.PP
The advisee would typically type ``advise -a'' to allow advise attaches;
the advisor would then type ``advise username'' which would connect the
advisors terminal the the advisee's.
.PP
All characters the advisor types are sent to the advisee's terminal
as if the advisee typed them save the meta character.
.PP
The default meta character is tilde (~).  The advisor uses the meta
character to disconnect or suspend the advise session.  The meta
commands that are available to the advisor are:
.PP
.RS
.TP
~?
Meta character help message.
.TP
~~
Send the meta character to the advisee's terminal.
.TP
~.
Disconnect advise session.
.TP
~=C
Change the meta character to C.
.TP
~^Z
Suspend this advise session.
.RE
.PP
In Advise mode the advisor uses ``~.'' to disconnect the advise session
(Note: the advisor should use ``~~'' to send one tilde to the advisee's
terminal).
.PP
In ``spy mode'' the advisor should use an interrupt is use to disconnect
the advise session.
.PP
``advise -d'' can be used by the advisee to disconnect the advise
session.
.SH OPTIONS
.TP
-A
Allow advise attaches to this terminal.
.TP
-D
Disallow advise attaches to this terminal.
.TP
-M module
Name of module to place advise module under.
.TP
-S
When attaching to another user, don't send the attach message.
(available to the super user, only).
.TP
-a
Push advise module on standard input stream and allow advise
attaches.
.TP
-d
Push advise module on standard input stream and disallow advise
attaches.
.TP
-m char
Change the meta character to ``char''.  The default meta character
is tilde (~).
.TP
-s
Spy mode only (ie, input from the advisor is not passed to the
advisee).
.TP
device
The name of the tty device to advise.
.TP
username
The name of the user to advise.
.SH AUTHOR
.RS
.PP
Keith M. Gabryelski (ag@amix.commodore.com)
.RE
SHAR_EOF
fi # end of overwriting check
if test -f 'advisedev.c'
then
	echo shar: will not over-write existing file "'advisedev.c'"
else
cat << \SHAR_EOF > 'advisedev.c'
/* Copyright (C) 1990 Keith Gabryelski (ag@amix.commodore.com)

This file is part of advise.

advise is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.  No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing.  Refer to the advise General Public
License for full details.

Everyone is granted permission to copy, modify and redistribute
advise, but only under the conditions described in the
advise General Public License.   A copy of this license is
supposed to have been given to you along with advise so you
can know your rights and responsibilities.  It should be in a
file named COPYING.  Among other things, the copyright notice
and this notice must be preserved on all copies.  */

/*
** Author:	Keith Gabryelski	(ag@amix.commodore.com)
*/

#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/signal.h>
#include <sys/file.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/termios.h>
#include <sys/ttold.h>
#include <sys/cmn_err.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/errno.h>
#include <sys/debug.h>
#include "advisemod.h"
#include <sys/inline.h>

int adviseopen(), adviseclose(), adviserput(), advisewput();
void advisesrvioc();

static struct module_info advisemiinfo =
{
    0, "advise", 0, INFPSZ, 2048, 128,
};

static struct qinit adviserinit =
{
    adviserput, NULL, adviseopen, adviseclose, NULL, &advisemiinfo,
};

static struct module_info advisemoinfo =
{
    42, "advise", 0, INFPSZ, 300, 200,
};

static struct qinit advisewinit =
{
    advisewput, NULL, adviseopen, adviseclose, NULL, &advisemoinfo,
};

struct streamtab adviseinfo =
{
    &adviserinit, &advisewinit, NULL, NULL,
};

extern struct advise_state advise_table;

/*ARGSUSED*/
static int
adviseopen(q, devp, flag, sflag, credp)
register queue_t *q;
dev_t *devp;
int flag, sflag;
cred_t *credp;
{
    register mblk_t *bp;
    struct advise_queue_list *ql;
    struct advise_state  *sp;
    int i;

    if (sflag == MODOPEN)
	return EINVAL;

    for (i=1; i < L_MAXMIN; ++i)
    {
	sp = &advise_table;

	while (sp->next != NULL)
	{
	    ql = sp->next->q_list;

	    while (ql != NULL)
	    {
		if (ql->minord == i)
		    break;

		ql = ql->next;
	    }

	    if (ql != NULL)
		break;

	    sp = sp->next;
	}

	if (sp->next == NULL)
	    break;
    }

    if (i == L_MAXMIN)
    {
	return ENOMEM;		/* no more resources */
    }

    *devp = makedevice(getmajor(*devp), i);

    if ((bp = allocb((int)sizeof(struct advise_queue_list), BPRI_MED)) == NULL)
    {
	return ENOMEM;
    }

    bp->b_wptr += sizeof(struct advise_queue_list);
    ql = (struct advise_queue_list *)bp->b_rptr;
    ql->savbp = bp;
    ql->next = NULL;
    ql->q = q;
    ql->state = NULL;
    ql->minord = i;

    q->q_ptr = (caddr_t)ql;
    WR(q)->q_ptr = (caddr_t)ql;

    return 0;
}

static
adviseclose(q)
register queue_t *q;
{
    struct advise_state *llist = &advise_table;
    struct advise_queue_list *qp = (struct advise_queue_list *)q->q_ptr;
    struct advise_queue_list *ql, *qlp;

    /* Remove us from the advisor's list */

    if (qp->state != NULL)
    {
	while (llist != NULL && llist->next != qp->state)
	    llist = llist->next;

	if (llist != NULL)
	{
	    ql = llist->next->q_list;

	    if (ql->q == q)
	    {
		llist->next->q_list = ql->next;
	    }
	    else
	    {
		while (ql->next != NULL && ql->next->q != q)
		    ql = ql->next;

		if (ql->next != NULL)
		{
		    ql->next = ql->next->next;
		}
	    }
	}
    }

    qp->state = NULL;
    freeb(qp->savbp);

    q->q_ptr = NULL;
}

static int
adviserput(q, bp)
struct queue *q;
mblk_t *bp;
{
    putnext(q, bp);
}

static int
advisewput(q, bp)
struct queue *q;
mblk_t *bp;
{
    struct advise_queue_list *qp = (struct advise_queue_list *)q->q_ptr;
    struct advise_state *sp = qp->state;

    switch (bp->b_datap->db_type)
    {
    case M_PROTO:
    {
	struct advise_message *ms = (struct advise_message *)bp->b_rptr;
	mblk_t *bp2 = unlinkb(bp);

	if (bp2)
	{
	    if (sp != NULL && sp->q != NULL)
	    {
		if (ms->type == ADVISE_READDATA)
		{
		    putnext(WR(sp->q), bp2);
		}
		else
		{
		    putnext(sp->q, bp2);
		}
	    }
	    else
		freemsg(bp2);
	}

	freemsg(bp);

	break;
    }

    case M_DATA:
	/*
	** Write data to advisee.
	*/
	if (sp != NULL && sp->q != NULL)
	    putnext(sp->q, bp);
	else
	    freemsg(bp);
	break;

    case M_IOCTL:
    case M_IOCDATA:
	advisesrvioc(q, bp);
	break;

    default:
	freemsg(bp);
	break;
    }
}

static void
advisesrvioc(q, mp)
queue_t *q;
mblk_t *mp;
{
    mblk_t *mp1;
    struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
    struct advise_queue_list *qp=(struct advise_queue_list *)q->q_ptr; 
    int s;

    if (mp->b_datap->db_type == M_IOCDATA)
    {
	/* For copyin/copyout failures, just free message. */
	if (((struct copyresp *)mp->b_rptr)->cp_rval)
	{
	    freemsg(mp);
	    return;
	}

	if (!((struct copyresp *)mp->b_rptr)->cp_private)
	{
	    mp->b_datap->db_type = M_IOCACK;
	    freemsg(unlinkb(mp));
	    iocbp->ioc_count = 0;
	    iocbp->ioc_rval = 0;
	    iocbp->ioc_error = 0;
	    putnext(RD(q), mp);
	    return;
	}
    }

    switch (iocbp->ioc_cmd)
    {
    case ADVISE_SETADVISEE:
	{
	    register dev_t p;
	    struct advise_queue_list *qlp;
	    struct advise_state *llist;

	    if (qp->state != NULL) /* already advising someone */
	    {
		iocbp->ioc_error = EBUSY;
		mp->b_datap->db_type = M_IOCNAK;
		iocbp->ioc_count = 0;
		putnext(RD(q), mp);
		break;
	    }

	    if (!mp->b_cont)
	    {
		iocbp->ioc_error = EINVAL;
		mp->b_datap->db_type = M_IOCNAK;
		iocbp->ioc_count = 0;
		putnext(RD(q), mp);
		break;
	    }

	    p = *(dev_t *)mp->b_cont->b_rptr;
	    
	    s = spladvise();

	    llist = advise_table.next;

	    while (llist != NULL && llist->dev != p)
	    {
		llist = llist->next;
	    }

	    if (llist == NULL)
	    {
		splx(s);
		iocbp->ioc_error = EUNATCH;
		mp->b_datap->db_type = M_IOCNAK;
		iocbp->ioc_count = 0;
		putnext(RD(q), mp);
		break;
	    }

	    if ((llist->status & ALLOW_ADVICE) == 0 && (!suser(u.u_cred)))
	    {
		splx(s);
		iocbp->ioc_error = EACCES;
		mp->b_datap->db_type = M_IOCNAK;
		iocbp->ioc_count = 0;
		putnext(RD(q), mp);
		break;
	    }

	    /*
	    ** Add ourself to the list of advisors for this advisee.
	    */

	    if (llist->q_list == NULL)
	    {
		qlp = llist->q_list = qp;
	    }
	    else
	    {
		qlp = llist->q_list;

		while (qlp->next != NULL)
		    qlp = qlp->next;

		qlp->next = qp;
		qlp = qp;
	    }

	    qlp->state = llist;

	    splx(s);

	    mp->b_datap->db_type = M_IOCACK;
	    mp1 = unlinkb(mp);
	    if (mp1)
		freeb(mp1);
	    iocbp->ioc_count = 0;
	    putnext(RD(q), mp);
	    break;
	}

    default:
	/* Unrecognized ioctl command */
	if (canput(RD(q)->q_next))
	{
	    mp->b_datap->db_type = M_IOCNAK;
	    putnext(RD(q), mp);
	}
	else
	    putbq(q, mp);
	break;
    }
}
SHAR_EOF
fi # end of overwriting check
if test -f 'advisemod.c'
then
	echo shar: will not over-write existing file "'advisemod.c'"
else
cat << \SHAR_EOF > 'advisemod.c'
/* Copyright (C) 1990 Keith Gabryelski (ag@amix.commodore.com)

This file is part of advise.

advise is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.  No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing.  Refer to the advise General Public
License for full details.

Everyone is granted permission to copy, modify and redistribute
advise, but only under the conditions described in the
advise General Public License.   A copy of this license is
supposed to have been given to you along with advise so you
can know your rights and responsibilities.  It should be in a
file named COPYING.  Among other things, the copyright notice
and this notice must be preserved on all copies.  */

/*
** Author:	Keith Gabryelski	(ag@amix.commodore.com)
*/

#include <sys/types.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/file.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/termios.h>
#include <sys/ttold.h>
#include <sys/cmn_err.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/errno.h>
#include <sys/debug.h>
#include "advisemod.h"
#include <sys/inline.h>

int advisemopen(), advisemclose(), advisemrput(), advisemwput();

static struct module_info advisemiinfo =
{
    0, "advisemod", 0, INFPSZ, 2048, 128,
};

static struct qinit adviserinit =
{
    advisemrput, NULL, advisemopen, advisemclose, NULL, &advisemiinfo,
};

static struct module_info advisemoinfo =
{
    42, "advisemod", 0, INFPSZ, 300, 200,
};

static struct qinit advisewinit =
{
    advisemwput, NULL, advisemopen, advisemclose, NULL, &advisemoinfo,
};

struct streamtab advisemodinfo =
{
    &adviserinit, &advisewinit, NULL, NULL,
};

struct advise_state advise_table;

/*ARGSUSED*/
static int
advisemopen(q, devp, flag, sflag, credp)
register queue_t *q;
dev_t *devp;
int flag, sflag;
cred_t *credp;
{
    register struct advise_state *sp;
    register mblk_t *bp;
    struct advise_state *llist = &advise_table;

    if (sflag != MODOPEN)
	return EINVAL;

    if ((bp = allocb((int)sizeof(struct advise_state), BPRI_MED)) == NULL)
    {
	return ENOMEM;
    }

    bp->b_wptr += sizeof(struct advise_state);
    sp = (struct advise_state *)bp->b_rptr;
    sp->savbp = bp;

    sp->dev = *devp;
    sp->status = 0;		/* Deny access by default */
    sp->next = NULL;
    sp->q_list = NULL;
    sp->q = q;

    while (llist->next != NULL)
    {
	if (llist->next->dev == *devp)
	{
	    /*
	    ** We are already pushed on this stream.
	    */
	    freeb(bp);

	    sp = llist->next;

	    break;
	}

	llist = llist->next;
    }

    llist->next = sp;

    q->q_ptr = (caddr_t)sp;
    WR(q)->q_ptr = (caddr_t)sp;

    return 0;
}

static
advisemclose(q)
register queue_t *q;
{
    register struct advise_state *sp = (struct advise_state *)q->q_ptr;
    struct advise_state *llist = &advise_table;
    struct advise_queue_list *qp = sp->q_list;

    sp->status = 0;

    /* unlink us from the state table */

    while (llist->next != sp)
	llist = llist->next;

    llist->next = llist->next->next;

    while (sp->next != NULL)
    {
	/* tell each advisor that we're shutting down */

	flushq(sp->q, FLUSHDATA);
	putctl(sp->q->q_next, M_HANGUP);
	
	sp = sp->next;
    }

    freeb(sp->savbp);

    q->q_ptr = NULL;
}

static
advisemrput(q, mp)
register queue_t *q;
register mblk_t *mp;
{
    putnext(q, mp);
}

static
advisemwput(q, mp)
register queue_t *q;
register mblk_t *mp;
{
    struct advise_state *sp = (struct advise_state *)q->q_ptr;
    register struct advise_queue_list *qp;
    int s;

    switch (mp->b_datap->db_type)
    {
    case M_DATA:
	/*
	** Write data to advisors.
	*/
	s = spladvise();
	for (qp = sp->q_list; qp != NULL; qp = qp->next)
	{
	    mblk_t *mp1 = copymsg(mp);

	    if (mp1 != NULL)
		putnext(qp->q, mp1);
	}

	splx(s);
	break;

    case M_IOCTL:
    case M_IOCDATA:
	if (advisemsrvioc(q, mp)) /* handled? */
	    return;
	break;
    }

    putnext(q, mp);
}

static int
advisemsrvioc(q, mp)
queue_t *q;
mblk_t *mp;
{
    mblk_t *mp1;
    struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
    struct advise_state *sp = (struct advise_state *)q->q_ptr;

    if (mp->b_datap->db_type == M_IOCDATA)
    {
	struct copyresp *csp = (struct copyresp *)mp->b_rptr;

	switch(csp->cp_cmd)
	{
	case ADVISE_STATUS:
	case ADVISE_ALLOW:
	case ADVISE_DENY:
	    /* For copyin/copyout failures, just free message. */

	    if (csp->cp_rval)
		freemsg(mp);
	    else if (!csp->cp_private)
	    {
		mp->b_datap->db_type = M_IOCACK;
		freemsg(unlinkb(mp));
		iocbp->ioc_count = 0;
		iocbp->ioc_rval = 0;
		iocbp->ioc_error = 0;
		putnext(RD(q), mp);
	    }

	    return 1;
        }
    }

    switch (iocbp->ioc_cmd)
    {
    case ADVISE_STATUS:
        {
	    int *status;
	    caddr_t arg = *(caddr_t *)mp->b_cont->b_rptr;

	    freemsg(mp->b_cont);

	    mp->b_cont = allocb(sizeof(int), BPRI_MED);
	    if (!mp->b_cont)
	    {
		mp->b_datap->db_type = M_IOCNAK;
		freemsg(unlinkb(mp));
		iocbp->ioc_count = 0;
		iocbp->ioc_rval = 0;
		iocbp->ioc_error = ENOMEM;
		putnext(RD(q), mp);
		return 1;
	    }

	    status = (int *)mp->b_cont->b_rptr;
	    mp->b_cont->b_wptr += sizeof(int);

	    *status = sp->status;

	    if (mp->b_datap->db_type == M_IOCTL &&
		iocbp->ioc_count == TRANSPARENT)
	    {
		struct copyreq *creq = (struct copyreq *)mp->b_rptr;
		mp->b_datap->db_type = M_COPYOUT;
		creq->cq_addr = arg;
		mp->b_wptr = mp->b_rptr + sizeof *creq;
		mp->b_cont->b_wptr = mp->b_cont->b_rptr + sizeof(int);
		creq->cq_size = sizeof(int);
		creq->cq_flag = 0;
		creq->cq_private = (mblk_t *)NULL;
		putnext(RD(q), mp);
		return 1;
	    }
	}
	break;

    case ADVISE_ALLOW:
	sp->status |= ALLOW_ADVICE;

	mp->b_datap->db_type = M_IOCACK;
	mp1 = unlinkb(mp);
	if (mp1)
	    freeb(mp1);
	iocbp->ioc_count = 0;
	putnext(RD(q), mp);
	break;

    case ADVISE_DENY:
	sp->status &= ~(ALLOW_ADVICE);

	mp->b_datap->db_type = M_IOCACK;
	mp1 = unlinkb(mp);
	if (mp1)
	    freeb(mp1);
	iocbp->ioc_count = 0;
	putnext(RD(q), mp);
	break;

    default:
	return 0;
    }

    return 1;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'advisemod.h'
then
	echo shar: will not over-write existing file "'advisemod.h'"
else
cat << \SHAR_EOF > 'advisemod.h'
/* Copyright (C) 1990 Keith Gabryelski (ag@amix.commodore.com)

This file is part of advise.

advise is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.  No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing.  Refer to the advise General Public
License for full details.

Everyone is granted permission to copy, modify and redistribute
advise, but only under the conditions described in the
advise General Public License.   A copy of this license is
supposed to have been given to you along with advise so you
can know your rights and responsibilities.  It should be in a
file named COPYING.  Among other things, the copyright notice
and this notice must be preserved on all copies.  */

/*
** Author:	Keith Gabryelski	(ag@amix.commodore.com)
*/

struct advise_queue_list
{
    mblk_t			*savbp;	/* ptr to this mblk for freeb()ing */
    queue_t			*q;	/* advisor's queue */
    int				minord; /* minor device for this advisor */
    struct advise_state		*state; /* ptr back to advise_state struct */
    struct advise_queue_list	*next;  /* ptr to next advisor */
};

struct advise_state
{
    mblk_t			*savbp;	/* ptr to this mblk for freeb()ing */
    int				status;	/* current status */
    dev_t			dev;	/* our device */
    queue_t			*q;	/* queue for advisor writing */
    struct advise_queue_list	*q_list;/* list of spies */
    struct advise_state		*next;  /* next in advise_table */
};

#define ALLOW_ADVICE		(0x01)

struct advise_message
{
    int				type;   /* What type of data is this? */
};

#define ADVISE_DATA		(0x00)
#define ADVISE_READDATA		(0x01)

#define ADVISE			('z'<<16)
#define ADVISE_SETADVISEE	(ADVISE|0x01)
#define	ADVISE_ALLOW		(ADVISE|0x02)
#define	ADVISE_DENY		(ADVISE|0x03)
#define ADVISE_STATUS		(ADVISE|0x04)

#define spladvise	spltty
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0