v06i011: A "talk" for System V (sysVtalkB)
sources-request at mirror.UUCP
sources-request at mirror.UUCP
Sat Jun 21 02:51:42 AEST 1986
Submitted by: cbosgd!ukma!ukecc!edward (Edward C. Bennett)
Mod.sources: Volume 6, Issue 11
Archive-name: sysVtalkB
[ This is the second of two different talk programs.
The manpage has some lines that start with periods;
let me know if this causes problems for anyone, and I
will fix this in future postings. --r$]
This a version of the Berkeley talk program for System V. Talk allows
conversing parties to type simultaineously while maintaining a neat,
readable screen. It uses the 'messages' Interprocess Communication
Facility of System V to pass data.
#! /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:
: 'README'
: 'talk.1'
: 'Makefile'
: 'talk.h'
: 'talk.c'
: 'talkd.c'
: 'infotalk.c'
: 'stoptalk.c'
: This archive created: 'Fri Jun 13 14:04:24 1986'
: By: 'Edward C. Bennett'
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(2386 characters)'
if test -f 'README'
then
echo shar: will not over-write existing file "'README'"
else
cat >'README' <<\SHAR_EOF
Installing and maintaining 'talk'
These instructions are intended to be fairly generic. They were
written for a 3B20. Your milage may vary.
The talk system is composed of three executable pieces.
talk the user interface
talkdemon the background process that supervises conversations
stoptalk a program for removing the talk msgqueue
(Yes, I know it's usally spelled 'daemon'. But since my 3B20 has
an 'errdemon' I figured System V wanted to be different. C'est la vie.)
First, read the 'Makefile' and make any nessecary modifications.
You might want to also change 'talk.h'. Running 'make install' will create
and install the three binaries for you. /usr/bin/talk must run setuid.
This is so that independent 'talk' process can exchange signals. Talkdemon
and stoptalk should have 'other' permissions turned off to prevent regular
users from tampering with them. The owner and group ids of the files are
not important so you can set them as you like.
After installing the binaries, provisions for automatic startup
and shutdown of the talkdemon should be made. In your startup script
(/etc/rc on my 3B20), at the point where /etc/cron is started, insert
these lines:
echo talkdemon started
/etc/talkdemon
and in your shutdown script (/etc/shutdown on my 3B20), before all user
processes are killed, insert these lines:
echo Talk msgqueue being removed
/etc/stoptalk
The entry in shutdown is needed because taking the system down to single
user mode doesn't remove msgqueues and because /etc/shutdown kills
user processes with a '-9' which prevents the talkdemon from removing
it's own msgqueue.
The 'infotalk' program provides status on the state of the demon.
It was initally used for debugging purposes and has been included for
completeness.
Known problems with talk
1) My version of System V has no high-precision sleep call, so the
main loop of 'talk' runs as fast as the machine allows. I don't think
this leads to any problems other than high CPU usage. If your system
has a fractional-second sleep call, I suggest you use it to reduce talk's
CPU time consumption. Let me know how it goes.
2) The version of `curses` on my machine has a bug which causes
the cursor to jump to and from the beginning of the current line when
a character is added to the screen. This is not harmful, just annoying.
Edward C. Bennett
ihnp4!cbosgd!ukma!ukecc!edward
SHAR_EOF
fi
echo shar: extracting "'talk.1'" '(1217 characters)'
if test -f 'talk.1'
then
echo shar: will not over-write existing file "'talk.1'"
else
cat >'talk.1' <<\SHAR_EOF
.TH TALK 1
.SH NAME
talk \- inter-terminal screen-oriented communication program
.SH SYNOPSIS
.B talk user
[
.B tty
]
.SH DESCRIPTION
.I Talk
is a inter-terminal conversation program designed
to allow both parties to type at the same time
and still maintain a readable display.
The
.IR curses (3X)
screen-management library is used to handle the
display so you must set your TERM environment variable.
.PP
When you run
.I talk
you give the login name of who you want to converse with.
If they are logged in more than once,
you must also specify which tty of theirs you wish to talk to.
.I Talk
will inform them that you wish to communicate
with them and wait for their response.
If they are slow,
.I talk
will resend its message every 30 seconds until either
they respond or you get bored and hit INTERRUPT.
.PP
A control-L will force the screen to be
redrawn in case it becomes garbled.
.PP
Although the program emulates to Berkeley program of the
same name, the code is 100% original.
.SH AUTHOR
Edward C. Bennett
.SH DIAGNOSTICS
Hopefully self-explanatory.
.SH BUGS
Because of an apparent bug in the SystemV curses package,
if one person backspaces while the other is typing,
text is lost off at least one screen.
SHAR_EOF
fi
echo shar: extracting "'Makefile'" '(1416 characters)'
if test -f 'Makefile'
then
echo shar: will not over-write existing file "'Makefile'"
else
cat >'Makefile' <<\SHAR_EOF
#
# Makefile for talk, stoptalk, and the talkdemon
#
# AUTHOR
# Edward C. Bennett (edward at ukecc)
#
# Copyright 1985 by Edward C. Bennett
#
# Permission is given to alter this code as needed to adapt it to forign
# systems provided that this header is included and that the original
# author's name is preserved.
#
BIN=/usr/bin
DEMONDIR=/etc
#
# /usr/bin/talk needs to run setuid. It doesn't have to be root so make
# this something harmless.
#
OWNER=edward
#
# Use whatever libraries you need to make curses work.
#
LIBS=-lcurses # -lterminfo -ltermcap -ltermlib
all: talk talkdemon infotalk stoptalk
talk: talk.o talk.c
cc -s talk.o -o talk ${LIBS}
tester: ntalk.c
cc -DSCHIZO ntalk.c -o tester ${LIBS}
talkdemon: talkd.o talkd.c
cc -s talkd.o -o talkdemon
infotalk: infotalk.o infotalk.c
cc -s infotalk.o -o infotalk
stoptalk: stoptalk.o stoptalk.c
cc -s stoptalk.o -o stoptalk
talk.o: talk.h
cc -c -O talk.c
talkd.o: talk.h
cc -c -O talkd.c
infotalk.o: talk.h
cc -c -O infotalk.c
stoptalk.o: talk.h
cc -c -O stoptalk.c
install: all
/etc/install -f ${BIN} talk
/etc/install -f ${DEMONDIR} talkdemon
/etc/install -f ${DEMONDIR} stoptalk
chown ${OWNER} ${BIN}/talk
chmod 4755 ${BIN}/talk
chmod 750 ${DEMONDIR}/talkdemon ${DEMONDIR}/stoptalk
clean:
rm -f *.o talk talkd talkdemon stoptalk
shar:
shar -v README talk.1 Makefile talk.h talk.c talkd.c infotalk.c stoptalk.c > talk.shar
SHAR_EOF
fi
echo shar: extracting "'talk.h'" '(2629 characters)'
if test -f 'talk.h'
then
echo shar: will not over-write existing file "'talk.h'"
else
cat >'talk.h' <<\SHAR_EOF
/*
* Definitions for talk and the talkdemon
*
* AUTHOR
* Edward C. Bennett (edward at ukecc)
*
* Copyright 1985 by Edward C. Bennett
*
* Permission is given to alter this code as needed to adapt it to forign
* systems provided that this header is included and that the original
* author's name is preserved.
*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <utmp.h>
#include <string.h>
/*
* If your system has a high resolutiuon sleep call, define SLEEP to
* be the name of the function and define SLEEP_TIME to be whatever number
* gives you about a .1 second delay. The defines are used like this:
* SLEEP(SLEEP_TIME);
*/
/* #define SLEEP sleep /* Name of high resolution sleep call */
/* #define SLEEP_TIME 100 /* Number to give ~.1 second delay */
/*
* If your system has the old utmp file format, i.e. without USER_PROCESS,
* define OLDUTMP
*/
/* #define OLDUTMP /* If you use the old utmp format */
/*
* If you have a version of curses that is more Berkeley than System V,
* you'll probably need at least one of these.
*/
/* #define cbreak crmode /* to turn off canonical input */
/* #define CLRONEXIT /* turn on if you want the screen cleared on exit */
/*
* Defines for using msgget()
* If for some reason the msgkey that talk generates is already used on
* your system, change MAGIC_ID to another character.
*/
#define TALK_PATH "/usr/bin/talk"
#define MAGIC_ID 'E' /* This can be any char. Be creative. */
#define MSGQ_PERMS 0666 /* You may want to change these */
/* to increase security */
#define NAMESIZ 8 /* from <utmp.h> */
#define LINESIZ 12 /* from <utmp.h> */
#define TTYLOC 16 /* ttys always start in this location in 'mtext' */
#define SEND 0 /* Locations in the 'lines' array for these msgtypes */
#define RECEIVE 1
#define CTL 2
#define PIDLOC 7 /* pids always ride in this spot in the 'lines' array */
#define STATUS 3 /* mtype for status messages */
/*
* Stuff for the Deamon MeSsaGes
* All message involving the demon use this structure
*
* Here's how the buffer is laid out:
* 1 2
* 0 8 6 8
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
* | name | name | tty line | pid
* | send |receive| ctl | | | | | pid |
*/
typedef struct {
long mtype;
union {
char mtext[32];
long lines[8];
} msgval;
} DMSG;
DMSG dmsgbuf;
#define MSGSIZ sizeof(dmsgbuf.msgval.mtext)
/*
* 'Find' return codes
*/
#define TALKABLE 1
#define NOTLOGGEDON -1
#define NOTWRITE -2
#define LOGGEDMORE -3
#define NOTONLINE -4
SHAR_EOF
fi
echo shar: extracting "'talk.c'" '(9418 characters)'
if test -f 'talk.c'
then
echo shar: will not over-write existing file "'talk.c'"
else
cat >'talk.c' <<\SHAR_EOF
/*
* talk - a two-way, screen-oriented communication program
*
* Talk, which emulates the Berkeley program of the same name, allows
* both parties in a conversation to type at will, without fear of garbling
* each others messages. Input from either user is immediatly written to both
* screens so that there is no waiting like there is with write(1).
*
* Although this program resembles, to the user, the Berkeley program
* 'talk', the code is entirely original by the author.
*
* AUTHOR
* Edward C. Bennett (edward at ukecc)
*
* Copyright 1985 by Edward C. Bennett
*
* Permission is given to alter this code as needed to adapt it to forign
* systems provided that this header is included and that the original
* author's name is preserved.
*/
#include <curses.h>
#include <signal.h>
#include <fcntl.h>
#include "talk.h"
struct msgbuf sndbuf, rcvbuf;
int msqid;
int avail; /* The availabity of a perspective partner */
int parpid = 0; /* The pid of the partner */
char *ctime(), *getlogin();
time_t tloc, otime, time();
main(argc, argv)
int argc;
char *argv[];
{
key_t msgkey, ftok();
long fromtype; /* The msgtype where we look for characters */
/* from the partner */
#ifdef FIONREAD
long waiting;
#endif
void exit();
int c, Finish();
int MIDDLE;
short orgy = 0, orgx = 0; /* That's ORGinator's Y you pervert */
short resy, resx = 0;
msgkey = ftok(TALK_PATH, MAGIC_ID);
if ((msqid = msgget(msgkey, MSGQ_PERMS)) == -1) {
fprintf(stderr, "%s: Nonexistant talk msgqueue\n", argv[0]);
exit(1);
}
#ifndef SCHIZO
if (!isatty(0)) {
fprintf(stderr, "%s: must have a terminal for input\n", argv[0]);
exit(1);
}
#endif
#ifndef SCHIZO
if (argc == 1) {
fprintf(stderr, "%s: No user specified\n", argv[0]);
exit(1);
}
/*
* We need to tell the demon who we are, who we want to talk to,
* the tty of who we want (if it was given) and our process id.
*/
strncpy(dmsgbuf.msgval.mtext, getlogin(), NAMESIZ);
strncpy(&dmsgbuf.msgval.mtext[NAMESIZ], argv[1], NAMESIZ);
if (argv[2])
strncpy(&dmsgbuf.msgval.mtext[TTYLOC], argv[2], LINESIZ);
dmsgbuf.msgval.lines[PIDLOC] = getpid();
dmsgbuf.mtype = 1;
/*
* Tell the demon we are here.
*/
if (msgsnd(msqid, (struct msgbuf *)&dmsgbuf, MSGSIZ, 0) == -1) {
fprintf(stderr, "%s: msgsnd failure(%d)\n", argv[0], errno);
exit(1);
}
/*
* Wait for a response from the demon.
*/
if (msgrcv(msqid, (struct msgbuf *)&dmsgbuf, MSGSIZ, (long)2, 0) == -1) {
fprintf(stderr, "%s: msgrcv failure(%d)\n", argv[0], errno);
exit(1);
}
avail = dmsgbuf.msgval.lines[0];
/*
* A negative availability is untalkable.
*/
if (avail < 0) {
Error(avail, argv);
exit(1);
}
sndbuf.mtype = dmsgbuf.msgval.lines[SEND];
#endif
/*
* Now that it's OK to talk, go ahead and
* initialize the terminal and screen
*/
if (initscr() == (WINDOW *)NULL) {
fprintf(stderr, "%s: TERM variable not set\n", argv[0]);
Finish(SIGINT);
}
/*
* Since curses in now ineffect, we need to watch for
* ALL signals so that we can properly terminate
*/
for (c = 0; c < NSIG; c++)
signal(c, Finish);
cbreak();
noecho();
#ifndef SCHIZO
#ifndef FIONREAD
nodelay(stdscr, TRUE);
#endif
#endif
MIDDLE = LINES / 2 - 1;
time(&tloc);
mvprintw(MIDDLE, 0, "--------------------------------------------------------------- %12.12s --", ctime(&tloc) + 4);
move(orgy, orgx);
refresh();
fromtype = dmsgbuf.msgval.lines[RECEIVE];
#ifndef SCHIZO
/*
* If we are given a ctl type, wait for a ctl message.
*/
if (dmsgbuf.msgval.lines[CTL] > 0) {
mvprintw(0, 0, "[Waiting for %s to answer]\n", argv[1]);
refresh();
Ring();
while (msgrcv(msqid, (struct msgbuf *)&dmsgbuf, MSGSIZ, dmsgbuf.msgval.lines[2], 0) == -1) {
/*
* The 'talkee' may have turned off his messages
*/
if (avail < 0) {
mvprintw(orgy++, orgx, "[%s no longer writable]\n", argv[1]);
refresh();
}
/*
* EINTR is an interrupted system call
*/
else if (errno == EINTR) {
mvprintw(orgy++, orgx, "[Ringing %s again]\n", argv[1]);
refresh();
}
else {
fprintf(stderr, "%s: msgrcv failure(%d)\n", argv[0], errno);
Finish(SIGINT);
}
}
if (orgy) {
while (orgy > 1) {
move(--orgy, orgx);
clrtoeol();
}
} else
orgy = 1;
alarm(0); /* Turn off the ringer */
beep();
mvaddstr(0, 0, "[Connection established]\n");
refresh();
}
parpid = dmsgbuf.msgval.lines[PIDLOC];
#endif
/*
* This loop just continually checks both users for input.
* It actually runs TOO FAST, but since SysV has no fractional
* second sleep we do the best we can.
*/
resy = MIDDLE + 1;
for (;;) {
/*
* This is the owner's half
*/
#ifdef FIONREAD
ioctl(0, FIONREAD, &waiting);
if (waiting) {
c = getch();
#else
if ((c = getch()) != EOF) {
#endif
if (c == erasechar()) {
c = '\b';
if (--orgx < 0)
orgx = 0;
else
mvaddstr(orgy, orgx, " \b");
}
else if (c == '\004')
Finish(SIGINT);
else if (c == '\f') {
clearok(curscr, TRUE);
refresh();
continue;
}
else if (c == '\n') {
orgy = ++orgy % MIDDLE;
orgx = 0;
move(orgy, orgx);
clrtoeol();
move((orgy + 1) % MIDDLE, orgx);
clrtoeol();
move(orgy, orgx);
}
/*
* Regular characters
*/
else {
/*
* Check for wrap around
*/
if (orgx >= 79) {
orgy = ++orgy % MIDDLE;
orgx = 0;
move(orgy, orgx);
clrtoeol();
move((orgy + 1) % MIDDLE, orgx);
clrtoeol();
}
mvaddch(orgy, orgx++, c);
}
refresh();
#ifndef SCHIZO
*sndbuf.mtext = c;
if (msgsnd(msqid, (struct msgbuf *)&sndbuf, 1, 0) == -1) {
fprintf(stderr, "%s: msgsnd failure(%d)\n", argv[0], errno);
Finish(SIGINT);
}
#endif
}
#ifndef SCHIZO
/*
* This is the partner's half
*/
if (msgrcv(msqid, (struct msgbuf *)&rcvbuf, 1, fromtype, IPC_NOWAIT) != -1) {
switch (*rcvbuf.mtext) {
case '\b':
if (--resx < 0)
resx = 0;
else
mvaddstr(resy, resx, " \b");
break;
case '\n':
if (++resy >= LINES - 1)
resy = MIDDLE + 1;
resx = 0;
mvaddch(resy, resx, '\n');
mvaddch(((resy - MIDDLE) % (LINES - MIDDLE - 2)) + MIDDLE + 1, resx, '\n');
move(resy, resx);
break;
default:
/*
* Check for wrap around
*/
if (resx >= 79) {
if (++resy >= LINES - 1)
resy = MIDDLE + 1;
resx = 0;
move(resy, resx);
clrtoeol();
move(((resy - MIDDLE) % (LINES - MIDDLE - 2)) + MIDDLE + 1, resx);
clrtoeol();
move(resy, resx);
}
mvaddch(resy, resx++, *rcvbuf.mtext);
break;
}
refresh();
}
#endif
/*
* Update the time
*/
time(&tloc);
if (tloc >= otime + 60) {
otime = tloc;
mvprintw(MIDDLE, COLS - 16, "%12.12s", ctime(&tloc) + 4);
refresh();
}
#ifdef SLEEP
SLEEP(SLEEP_TIME);
#endif
}
}
/*
* Ring - ring the perspective partner
*
* Ring();
*
* A request message is sent to the partner's terminal.
*/
Ring()
{
FILE *fp, *fopen();
char *ttyname();
int Ring();
tloc = time((time_t *)0);
if ((fp = fopen(&dmsgbuf.msgval.mtext[TTYLOC], "w")) != NULL) {
fprintf(fp, "\r\n%c%cTalk request from %s on %s at %5.5s\r\n",
'\007', '\011', getlogin(), ttyname(0)+5, ctime(&tloc)+11);
fprintf(fp, "%cRespond with 'talk %s'\r\n", '\007', getlogin());
fclose(fp);
}
/*
* If the person being rung turns of his messages, set avail to
* indicate his new unavailablity
*/
else
avail = -1;
signal(SIGALRM, Ring);
alarm((unsigned)20);
}
/*
* Error - print an error message
*
* Error(code);
* code is the availability of who we want to talk to
*
* A message regarding why we can't talk is printed.
*/
Error(code, argv)
int code;
char **argv;
{
switch (code) {
case NOTLOGGEDON:
fprintf(stderr, "%s: %s not logged on\n", argv[0], argv[1]);
break;
case NOTWRITE:
fprintf(stderr, "%s: %s not writeable\n", argv[0], argv[1]);
break;
case LOGGEDMORE:
fprintf(stderr, "%s: %s logged on more than once\n", argv[0], argv[1]);
break;
case NOTONLINE:
fprintf(stderr, "%s: %s not on line %s\n", argv[0], argv[1], argv[2]);
break;
}
}
/*
* Finish - reset and exit
*
* Finish();
*
* Finish is called upon receipt of SIGINT or SIGUSR1. Finish resets the
* user's terminal and if called by SIGINT, sends SIGUSR1 signal to his
* partner's talk process so that both terminate simultaineously
*/
Finish(sig)
int sig;
{
FILE *fp, *fopen();
int i;
/*
* Prevent from being interrupted while finishing
*/
for (i = 0; i < NSIG; i++)
signal(i, SIG_IGN);
#ifndef SCHIZO
if (sig == SIGINT) {
/*
* If a conversation was in process, tell the partner's
* process to stop also. Otherwise, tell the partner
* that the request is no longer pending.
*/
if (parpid)
kill(parpid, SIGUSR1);
else if ((fp = fopen(&dmsgbuf.msgval.mtext[TTYLOC], "w")) != NULL) {
tloc = time((long *)0);
fprintf(fp, "\r\n%cTalk request from %s cancelled at %5.5s\r\n", '\011', getlogin(), ctime(&tloc)+11);
fclose(fp);
}
}
#endif
#ifdef CLRONEXIT
clear();
#endif
mvaddstr(0, 0, "[Connection closed]\n");
refresh();
nodelay(stdscr, FALSE);
endwin();
#ifndef SCHIZO
dmsgbuf.msgval.lines[PIDLOC] = -getpid();
dmsgbuf.mtype = 1;
if (msgsnd(msqid, (struct msgbuf *)&dmsgbuf, MSGSIZ, 0) == -1) {
fprintf(stderr, "talk: msgsnd failure(%d)\n", errno);
exit(1);
}
#endif
exit(0);
}
SHAR_EOF
fi
echo shar: extracting "'talkd.c'" '(13950 characters)'
if test -f 'talkd.c'
then
echo shar: will not over-write existing file "'talkd.c'"
else
cat >'talkd.c' <<\SHAR_EOF
/*
* talkdemon - the demon for the talk program
*
* The talkdemon maintains a linked list of TALK structures, one for each
* person engaged in a conversation. The demon sits in the background and
* 'listens' on msgtype 1 for messages from talk programs. Whenever a talk
* process starts up or exits, it sends a message to the demon. The demon
* examines the message to see if it is and startup or an exit message.
* If it is the former, the demon compares the new structure with the existing
* list and manages to determine a set of send/receive msgtypes. (Each
* conversation gets its own set) Now, ctl messages. When the first half
* of a conversation (the originator) starts up, it has to wait for the
* other process to start. If the demon determines that a talk process is
* an originator, in addition to the send/receive pair, the demon sends back
* a ctltype. The originating talk process waits on this ctltype for a demon
* message. This is sent when the second half of the conversation starts
* up (the responder). When either talk process exits, it sends a message
* to the demon as well as SIGUSR1 to the other process. When the demon
* receives an exit message, it removes that process's TALK structure
* from the linked list.
*
* AUTHOR
* Edward C. Bennett (edward at ukecc)
*
* Copyright 1985 by Edward C. Bennett
*
* Permission is given to alter this code as needed to adapt it to forign
* systems provided that this header is included and that the original
* author's name is preserved.
*/
#include <stdio.h>
#include <signal.h>
#include "talk.h"
#include <sys/stat.h>
#define CONSOLE "/dev/console" /* Where to write errors if talkd exits */
/*
* Each conversationalist gets one of these
*/
typedef struct talk {
char user[NAMESIZ]; /* The user */
char other[NAMESIZ]; /* Who they are talking to */
char tty[LINESIZ]; /* The line of the PARTNER */
long types; /* Msgtype index */
int pid; /* pid of the user's talk process (what else?)*/
struct talk *next;
} TALK;
int msqid;
char *program, errbuf[BUFSIZ];
void exit(), free();
main(argc, argv)
int argc;
char **argv;
{
TALK *Head, *Addper(), *Delper();
key_t msgkey, ftok();
long ctl, Link();
int i, avail, Finish();
program = *argv;
if (fork())
exit(0);
setpgrp();
for (i = 0; i < NSIG; i++)
signal(i, Finish);
msgkey = ftok(TALK_PATH, MAGIC_ID);
if ((msqid = msgget(msgkey, MSGQ_PERMS|IPC_CREAT|IPC_EXCL)) == -1) {
sprintf(errbuf, "%s: Unable to create talk msgqueue(%d)", program, errno);
Finish(NSIG);
}
Head = (TALK *)NULL;
/*
* The demon sits in this loop, waiting for a message
* from a potential user.
*/
for (;;) {
#ifdef DEBUG
fflush(stdout);
#endif
if (msgrcv(msqid, (struct msgbuf *)&dmsgbuf, MSGSIZ, (long)1, 0) == -1) {
sprintf(errbuf, "%s: msgrcv failure(%d)", program, errno);
Finish(NSIG);
}
/*
* If a message comes in with the pid '0', the talkdemon
* will remove its msgqueue and exit. This is needed for
* for reboot procedures. Note that process '0' is ALWAYS
* the swapper so there is no danger of a user accidently
* killing the demon.
*/
if (dmsgbuf.msgval.lines[PIDLOC] == 0) {
#ifdef DEBUG
printf("\nStoptalk message received\n");
#endif
sprintf(errbuf, "stopped by a command from stoptalk");
Finish(NSIG);
}
/*
* A message with a pid of '1' is a request for status
* message. Status info is returned on msgtype 3. Note
* that process '1' is ALWAYS /etc/init so it is safe to
* assume that a message with a pid of '1' is an info
* request.
*/
if (dmsgbuf.msgval.lines[PIDLOC] == 1) {
#ifdef DEBUG
printf("\nInfotalk status request message received\n");
#endif
Status(Head);
continue;
}
/*
* When a users exits talk, he sends a removal message to
* the demon. Removal messages are identified by a negative
* pid.
*/
if (dmsgbuf.msgval.lines[PIDLOC] < 0) {
#ifdef DEBUG
printf("\nRemoval message received\n");
#endif
Head = Delper(Head, dmsgbuf.msgval.lines[PIDLOC]);
continue;
}
#ifdef DEBUG
printf("\nInitiate message from: %s pid: %d to: %s\n",
dmsgbuf.msgval.mtext,
dmsgbuf.msgval.lines[PIDLOC],
&dmsgbuf.msgval.mtext[NAMESIZ]);
#endif
/*
* Determine the availability of the potential partner.
*/
avail = Find(&dmsgbuf.msgval.mtext[8], &dmsgbuf.msgval.mtext[TTYLOC]);
#ifdef DEBUG
printf("Availability of %s on %s is %d\n", &dmsgbuf.msgval.mtext[8], &dmsgbuf.msgval.mtext[TTYLOC], avail);
#endif
if (avail == TALKABLE) {
if ((Head = Addper(Head, &dmsgbuf)) == NULL) {
sprintf(errbuf, "%s: Unable to add user, malloc failure(%d)", program, errno);
Finish(NSIG);
}
/*
* Determine if the new user is an 'originator' or
* a 'responder' and assign them send and receive
* msgtypes.
*/
ctl = Link(Head, &dmsgbuf);
#ifdef DEBUG
printf("Sending %d %d %d to type 2\n",
dmsgbuf.msgval.lines[SEND],
dmsgbuf.msgval.lines[RECEIVE],
dmsgbuf.msgval.lines[CTL]);
#endif
}
/*
* We do this if the requested partner is unavailable.
*/
else {
dmsgbuf.msgval.lines[SEND] = avail;
ctl = -1;
}
/*
* Send an acknowledgement to the user informing him of the
* availability of his partner and the msgtypes to use for
* sending and receiving.
*/
dmsgbuf.mtype = 2;
if (msgsnd(msqid, (struct msgbuf *)&dmsgbuf, MSGSIZ, 0) == -1) {
sprintf(errbuf, "%s: msgsnd failure(%d)", program, errno);
Finish(NSIG);
}
/*
* If the most recent message was a 'responder', send
* a ctl message to the 'originator' telling him that
* his partner has answered. The ctl message includes
* the pid of the respondent.
*/
if (ctl > 0) {
#ifdef DEBUG
printf("Sending ctl message to %d\n", ctl);
#endif
dmsgbuf.mtype = ctl;
dmsgbuf.msgval.lines[PIDLOC] = Head->pid;
if (msgsnd(msqid, (struct msgbuf *)&dmsgbuf, MSGSIZ, 0) == -1) {
sprintf(errbuf, "%s: msgsnd failure(%d)", program, errno);
Finish(NSIG);
}
}
}
}
/*
* Finish - clean up
*
* Finish();
*
* Finish remove the msgqueue and exits. It is normally not called
* but is here in case the demon encounters an error.
*/
Finish(sig)
int sig;
{
FILE *fp, *fopen();
time_t tloc, time();
char *ctime();
int rmv;
if (sig != NSIG)
sprintf(errbuf, "caught signal #%d", sig);
rmv = msgctl(msqid, IPC_RMID, (struct msqid_ds *)NULL);
if ((fp = fopen(CONSOLE, "w")) != NULL) {
tloc = time((long *)0);
fprintf(fp, "talkdemon exiting: %s", ctime(&tloc));
fprintf(fp, "%s\n", errbuf);
if (rmv == 0) {
fprintf(fp, "talk msgqueue removed\n");
#ifdef DEBUG
printf("%s: msgqueue removed. demon exiting\n", program);
#endif
}
else {
fprintf(fp, "talk msgqueue not removed\n");
#ifdef DEBUG
printf("%s: msgqueue not removed. demon exiting\n", program);
#endif
}
}
exit(1);
}
/*
* Status - report the status of the talkdemon
*
* Status(Head);
* Head is a pointer to the first TALK structure in the user list
*
* Status runs through the current list of talk users and sends out one status
* msgbuf for each user. All messages are sent to msgtype 3 which is reserved
* for this use.
*
*/
Status(Head)
TALK *Head;
{
dmsgbuf.mtype = STATUS;
for (; Head; Head = Head->next) {
strncpy(dmsgbuf.msgval.mtext, Head->user, NAMESIZ);
strncpy(&dmsgbuf.msgval.mtext[NAMESIZ], Head->other, NAMESIZ);
strncpy(&dmsgbuf.msgval.mtext[TTYLOC], Head->tty, LINESIZ);
/*
* Do a little bit play to squeeze in some extra infomation.
* No entirely kosher, but it will only screw up in already
* hazardous situations. (types > 2^16)
*/
dmsgbuf.msgval.lines[PIDLOC] = Head->types;
dmsgbuf.msgval.lines[PIDLOC] <<= 16;
dmsgbuf.msgval.lines[PIDLOC] += Head->pid;
if (msgsnd(msqid, (struct msgbuf *)&dmsgbuf, MSGSIZ, 0) == -1) {
sprintf(errbuf, "%s: msgsnd failure(%d)", program, errno);
Finish(NSIG);
}
}
/*
* A message containing a pid of '0' indicates the end of the status
* information.
*/
dmsgbuf.msgval.lines[PIDLOC] = 0;
if (msgsnd(msqid, (struct msgbuf *)&dmsgbuf, MSGSIZ, 0) == -1) {
sprintf(errbuf, "%s: msgsnd failure(%d)", program, errno);
Finish(NSIG);
}
#ifdef DEBUG
printf("End-of-status message written\n");
#endif
}
/*
* Link - determine send and receive msgtypes for a TALK structure
*
* ctl = Link(Head, bufptr)
* ctl is a ctl type for the TALK structure
* Head is a pointer to the first TALK structure in the list,
* which is ALWAYS the most recently added.
* bufptr is a pointer to a DMSG buffer.
*
* The newest TALK structure is compared against the rest of the list to
* determine if the structure belongs to an 'originator' or a 'responder'.
* If it's the former, a send/receive/ctl msgtype triple is selected, if
* the latter, a send/receive pair is taken from the matched initiator's
* structure.
*
* If the TALK structure belongs to a 'responder', the ctl msgtype for the
* corresponding 'originator' is returned, otherwise a zero is returned.
*/
long
Link(Head, bufptr)
TALK *Head;
DMSG *bufptr;
{
TALK *ptr;
int i = 4;
/*
* See if there is a 'partner' for the new person.
* This is done by looking for a TALK structure with the same
* names, but reversed.
*/
for (ptr = Head->next; ptr; ptr = ptr->next) {
/*
* Perform a sanity check on the list. Occasionally
* talk process die without removing their TALK
* structures. Dunno why.
*
* If a TALK structure represents a dead process, remove
* it. Notice that the return value of Delper() is ignored.
* We can do this because ptr->pid will never be Head->pid.
*/
if (kill(ptr->pid, 0) == -1)
(void) Delper(Head, ptr->pid);
if (!strncmp(ptr->user, Head->other, NAMESIZ) &&
!strncmp(ptr->other, Head->user, NAMESIZ)) {
bufptr->msgval.lines[RECEIVE] = ptr->types;
bufptr->msgval.lines[SEND] = ptr->types + 1;
bufptr->msgval.lines[CTL] = 0;
bufptr->msgval.lines[PIDLOC] = ptr->pid;
/*
* NULL the tty pointers of the structures
* to show that they're connected.
*/
*Head->tty = NULL;
*ptr->tty = NULL;
Head->types = ptr->types;
return(ptr->types + 2);
}
}
/*
* If we got this far, the new person is an 'originator'.
*
* First, find an unused send/receive/ctl triple.
*/
do {
for (ptr = Head->next; ptr; ptr = ptr->next) {
if (i == ptr->types) {
i += 3;
break;
}
}
} while (ptr);
Head->types = i;
bufptr->msgval.lines[SEND] = i;
bufptr->msgval.lines[RECEIVE] = i + 1;
bufptr->msgval.lines[CTL] = i + 2;
return(0);
}
/*
* Find - locate the partner and determine their availability
*
* avail = Find(p1, p2);
* avail is the availability of the requested partner
* p1 is a pointer to the partner's name
* p2 is a pointer to the name of the partner's tty
*
* Find reads UTMP_FILE to get the tty line of the initiator's requested
* partner. If no tty was given, Find() fills it into p2.
*/
Find(partner, line)
char *partner, *line;
{
struct stat lstat;
struct utmp utmp;
char tmpbuf[18];
int fd, flag;
flag = 0;
if ((fd = open(UTMP_FILE, 0)) >= 0) {
while (read(fd, (char *)&utmp, sizeof(utmp)) == sizeof(utmp)) {
#ifndef OLDUTMP
if (utmp.ut_type != USER_PROCESS)
continue;
#endif
if (!strncmp(utmp.ut_line, line, LINESIZ) &&
strncmp(utmp.ut_name, partner, NAMESIZ)) {
flag = -1;
break;
}
else if (!strncmp(utmp.ut_name, partner, NAMESIZ)) {
flag++;
/* if line was speced and found him on this
line, make flag one even if he was already
found on another line */
if (*line != NULL &&
!strncmp(utmp.ut_line, line, LINESIZ)) {
flag = 1;
break;
}
/* safe to copy line if not speced since it
cant match again */
if (*line == NULL)
strcpy(line, utmp.ut_line);
#ifdef DEBUG
printf("%s found on %s\n", partner, line);
#endif
}
}
close(fd);
}
else {
sprintf(errbuf, "%s: cannot open %s(%d)", program, UTMP_FILE, errno);
Finish(NSIG);
}
if (flag == 0)
return(NOTLOGGEDON);
if (flag < 0)
return(NOTONLINE);
if (flag > 1)
return(LOGGEDMORE);
strcpy(tmpbuf, line);
strcpy(line, "/dev/");
strcat(line, tmpbuf);
stat(line, &lstat);
if (!(lstat.st_mode & 0002))
return(NOTWRITE);
return(TALKABLE);
}
/*
* Addper - add a new user to the list
*
* tp = Addper(hp, mp);
* tp is a pointer to new TALK structure
* hp is a pointer to the Head of the TALK list
* mp is a pointer to the dmsgbuf
*
* A new TALK structure is allocated and linked in at the head of the list.
* Data from the just-received dmsg is copied into the new structure.
* A pointer to the new structure is returned unless malloc() was unable
* to allocate space, in which case NULL is returned.
*/
TALK *
Addper(ptr, mptr)
TALK *ptr;
DMSG *mptr;
{
TALK *p;
char *malloc();
if ((p = (TALK *)malloc(sizeof(TALK))) == NULL)
return(NULL);
p->next = ptr;
strncpy(p->user, mptr->msgval.mtext, NAMESIZ);
strncpy(p->other, &mptr->msgval.mtext[NAMESIZ], NAMESIZ);
strncpy(p->tty, &mptr->msgval.mtext[TTYLOC], LINESIZ);
p->pid = mptr->msgval.lines[PIDLOC];
return(p);
}
/*
* Delper - remove somebody
*
* hp = Delper(p, pid);
* hp is a pointer to the (possibly new) head of the list
* p is a pointer to the head of the list
* pid is the pid of the structure to remove
*
* The person whose talk pid is given is removed from the list.
* The, possibly new, head of the list is returned.
*/
TALK *
Delper(Head, pid)
TALK *Head;
int pid;
{
TALK *ptr, *lastptr;
pid = abs(pid); /* Make sure pid is positive */
ptr = Head;
lastptr = (TALK *)NULL;
while (ptr->pid != pid) {
lastptr = ptr;
ptr = ptr->next;
if (ptr == (TALK *)NULL) /* Just to be safe */
return(Head);
}
if (lastptr)
lastptr->next = ptr->next;
else
Head = ptr->next;
#ifdef DEBUG
printf("Removing: %s %d from list\n", ptr->user, pid);
#endif
free((char *)ptr);
return(Head);
}
SHAR_EOF
fi
echo shar: extracting "'infotalk.c'" '(1673 characters)'
if test -f 'infotalk.c'
then
echo shar: will not over-write existing file "'infotalk.c'"
else
cat >'infotalk.c' <<\SHAR_EOF
/*
* infotalk - provide current status of the talk system
*
* AUTHOR
* Edward C. Bennett (edward at ukecc)
*
* Copyright 1985 by Edward C. Bennett
*
* Permission is given to alter this code as needed to adapt it to forign
* systems provided that this header is included and that the original
* author's name is preserved.
*/
#include <stdio.h>
#include "talk.h"
int msqid;
main(argc, argv)
int argc;
char *argv[];
{
key_t msgkey, ftok();
void exit();
int busy;
msgkey = ftok(TALK_PATH, MAGIC_ID);
if ((msqid = msgget(msgkey, MSGQ_PERMS)) == -1) {
fprintf(stderr, "%s: Nonexistant talk msgqueue\n", argv[0]);
exit(1);
}
dmsgbuf.msgval.lines[PIDLOC] = 1;
dmsgbuf.mtype = 1;
/*
* Send the request to the demon
*/
if (msgsnd(msqid, (struct msgbuf *)&dmsgbuf, MSGSIZ, 0) == -1) {
fprintf(stderr, "%s: msgsnd failure(%d)\n", argv[0], errno);
exit(1);
}
#ifdef DEBUG
printf("Info request sent to talkdemon\n");
#endif
printf("Talker Talkee TTY PID MTYPE\n");
do {
if (msgrcv(msqid, (struct msgbuf *)&dmsgbuf, MSGSIZ, (long)STATUS, 0) == -1) {
fprintf(stderr, "%s: msgrcv failure(%d)\n", argv[0], errno);
exit(1);
}
#ifdef DEBUG
printf("Info message received from talkdemon\n");
#endif
if (dmsgbuf.msgval.lines[PIDLOC]) {
printf("%-12s", dmsgbuf.msgval.mtext);
printf("%-12s", &dmsgbuf.msgval.mtext[NAMESIZ]);
printf("%-16s", &dmsgbuf.msgval.mtext[TTYLOC]);
printf("%5d", dmsgbuf.msgval.lines[PIDLOC] & 0177777);
printf(" %5d\n", dmsgbuf.msgval.lines[PIDLOC] >> 16);
busy++;
}
} while (dmsgbuf.msgval.lines[PIDLOC]);
if (!busy)
printf("No one is currently using talk\n");
}
SHAR_EOF
fi
echo shar: extracting "'stoptalk.c'" '(1814 characters)'
if test -f 'stoptalk.c'
then
echo shar: will not over-write existing file "'stoptalk.c'"
else
cat >'stoptalk.c' <<\SHAR_EOF
/*
* stoptalk - shut down the talk facility
*
* SYNOPSIS
* stoptalk
*
* Stoptalk is used to stop the talk facility. Prior to a reboot the
* current talk msgqueue needs to be removed manually because the
* reboot procedure does not clear system msgqueues. The talkdemon
* is incapable of removing its own msgqueue automatically because
* /etc/kill stops process with SIGKILL which cannot be caught.
*
* Stoptalk sends a message to the talkdemon instructing it to exit.
* If this fails, stoptalk tries to remove the msgsqueue itself.
*
* AUTHOR
* Edward C. Bennett (edward at ukecc)
*
* Copyright 1985 by Edward C. Bennett
*
* Permission is given to alter this code as needed to adapt it to forign
* systems provided that this header is included and that the original
* author's name is preserved.
*/
#include <stdio.h>
#include "talk.h"
main(argc, argv)
int argc;
char **argv;
{
key_t msgkey, ftok();
void exit();
int msqid;
msgkey = ftok(TALK_PATH, MAGIC_ID);
if ((msqid = msgget(msgkey, MSGQ_PERMS)) == -1) {
fprintf(stderr, "%s: Nonexistant talk msgqueue\n", *argv);
exit(1);
}
/*
* Set up a msg containing the pid '0'. This will signal
* the talkdemon to exit gracefully.
*/
dmsgbuf.msgval.lines[PIDLOC] = 0;
dmsgbuf.mtype = 1;
/*
* Send the 'kill' msg to the demon.
*/
if (msgsnd(msqid, (struct msgbuf *)&dmsgbuf, MSGSIZ, IPC_NOWAIT) == 0) {
/*
* Wait while the demon prints its exit message
*/
sleep(2);
exit(0);
}
/*
* The graceful method didn't work, get brutal
*/
fprintf(stderr, "%s: msgsnd failure(%d)\n", *argv, errno);
if (msgctl(msqid, IPC_RMID, (struct msqid_ds *)NULL) == 0) {
fprintf(stderr, "%s: Talk msgqueue removed\n", *argv);
exit(0);
}
fprintf(stderr, "%s: Unable to remove talk msgqueue\n", *argv);
exit(1);
}
SHAR_EOF
fi
: End of shell archive
exit 0
More information about the Mod.sources
mailing list