home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Source Code 1992 March
/
Source_Code_CD-ROM_Walnut_Creek_March_1992.iso
/
usenet
/
altsrcs
/
3
/
3576
< prev
next >
Wrap
Internet Message Format
|
1991-07-02
|
14KB
From: bob@snuffy.uucp (Bob Smith)
Newsgroups: alt.sources
Subject: talk for Sun without sockets
Message-ID: <1991Jul3.003000.557@snuffy.uucp>
Date: 3 Jul 91 00:30:00 GMT
I've just finished reworking a talk program that uses named pipes,
to work on a Sun 2/50 (SunOS3.5). It was originally posted for
SysV type machines, and still maintains all of the original
source. You may be asking why do I want this? Well you probably
don't since SunOS comes with a talk program, but the as supplied
SunOS talk requires that sockets be alive on your machine, i.e.
you're connected to a network with your ethernet port 'up'. If
you're NOT connected to a network and your ethernet port is 'down',
you could use this, trust me:-)
(don't forget to take the signature off the bottom!)
---- cut here ---- cut here ---- cut here ---- cut here ----
/*
* Subject: v06i010: A "talk" for system V.2 (sysVtalkA)
* Newsgroups: mod.sources
* Approved: rs@mirror.UUCP
*
* Submitted by: genrad!decvax!mcvax!mdtch!watson (James Watson)
* Mod.sources: Volume 6, Issue 10
* Archive-name: sysVtalkA
*
* [ This is the first of two different USG talk programs. This one
* uses FIFO's (named pipes). I have not tried it out. -r$]
*
* ---
* I wrote the following because I got tired of having only write (ugh) on
* SVR2. It could stand some improvement, especially in the area of
* handling unlikely errors, but...
*
* James Watson
* Modulator SA, Koenizstrasse 194,
* 3097 Liebefeld-Bern, Switzerland
*
* ---
* Modified for a Sun 2/50 (SunOS 3.5);
* Just enter this to compile it:
*
* cc -O -s -DBSD talk.c -lcurses -ltermcap -o talk
*
* With SunOS already possessing a 'talk' program, you may be
* asking; 'what the hell do I need this for...'. Well you
* probably don't! This is for the special condition where
* you want to have a talk like program, but your unfortunate
* enough to NOT be connected to a network; which means you
* probably have 'ifconfig {ie0|ec0|le0} ... down' in your
* /etc/rc.boot file; which further translates into the fact
* that socket calls on your machine will be dead, thereby
* negating any usefulness that might be derived from the as
* supplied SunOS 'talk'...
*
* As Jim says above; 'it could stand some improvement...'
*
* snuffy!bob (Bob Smith)
* +1 508 670-6712
*/
#include <sys/types.h>
#include <fcntl.h>
#include <utmp.h>
#include <signal.h>
#include <string.h>
#include <curses.h>
#define BELL 7
#ifdef BSD
struct utmp *getutent();
int utmp_fp = 0, tflags;
#else
extern struct utmp *getutent();
#endif
extern char *ttyname();
extern unsigned alarm();
char *make_str();
char out_fn[20];
terminate ()
{
unlink (out_fn);
move (LINES-1, 0);
refresh ();
endwin ();
#ifdef BSD
fcntl (0, F_GETFL, &tflags);
fcntl (0, F_SETFL, tflags & ~FNDELAY);
#endif
exit (0);
}
catch_alarm () {}
main (argc, argv)
int argc;
char *argv[];
{
struct utmp *utp, ctp;
#ifndef BSD
struct termio tflags;
#endif
int in_pipe, out_pipe, their_fd, count = 0;
WINDOW *in_win, *out_win;
char in_c, their_tty[20], in_fn[20];
char *my_tty, my_name[L_cuserid+1], call_mesg[100];
/* Check validity of arguments. */
if (argc < 2 || argc > 3) {
fprintf (stderr, "Use: %s username [ttyn]\n", *argv);
exit (-1);
}
/* We will need to know the tty device name... */
if ((my_tty = ttyname (0)) == NULL) {
fprintf (stderr, "Sorry, I cannot figure out what tty you are on.\n");
exit (-1);
}
/* But only the last component of it. */
my_tty = strrchr (my_tty, '/') + 1;
/*
* Sanity check. You cannot ask to talk to yourself unless you specify
* some tty other than the one you are on.
*/
if (!strncmp (argv[1], cuserid(my_name), L_cuserid) &&
(argc == 2 || !strncmp (my_tty, argv[2], 12))) {
fprintf (stderr, "Only crazy people talk to themselves...\n");
exit (-1);
}
/* Now find out if the requested user is logged in. */
while (utp = getutent()) {
/*
* There are three criteria here:
* 1. The entry must be for a user process.
* 2. The user name must match the first argument.
* 3. If a second argument was given, the tty name
* must match that argument.
* We have to allow for the possibility that the request is not
* specific enough - i.e. no tty name was given, and the requested
* user is logged in more than once.
*/
#ifdef BSD
if (!strncmp (utp->ut_name, argv[1], 8) &&
#else
if (utp->ut_type == USER_PROCESS &&
!strncmp (utp->ut_user, argv[1], 8) &&
#endif
(argc == 2 || !strncmp (utp->ut_line, argv[2], 12))) {
if (count++) {
fprintf (stderr,
"User '%s' logged in more than once. Use \"%s %s ttyn\".\n",
argv[1], argv[0], argv[1]);
exit (-1);
}
else ctp = *utp;
}
}
if (!count) {
fprintf (stderr, "%s not currently logged in", argv[1]);
if (argc == 3)
fprintf (stderr, " on %s", argv[2]);
fprintf (stderr, ".\n");
exit (-1);
}
/* Finally found someone. Make sure we are allowed to talk to them. */
sprintf (their_tty, "/dev/%s", ctp.ut_line);
if ((their_fd = open (their_tty, O_WRONLY, 0)) < 0) {
fprintf (stderr, "Sorry, no write permission on %s.\n", their_tty);
exit (-1);
}
/*
* At this point, we know we are going to at least try to talk to
* someone. Now we do the complete initialization.
*
* Initialize curses. Note that the signal traps set set immediately
* upon return from iniscr() - before calling any other curses
* functions. No sense in leaving a bigger window than necessary.
*/
initscr ();
signal (SIGINT, terminate);
signal (SIGKILL, terminate);
cbreak ();
noecho ();
/* Declare the windows to be used for the conversation. */
in_win = subwin (stdscr, (LINES-1)/2, COLS, 0, 0);
out_win = subwin (stdscr, (LINES-1)/2, COLS, (LINES-1)/2+1, 0);
scrollok (out_win, TRUE);
scrollok (in_win, TRUE);
idlok (out_win, TRUE);
idlok (in_win, TRUE);
clear ();
wclear (in_win);
wclear (out_win);
mvaddstr ((LINES-1)/2, 0, make_str ('-', COLS-1));
wmove (in_win, 0, 0);
wmove (out_win, 0, 0);
refresh ();
/*
* Here is a decidedly shaky trick to play. Curses only gives you two
* choices for keyboard reads - completely blocking, or completely
* non-blocking. Obviously, we cannot use blocking reads, because we
* need to get the input from the other person even when we have
* nothing to say. On the other hand, completely non-blocking reads
* would put the program in a very tight loop looking at the keyboard
* and the pipe for input - which would put an unnecessarily heavy
* load on the system. This loop could be slowed down with some kind
* of a sleep() or napms(), but that would make the screen look sort
* of jumpy. System V termio supports timed out reads, so I choose
* to use them as the gating factor in the loop. I am assuming that
* when I call cbreak(), curses sets ~ICANON, VMIN = 1 and VTIME = 1.
* If I now sneak in here and set VMIN = 0 and VTIME = 10, keyboard
* reads will time out if there is no input in 1 second, and I can
* pick up the input from the pipe. The advantage over a simple
* sleep() is that if there IS input from the keyboard, the read will
* return in less than 1 second, and I can read from the pipe that
* much sooner. This makes the screen updating less jumpy.
*
* BSD Note: Since SunOS doesn't do this; (at least not with the
* stock BSD derived libraries), I resort to using usleep() to
* the CPU burden down. (See farther below.)
*/
#ifdef BSD
fcntl (0, F_GETFL, &tflags);
fcntl (0, F_SETFL, tflags | FNDELAY);
#else
ioctl (0, TCGETA, &tflags);
tflags.c_cc[VMIN] = 0;
tflags.c_cc[VTIME] =10;
ioctl (0, TCSETA, &tflags);
#endif
/*
* The sequence of events during the startup is important, and is
* relatiely interesting. The situation is this:
* 1. The user has requested a talk connection to someone.
* 2. We don't know yet if this user is trying to originiate a
* connection, or if they are responding to a request from
* the other person.
* 3. If this is an originating request, we have to notify the
* other user of it, and then wait for that user to respond.
* 4. If this is a response to a request from the other user, we
* must not fool around with notification, but rather go
* straight into the conversation.
* We will use several characteristics of Unix pipes to resolve this:
* 1. Attempting to open() a named pipe that does not exist will
* fail. (I hope this is not a great surprise...)
* 2. Opening a named pipe for reading, with O_NDELAY clear, will
* succeed immediately if someone else already has that pipe
* open for writing.
* 3. Opening a named pipe for writing, with O_NDELAY clear, will
* block the process until someone opens that pipe for reading.
* So, what we will actually do is this:
* 1. Make up a couple of pipe names, based on the tty names
* involved. Note that this entire sequence could get screwed
* up if someone other than the talk programs opened, deleted,
* or otherwise screwed up our pipes. We therefore choose to
* use dot files in /tmp, so they are not so visible.
* 2. Try to open the pipe that should be created by the other
* user's talk program. If this open succeeds, then we must
* be replying to a call; if it fails, we are originating one.
* 3. If the open fails, send a message to the other user, set an
* alarm for 30 seconds, and block the process by trying to
* open the pipe we created. If the user responds, the other
* talk program will open our pipe, and then our open will
* succeed. If there is no response the alarm will fire, and
* the open will return -1.
*/
sprintf (out_fn, "/tmp/.talk_%s", my_tty);
sprintf (in_fn, "/tmp/.talk_%s", ctp.ut_line);
mknod (out_fn, 0010644, 0);
/* See if they are waiting on us... */
if ((in_pipe = open (in_fn, O_RDONLY | O_NDELAY, 0644)) >= 0)
/* They were, so we can open the other pipe and get to work. */
out_pipe = open (out_fn, O_WRONLY, 0644);
else {
/* Nope, so we are the ones that have to do all the work... */
sprintf (call_mesg,"\n\n\
[%s is requesting a 'talk' link with you.]\n\
[Type 'talk %s %s' to reply.]%c\n",
my_name, my_name, my_tty, BELL);
count = 0;
do {
write (their_fd, call_mesg, (unsigned) strlen(call_mesg));
mvwprintw (in_win, 0, 0, "[%d ringy-ding", ++count);
if (count == 1)
wprintw (in_win, "y]\n");
else
wprintw (in_win, "ies]\n");
wrefresh (in_win);
/*
* Note that the alarm catching routine is a no-op. All the
* real work is done right here; we are simply using the alarm
* to unblock the open() periodically.
*/
signal (SIGALRM, catch_alarm);
alarm ((unsigned) 30);
/* We block here waiting on a reply... */
out_pipe = open (out_fn, O_WRONLY, 0644);
} while (count < 4 && out_pipe < 0);
close (their_fd);
if (out_pipe < 0) {
waddstr (in_win, "[No answer. Giving up.]");
wrefresh (in_win);
terminate ();
}
/* OK, they answered, so open the other pipe. */
in_pipe = open (in_fn, O_RDONLY | O_NDELAY, 0644);
}
/*
* We are now ready to talk. Both processes will hold the named pipes
* open forever from this point, so we can unlink() them, to avoid any
* chance that someone will see them, and say "Gee, I wonder what those
* are? Maybe I'll cat /etc/termcap to them to see what happens..."
*/
unlink (out_fn);
wmove (in_win, 0, 0);
wclrtoeol (in_win);
waddstr (in_win, "[Connected]\n");
wrefresh (in_win);
signal (SIGPIPE, terminate);
/*
* Believe it or not, the preceding 100+ lines of code were necessary
* just to set things up for the following 10 line loop.
*
* The infinite loop is exited only upon receipt of a signal. The
* possiblilites are:
* 1. This user terminates the conversation by typing the
* interrupt or quit character.
* 2. The other user terminates the conversation, thereby
* closing the pipes. The next attempt to write a character
* on the output pipe generates SIGPIPE.
* All three signals are trapped to the terminate() routine.
*
* Note the use of if() when reading the keyboard, and while() when
* reading the pipe. Since we are using the keyboard timeout to
* control the entire loop, we must drain the pipe each time the
* keyboard read is satisfied or times out.
*
* BSD Note: This thing is a CPU hog without the usleep() function
* call since it uses non-blocking reads for both the keyboard and
* the pipe. It should be 'prettied' up somehow; possibly by
* rewriting the whole thing into a 2 process program, 1 to read
* the keyboard and write to the out_pipe, the other to read the
* in_pipe. That would allow blocking reads to be used.
*
* Also note the addition of wclrtoeol(); a rather crude hack that
* makes sure the last line of each window is cleared when the
* window scrolls. There's probably a better way than that too,
* but I'm not a curses wizard...
*/
for (;;) {
if (read (0, &in_c, 1) > 0) {
waddch (out_win, in_c);
#ifdef BSD
wclrtoeol (out_win);
#endif
wrefresh (out_win);
write (out_pipe, &in_c, 1);
}
while (read (in_pipe, &in_c, 1) > 0) {
waddch (in_win, in_c);
#ifdef BSD
wclrtoeol (in_win);
#endif
wrefresh (in_win);
}
#ifdef BSD
usleep((unsigned)100000L);
#endif
}
}
char *
make_str (c, size)
char c;
int size;
{
char *s;
static char t[BUFSIZ];
if (size > BUFSIZ) size = BUFSIZ - 1;
s = t;
while (size--) *s++ = c;
*s = '\0';
return t;
}
#ifdef BSD
struct utmp *
getutent ()
{
static struct utmp ttmp;
if (utmp_fp == 0) {
utmp_fp = open ("/etc/utmp", O_RDONLY);
if (utmp_fp < 0) {
fprintf (stderr, "Cannot open /etc/utmp\n");
exit (-1);
}
}
if (read (utmp_fp, &ttmp, sizeof(ttmp)) <= 0) {
close (utmp_fp);
utmp_fp = 0;
return (struct utmp *)0;
}
else
return (&ttmp);
}
#endif
--
\ Bob Smith \________________________________________
\ 835 Mammoth Rd. \ uucp: ...!wang.com!snuffy!bob
\ Dracut, MA. 01826 \ Office && voice mail: +1 508 670-6712