home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-09-11 | 36.2 KB | 1,463 lines |
- Subject: v06i011: A "talk" for System V (sysVtalkB)
- Newsgroups: mod.sources
- Approved: rs@mirror.UUCP
-
- 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@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@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@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@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@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@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
-