sources-request@mirror.UUCP (06/20/86)
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