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 >
Internet Message Format  |  1991-07-02  |  14KB

  1. From: bob@snuffy.uucp (Bob Smith)
  2. Newsgroups: alt.sources
  3. Subject: talk for Sun without sockets
  4. Message-ID: <1991Jul3.003000.557@snuffy.uucp>
  5. Date: 3 Jul 91 00:30:00 GMT
  6.  
  7. I've just finished reworking a talk program that uses named pipes,
  8. to work on a Sun 2/50 (SunOS3.5).  It was originally posted for
  9. SysV type machines, and still maintains all of the original
  10. source.  You may be asking why do I want this?  Well you probably
  11. don't since SunOS comes with a talk program, but the as supplied
  12. SunOS talk requires that sockets be alive on your machine, i.e.
  13. you're connected to a network with your ethernet port 'up'.  If
  14. you're NOT connected to a network and your ethernet port is 'down',
  15. you could use this, trust me:-)
  16.  
  17. (don't forget to take the signature off the bottom!)
  18. ---- cut here ---- cut here ---- cut here ---- cut here ----
  19. /*
  20.  * Subject: v06i010:  A "talk" for system V.2 (sysVtalkA)
  21.  * Newsgroups: mod.sources
  22.  * Approved: rs@mirror.UUCP
  23.  *
  24.  * Submitted by: genrad!decvax!mcvax!mdtch!watson (James Watson)
  25.  * Mod.sources: Volume 6, Issue 10
  26.  * Archive-name: sysVtalkA
  27.  *
  28.  * [ This is the first of two different USG talk programs.  This one
  29.  *   uses FIFO's (named pipes).  I have not tried it out.  -r$]
  30.  *
  31.  * ---
  32.  * I wrote the following because I got tired of having only write (ugh) on
  33.  * SVR2.  It could stand some improvement, especially in the area of
  34.  * handling unlikely errors, but...
  35.  *
  36.  *    James Watson
  37.  *    Modulator SA, Koenizstrasse 194,
  38.  *    3097 Liebefeld-Bern, Switzerland
  39.  *
  40.  * ---
  41.  * Modified for a Sun 2/50 (SunOS 3.5);
  42.  * Just enter this to compile it:
  43.  *
  44.  *    cc -O -s -DBSD talk.c -lcurses -ltermcap -o talk
  45.  *
  46.  * With SunOS already possessing a 'talk' program, you may be
  47.  * asking; 'what the hell do I need this for...'.  Well you
  48.  * probably don't!  This is for the special condition where
  49.  * you want to have a talk like program, but your unfortunate
  50.  * enough to NOT be connected to a network; which means you
  51.  * probably have 'ifconfig {ie0|ec0|le0} ... down' in your
  52.  * /etc/rc.boot file; which further translates into the fact
  53.  * that socket calls on your machine will be dead, thereby
  54.  * negating any usefulness that might be derived from the as
  55.  * supplied SunOS 'talk'...
  56.  *
  57.  * As Jim says above; 'it could stand some improvement...'
  58.  *
  59.  *      snuffy!bob (Bob Smith)
  60.  *    +1 508 670-6712
  61.  */
  62.  
  63. #include <sys/types.h>
  64. #include <fcntl.h>
  65. #include <utmp.h>
  66. #include <signal.h>
  67. #include <string.h>
  68. #include <curses.h>
  69.  
  70. #define BELL 7
  71.  
  72. #ifdef BSD
  73. struct utmp *getutent();
  74. int utmp_fp = 0, tflags;
  75. #else
  76. extern struct utmp *getutent();
  77. #endif
  78.  
  79. extern char *ttyname();
  80. extern unsigned alarm();
  81.  
  82. char *make_str();
  83. char out_fn[20];
  84.  
  85. terminate ()
  86. {
  87.     unlink (out_fn);
  88.     move (LINES-1, 0);
  89.     refresh ();
  90.     endwin ();
  91. #ifdef BSD
  92.     fcntl (0, F_GETFL, &tflags);
  93.     fcntl (0, F_SETFL, tflags & ~FNDELAY);
  94. #endif
  95.     exit (0);
  96. }
  97.  
  98. catch_alarm () {}
  99.  
  100. main (argc, argv)
  101. int argc;
  102. char *argv[];
  103. {
  104.     struct utmp *utp, ctp;
  105. #ifndef BSD
  106.     struct termio tflags;
  107. #endif
  108.     int in_pipe, out_pipe, their_fd, count = 0;
  109.     WINDOW *in_win, *out_win;
  110.     char in_c, their_tty[20], in_fn[20];
  111.     char *my_tty, my_name[L_cuserid+1], call_mesg[100];
  112.  
  113.     /* Check validity of arguments. */
  114.     if (argc < 2 || argc > 3) {
  115.         fprintf (stderr, "Use: %s username [ttyn]\n", *argv);
  116.         exit (-1);
  117.     }
  118.  
  119.     /* We will need to know the tty device name... */
  120.     if ((my_tty = ttyname (0)) == NULL) {
  121.         fprintf (stderr, "Sorry, I cannot figure out what tty you are on.\n");
  122.         exit (-1);
  123.     }
  124.  
  125.     /* But only the last component of it. */
  126.     my_tty = strrchr (my_tty, '/') + 1;
  127.  
  128.     /*
  129.      * Sanity check.  You cannot ask to talk to yourself unless you specify
  130.      * some tty other than the one you are on.
  131.      */
  132.     if (!strncmp (argv[1], cuserid(my_name), L_cuserid) &&
  133.       (argc == 2 || !strncmp (my_tty, argv[2], 12))) {
  134.         fprintf (stderr, "Only crazy people talk to themselves...\n");
  135.         exit (-1);
  136.     }
  137.  
  138.     /* Now find out if the requested user is logged in. */
  139.     while (utp = getutent()) {
  140.  
  141.         /*
  142.          * There are three criteria here:
  143.          *    1. The entry must be for a user process.
  144.          *    2. The user name must match the first argument.
  145.          *    3. If a second argument was given, the tty name
  146.          *       must match that argument.
  147.          * We have to allow for the possibility that the request is not
  148.          * specific enough - i.e. no tty name was given, and the requested
  149.          * user is logged in more than once.
  150.          */
  151. #ifdef BSD
  152.         if (!strncmp (utp->ut_name, argv[1], 8) &&
  153. #else
  154.         if (utp->ut_type == USER_PROCESS &&
  155.           !strncmp (utp->ut_user, argv[1], 8) &&
  156. #endif
  157.           (argc == 2 || !strncmp (utp->ut_line, argv[2], 12))) {
  158.             if (count++) {
  159.                 fprintf (stderr,
  160.                   "User '%s' logged in more than once.  Use \"%s %s ttyn\".\n",
  161.                   argv[1], argv[0], argv[1]);
  162.                 exit (-1);
  163.             }
  164.             else ctp = *utp;
  165.         }
  166.     }
  167.  
  168.     if (!count) {
  169.         fprintf (stderr, "%s not currently logged in", argv[1]);
  170.         if (argc == 3)
  171.             fprintf (stderr, " on %s", argv[2]);
  172.         fprintf (stderr, ".\n");
  173.         exit (-1);
  174.     }
  175.  
  176.     /* Finally found someone.  Make sure we are allowed to talk to them. */
  177.     sprintf (their_tty, "/dev/%s", ctp.ut_line);
  178.     if ((their_fd = open (their_tty, O_WRONLY, 0)) < 0) {
  179.         fprintf (stderr, "Sorry, no write permission on %s.\n", their_tty);
  180.         exit (-1);
  181.     }
  182.  
  183.     /*
  184.      * At this point, we know we are going to at least try to talk to
  185.      * someone.  Now we do the complete initialization.
  186.      *
  187.      * Initialize curses.  Note that the signal traps set set immediately
  188.      * upon return from iniscr() - before calling any other curses
  189.      * functions.  No sense in leaving a bigger window than necessary.
  190.      */
  191.     initscr ();
  192.     signal (SIGINT, terminate);
  193.     signal (SIGKILL, terminate);
  194.     cbreak ();
  195.     noecho ();
  196.  
  197.     /* Declare the windows to be used for the conversation. */
  198.     in_win = subwin (stdscr, (LINES-1)/2, COLS, 0, 0);
  199.     out_win = subwin (stdscr, (LINES-1)/2, COLS, (LINES-1)/2+1, 0);
  200.     scrollok (out_win, TRUE);
  201.     scrollok (in_win, TRUE);
  202.     idlok (out_win, TRUE);
  203.     idlok (in_win, TRUE);
  204.     clear ();
  205.     wclear (in_win);
  206.     wclear (out_win);
  207.     mvaddstr ((LINES-1)/2, 0, make_str ('-', COLS-1));
  208.     wmove (in_win, 0, 0);
  209.     wmove (out_win, 0, 0);
  210.     refresh ();
  211.  
  212.     /*
  213.      * Here is a decidedly shaky trick to play.  Curses only gives you two
  214.      * choices for keyboard reads - completely blocking, or completely
  215.      * non-blocking.  Obviously, we cannot use blocking reads, because we
  216.      * need to get the input from the other person even when we have
  217.      * nothing to say.  On the other hand, completely non-blocking reads
  218.      * would put the program in a very tight loop looking at the keyboard
  219.      * and the pipe for input - which would put an unnecessarily heavy
  220.      * load on the system.  This loop could be slowed down with some kind
  221.      * of a sleep() or napms(), but that would make the screen look sort
  222.      * of jumpy.  System V termio supports timed out reads, so I choose
  223.      * to use them as the gating factor in the loop.  I am assuming that
  224.      * when I call cbreak(), curses sets ~ICANON, VMIN = 1 and VTIME = 1.
  225.      * If I now sneak in here and set VMIN = 0 and VTIME = 10, keyboard
  226.      * reads will time out if there is no input in 1 second, and I can
  227.      * pick up the input from the pipe.  The advantage over a simple
  228.      * sleep() is that if there IS input from the keyboard, the read will
  229.      * return in less than 1 second, and I can read from the pipe that
  230.      * much sooner.  This makes the screen updating less jumpy.
  231.      *
  232.      * BSD Note: Since SunOS doesn't do this; (at least not with the
  233.      * stock BSD derived libraries), I resort to using usleep() to
  234.      * the CPU burden down.  (See farther below.)
  235.      */
  236. #ifdef BSD
  237.     fcntl (0, F_GETFL, &tflags);
  238.     fcntl (0, F_SETFL, tflags | FNDELAY);
  239. #else
  240.     ioctl (0, TCGETA, &tflags);
  241.     tflags.c_cc[VMIN] = 0;
  242.     tflags.c_cc[VTIME] =10;
  243.     ioctl (0, TCSETA, &tflags);
  244. #endif
  245.  
  246.     /*
  247.      * The sequence of events during the startup is important, and is
  248.      * relatiely interesting.  The situation is this:
  249.      *        1. The user has requested a talk connection to someone.
  250.      *        2. We don't know yet if this user is trying to originiate a
  251.      *          connection, or if they are responding to a request from
  252.      *          the other person.
  253.      *        3. If this is an originating request, we have to notify the
  254.      *           other user of it, and then wait for that user to respond.
  255.      *        4. If this is a response to a request from the other user, we
  256.      *           must not fool around with notification, but rather go
  257.      *           straight into the conversation.
  258.      * We will use several characteristics of Unix pipes to resolve this:
  259.      *        1. Attempting to open() a named pipe that does not exist will
  260.      *          fail.  (I hope this is not a great surprise...)
  261.      *        2. Opening a named pipe for reading, with O_NDELAY clear, will
  262.      *          succeed immediately if someone else already has that pipe
  263.      *           open for writing.
  264.      *        3. Opening a named pipe for writing, with O_NDELAY clear, will
  265.      *           block the process until someone opens that pipe for reading.
  266.      * So, what we will actually do is this:
  267.      *        1. Make up a couple of pipe names, based on the tty names
  268.      *           involved.  Note that this entire sequence could get screwed
  269.      *           up if someone other than the talk programs opened, deleted,
  270.      *          or otherwise screwed up our pipes.  We therefore choose to
  271.      *           use dot files in /tmp, so they are not so visible.
  272.      *        2. Try to open the pipe that should be created by the other
  273.      *           user's talk program.  If this open succeeds, then we must
  274.      *           be replying to a call; if it fails, we are originating one.
  275.      *        3. If the open fails, send a message to the other user, set an
  276.      *           alarm for 30 seconds, and block the process by trying to
  277.      *          open the pipe we created.  If the user responds, the other
  278.      *          talk program will open our pipe, and then our open will
  279.      *           succeed.  If there is no response the alarm will fire, and
  280.      *          the open will return -1.
  281.      */
  282.     sprintf (out_fn, "/tmp/.talk_%s", my_tty);
  283.     sprintf (in_fn, "/tmp/.talk_%s", ctp.ut_line);
  284.     mknod (out_fn, 0010644, 0);
  285.  
  286.     /* See if they are waiting on us... */
  287.     if ((in_pipe = open (in_fn, O_RDONLY | O_NDELAY, 0644)) >= 0)
  288.  
  289.         /* They were, so we can open the other pipe and get to work. */
  290.         out_pipe = open (out_fn, O_WRONLY, 0644);
  291.     else {
  292.  
  293.         /* Nope, so we are the ones that have to do all the work... */
  294.         sprintf (call_mesg,"\n\n\
  295. [%s is requesting a 'talk' link with you.]\n\
  296. [Type 'talk %s %s' to reply.]%c\n",
  297.             my_name, my_name, my_tty, BELL);
  298.         count = 0;
  299.         do {
  300.             write (their_fd, call_mesg, (unsigned) strlen(call_mesg));
  301.             mvwprintw (in_win, 0, 0, "[%d ringy-ding", ++count);
  302.             if (count == 1)
  303.                 wprintw (in_win, "y]\n");
  304.             else
  305.                 wprintw (in_win, "ies]\n");
  306.             wrefresh (in_win);
  307.  
  308.             /*
  309.              * Note that the alarm catching routine is a no-op.  All the
  310.              * real work is done right here; we are simply using the alarm
  311.              * to unblock the open() periodically.
  312.              */
  313.             signal (SIGALRM, catch_alarm);
  314.             alarm ((unsigned) 30);
  315.  
  316.             /* We block here waiting on a reply... */
  317.             out_pipe = open (out_fn, O_WRONLY, 0644);
  318.         } while (count < 4 && out_pipe < 0);
  319.         close (their_fd);
  320.         if (out_pipe < 0) {
  321.             waddstr (in_win, "[No answer.  Giving up.]");
  322.             wrefresh (in_win);
  323.             terminate ();
  324.         }
  325.  
  326.         /* OK, they answered, so open the other pipe. */
  327.         in_pipe = open (in_fn, O_RDONLY | O_NDELAY, 0644);
  328.     }
  329.  
  330.     /*
  331.      * We are now ready to talk.  Both processes will hold the named pipes
  332.      * open forever from this point, so we can unlink() them, to avoid any
  333.      * chance that someone will see them, and say "Gee, I wonder what those
  334.      * are?  Maybe I'll cat /etc/termcap to them to see what happens..."
  335.      */
  336.     unlink (out_fn);
  337.     wmove (in_win, 0, 0);
  338.     wclrtoeol (in_win);
  339.     waddstr (in_win, "[Connected]\n");
  340.     wrefresh (in_win);
  341.     signal (SIGPIPE, terminate);
  342.  
  343.     /*
  344.      * Believe it or not, the preceding 100+ lines of code were necessary
  345.      * just to set things up for the following 10 line loop.
  346.      *
  347.      * The infinite loop is exited only upon receipt of a signal.  The
  348.      * possiblilites are:
  349.      *        1. This user terminates the conversation by typing the
  350.      *           interrupt or quit character.
  351.      *        2. The other user terminates the conversation, thereby
  352.      *           closing the pipes.  The next attempt to write a character
  353.      *           on the output pipe generates SIGPIPE.
  354.      * All three signals are trapped to the terminate() routine.
  355.      *
  356.      * Note the use of if() when reading the keyboard, and while() when
  357.      * reading the pipe.  Since we are using the keyboard timeout to
  358.      * control the entire loop, we must drain the pipe each time the
  359.      * keyboard read is satisfied or times out.
  360.      *
  361.      * BSD Note: This thing is a CPU hog without the usleep() function
  362.      * call since it uses non-blocking reads for both the keyboard and
  363.      * the pipe.  It should be 'prettied' up somehow; possibly by
  364.      * rewriting the whole thing into a 2 process program, 1 to read
  365.      * the keyboard and write to the out_pipe, the other to read the
  366.      * in_pipe.  That would allow blocking reads to be used.
  367.      *
  368.      * Also note the addition of wclrtoeol(); a rather crude hack that
  369.      * makes sure the last line of each window is cleared when the
  370.      * window scrolls.  There's probably a better way than that too,
  371.      * but I'm not a curses wizard...
  372.      */
  373.     for (;;) {
  374.         if (read (0, &in_c, 1) > 0) {
  375.             waddch (out_win, in_c);
  376. #ifdef BSD
  377.             wclrtoeol (out_win);
  378. #endif
  379.             wrefresh (out_win);
  380.             write (out_pipe, &in_c, 1);
  381.         }
  382.         while (read (in_pipe, &in_c, 1) > 0) {
  383.             waddch (in_win, in_c);
  384. #ifdef BSD
  385.             wclrtoeol (in_win);
  386. #endif
  387.             wrefresh (in_win);
  388.         }
  389. #ifdef BSD
  390.         usleep((unsigned)100000L);
  391. #endif
  392.     }
  393. }
  394.  
  395. char *
  396. make_str (c, size)
  397. char c;
  398. int size;
  399. {
  400.     char *s;
  401.     static char t[BUFSIZ];
  402.  
  403.     if (size > BUFSIZ) size = BUFSIZ - 1;
  404.     s = t;
  405.     while (size--) *s++ = c;
  406.     *s = '\0';
  407.     return t;
  408. }
  409.  
  410. #ifdef BSD
  411. struct utmp *
  412. getutent ()
  413. {
  414.     static struct utmp ttmp;
  415.  
  416.     if (utmp_fp == 0) {
  417.         utmp_fp = open ("/etc/utmp", O_RDONLY);
  418.         if (utmp_fp < 0) {
  419.             fprintf (stderr, "Cannot open /etc/utmp\n");
  420.             exit (-1);
  421.         }
  422.     }
  423.  
  424.     if (read (utmp_fp, &ttmp, sizeof(ttmp)) <= 0) {
  425.         close (utmp_fp);
  426.         utmp_fp = 0;
  427.         return (struct utmp *)0;
  428.     }
  429.     else
  430.         return (&ttmp);
  431. }
  432. #endif
  433.  
  434. -- 
  435.  \ Bob Smith         \________________________________________
  436.   \ 835 Mammoth Rd.   \ uucp: ...!wang.com!snuffy!bob
  437.    \ Dracut, MA. 01826 \ Office && voice mail: +1 508 670-6712
  438.