home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1625 / say.c < prev    next >
C/C++ Source or Header  |  1990-12-28  |  9KB  |  341 lines

  1. /*
  2. ** say - front-end for write(1) that records messages
  3. **
  4. ** Copyright (C) 1990 by Jef Poskanzer.
  5. **
  6. ** Permission to use, copy, modify, and distribute this software and its
  7. ** documentation for any purpose and without fee is hereby granted, provided
  8. ** that the above copyright notice appear in all copies and that both that
  9. ** copyright notice and this permission notice appear in supporting
  10. ** documentation.  This software is provided "as is" without express or
  11. ** implied warranty.
  12. */
  13.  
  14. #ifndef lint
  15. static char rcsid[] = "@(#) $Header: say.c,v 1.7 90/07/24 16:39:48 jef Exp $";
  16. #endif
  17.  
  18. #include <stdio.h>
  19. #include <sys/file.h>
  20. #include <sys/types.h>
  21. #include <sys/signal.h>
  22. #include <utmp.h>
  23. #include <sys/stat.h>
  24. #include <sys/wait.h>
  25. #include <pwd.h>
  26. #include <ctype.h>
  27. #include <errno.h>
  28. #include "libsaywha.h"
  29.  
  30. #ifndef _PATH_UTMP
  31. #define _PATH_UTMP "/etc/utmp"
  32. #endif
  33.  
  34. /* External routines. */
  35. extern int dup2(), fprintf(), printf(), strlen(), strncmp(), system(), vfork();
  36. extern time_t time();
  37. extern char *ctime(), *getenv(), *getlogin(), *mktemp(), *sprintf(), *strcpy(),
  38.     *ttyname();
  39. extern int errno;    /* in case errno.h fails to declare it */
  40.  
  41. /* Forward routines. */
  42. int cleanup();
  43. int search_utmp();
  44.  
  45. /* Private globals. */
  46. static char *tmpfilename = "/tmp/say.XXXXXX";
  47. static int unlinktmpfile = 0;
  48.  
  49. main( argc, argv )
  50. int argc;
  51. char *argv[];
  52.     {
  53.     struct stat termstat;
  54.     char filename[200], buf[1000];
  55.     FILE *fp;
  56.     int fd, child, wval;
  57.     union wait waitstatus;
  58.  
  59.     sw_argv0 = argv[0];
  60.  
  61.     /* Initialize. */
  62.     (void) signal( SIGINT, cleanup );
  63.     (void) signal( SIGHUP, cleanup );
  64.     if ( sw_check_sayrc( ) < 0 )
  65.     (void) cleanup( 1 );
  66.     if ( sw_check_my_sayfile( ) < 0 )
  67.     (void) cleanup( 1 );
  68.  
  69.     /* Cursory check of args - let write(1) do the rigorous check. */
  70.     if ( argc != 2 && argc != 3 )
  71.     {
  72.     (void) fprintf( stderr, "usage:  %s <user> [ <tty> ]\n", sw_argv0 );
  73.     (void) cleanup( 1 );
  74.     }
  75.     if ( search_utmp( argv[1] ) < 0 )
  76.     {
  77.     (void) fprintf( stderr,
  78.         "%s: there is no user \"%s\" logged in right now\n",
  79.         sw_argv0, argv[1] );
  80.     (void) cleanup( 1 );
  81.     }
  82.     if ( stat( ttyname( fileno( stderr ) ), &termstat ) >= 0 )
  83.     {
  84.     if ( ( termstat.st_mode & (S_IWRITE>>3) ) == 0 )  /* group write bit */
  85.         {
  86.         (void) fprintf( stderr,
  87.         "%s: you have write permission turned off\n", sw_argv0 );
  88.         (void) cleanup( 1 );
  89.         }
  90.     }
  91.  
  92.     /* Read in the message and save it to a temp file. */
  93.     (void) mktemp( tmpfilename );
  94.     if ( ( fp = fopen( tmpfilename, "w" ) ) == NULL )
  95.     {
  96.     (void) fprintf( stderr, "%s: couldn't open temp file ", sw_argv0 );
  97.     perror( tmpfilename );
  98.     (void) cleanup( 1 );
  99.     }
  100.     unlinktmpfile = 1;
  101.     (void) fchmod( fileno( fp ), 0600 );
  102. #ifdef MAIL_EDITOR
  103.     if ( sw_mail_editor )
  104.     {
  105.     (void) sprintf( buf, "%s %s", MAIL_EDITOR, tmpfilename );
  106.     (void) system( buf );
  107.     }
  108.     else
  109.     {
  110. #endif
  111.     if ( ! sw_terse )
  112.     (void) printf( "Enter message, '^D' or '.' to end.\n" );
  113.     while ( fgets( buf, sizeof(buf), stdin ) != NULL )
  114.     {
  115.     int i;
  116.  
  117.     if ( buf[0] == '.' && buf[1] == '\n' && buf[2] == '\0' )
  118.         break;
  119.     /* Write the line with control characters made visible. */
  120.     for ( i = 0; buf[i] != '\0'; ++i )
  121.         {
  122.         char c;
  123.  
  124.         c = toascii( buf[i] );
  125.         if ( ! isprint( c ) && ! isspace( c ) && c != '\n' && c != '\007' )
  126.         {
  127.         (void) putc( '^', fp );
  128.         (void) putc( c ^ 0x40, fp );    /* DEL to ?, others to alpha */
  129.         }
  130.         else
  131.         (void) putc( c, fp );
  132.         }
  133.     }
  134.     if ( fclose( fp ) == EOF )
  135.     {
  136.     (void) fprintf( stderr, "%s: error closing temp file ", sw_argv0 );
  137.     perror( tmpfilename );
  138.     (void) cleanup( 1 );
  139.     }
  140. #ifdef MAIL_EDITOR
  141.     }
  142. #endif
  143.  
  144.     /* Try sending it. */
  145.     if ( ( child = vfork() ) == 0 )
  146.     {
  147.     if ( ( fd = open( tmpfilename, O_RDONLY ) ) < 0 )
  148.         {
  149.         (void) fputs( sw_argv0, stderr );
  150.         perror( ": couldn't re-open temp file" );
  151.         exit( 1 );
  152.         }
  153.     (void) dup2( fd, fileno( stdin ) );
  154.     if ( argc == 2 )
  155.         (void) execlp( WRITE, WRITE, argv[1], NULL );
  156.     else
  157.         (void) execlp( WRITE, WRITE, argv[1], argv[2], NULL );
  158.     (void) fputs( sw_argv0, stderr );
  159.     perror( ": exec failed" );
  160.     exit( 1 );
  161.     }
  162.     while ( ( wval = wait( &waitstatus ) ) != child && wval != -1 )
  163.     ;
  164.     if ( wval == child && waitstatus.w_termsig == 0 &&
  165.      waitstatus.w_retcode == 0 )
  166.     {
  167.     /* Success.  Now save a copy in the recipient's .sayfile, and the
  168.     ** sender's too if sw_savemine is set. */
  169.     struct passwd *pw;
  170.     char *home, *login, *nows;
  171.     int myfd;
  172.     char myfilename[200], host[100];
  173.     time_t now;
  174.  
  175.     if ( ( pw = getpwnam( argv[1] ) ) == NULL )
  176.         {
  177.         (void) fprintf( stderr, "%s: couldn't find home directory for %s\n",
  178.         sw_argv0, argv[1] );
  179.         (void) cleanup( 1 );
  180.         }
  181.     (void) sprintf( filename, "%s/.sayfile", pw->pw_dir );
  182.     if ( ( fd = open( filename, O_WRONLY|O_APPEND ) ) < 0 )
  183.         {
  184.         if ( errno != ENOENT ) /* don't bother reporting lack of .sayfile */
  185.         {
  186.         (void) fprintf(
  187.             stderr, "%s: warning, couldn't open ", sw_argv0 );
  188.         perror( filename );
  189.         }
  190.         }
  191.  
  192.     if ( ( login = getlogin() ) == NULL )
  193.         if ( pw = getpwuid( getuid() ) )
  194.         login = pw->pw_name;
  195.         else
  196.         login = "???";
  197.     if ( sw_savemine && strcmp( argv[1], login ) != 0 )
  198.         {
  199.         if ( ( home = getenv( "HOME" ) ) == NULL )
  200.         {
  201.         (void) fprintf(
  202.             stderr, "%s: can't find home directory\n", sw_argv0 );
  203.         (void) cleanup( 1 );
  204.         }
  205.         (void) sprintf( myfilename, "%s/.sayfile", home );
  206.         if ( ( myfd = open( myfilename, O_WRONLY|O_APPEND ) ) < 0 )
  207.         {
  208.         if ( errno != ENOENT ) /* don't bother reporting lack of .sayfile */
  209.             {
  210.             (void) fprintf(
  211.             stderr, "%s: warning, couldn't open ", sw_argv0 );
  212.             perror( myfilename );
  213.             }
  214.         }
  215.         }
  216.     else
  217.         myfd = -1;
  218.  
  219.     if ( fd >= 0 || myfd >= 0 )
  220.         {
  221.         if ( ( fp = fopen( tmpfilename, "r" ) ) == NULL )
  222.         {
  223.         (void) fputs( sw_argv0, stderr );
  224.         perror( ": couldn't re-open temp file" );
  225.         (void) cleanup( 1 );
  226.         }
  227.         /* Greeting code extracted from BSD write(1). */
  228.         if ( gethostname( host, sizeof(host) ) < 0 )
  229.         (void) strcpy( host, "???" );
  230.         now = time( (time_t *) NULL );
  231.         nows = ctime( &now );
  232.         nows[16] = '\0';
  233.         /* Message separator - a control-A, a timestamp, and the sender. */
  234.         (void) sprintf( buf, "%c%d,%s\n", MSGSEP, (int) now, login );
  235.         if ( fd >= 0 )
  236.         if ( write( fd, buf, strlen(buf) ) < 0 )
  237.             {
  238.             (void) fprintf( stderr, "%s: error writing on ", sw_argv0 );
  239.             perror( filename );
  240.             (void) cleanup( 1 );
  241.             }
  242.         if ( myfd >= 0 )
  243.         if ( write( myfd, buf, strlen(buf) ) < 0 )
  244.             {
  245.             (void) fprintf( stderr, "%s: error writing on ", sw_argv0 );
  246.             perror( myfilename );
  247.             (void) cleanup( 1 );
  248.             }
  249.         (void) sprintf( buf, WRITE_HEAD, login, host,
  250.         ttyname( fileno( stderr ) ) + 5, nows + 11 );
  251.         if ( fd >= 0 )
  252.         (void) write( fd, buf, strlen(buf) );
  253.         if ( myfd >= 0 )
  254.         (void) write( myfd, buf, strlen(buf) );
  255.         /* And copy in the message. */
  256.         while ( fgets( buf, sizeof(buf), fp ) != NULL )
  257.         {
  258.         if ( fd >= 0 )
  259.             (void) write( fd, buf, strlen(buf) );
  260.         if ( myfd >= 0 )
  261.             (void) write( myfd, buf, strlen(buf) );
  262.         }
  263.         if ( fd >= 0 )
  264.         (void) write( fd, WRITE_TAIL, strlen(WRITE_TAIL) );
  265.         if ( myfd >= 0 )
  266.         (void) write( myfd, WRITE_TAIL, strlen(WRITE_TAIL) );
  267.         if ( fclose( fp ) == EOF )
  268.         {
  269.         (void) fprintf(
  270.             stderr, "%s: error closing temp file ", sw_argv0 );
  271.         perror( tmpfilename );
  272.         (void) cleanup( 1 );
  273.         }
  274.         if ( fd >= 0 )
  275.         if ( close( fd ) < 0 )
  276.             {
  277.             (void) fprintf( stderr, "%s: error closing ", sw_argv0 );
  278.             perror( filename );
  279.             (void) cleanup( 1 );
  280.             }
  281.         if ( myfd >= 0 )
  282.         if ( close( myfd ) < 0 )
  283.             {
  284.             (void) fprintf( stderr, "%s: error closing ", sw_argv0 );
  285.             perror( myfilename );
  286.             (void) cleanup( 1 );
  287.             }
  288.         }
  289.     }
  290.     else
  291.     {
  292.     /* Write failed. */
  293.     (void) printf(
  294.         "Your message could not be delivered for some reason.\n" );
  295.     (void) printf( "I will mail it instead.\n" );
  296.     (void) sprintf( buf, "mail -s 'this was a failing say' %s < %s",
  297.         argv[1], tmpfilename );
  298.     (void) system( buf );
  299.     }
  300.  
  301.     /* All done. */
  302.     (void) cleanup( 0 );
  303.     }
  304.  
  305. int
  306. search_utmp( user )
  307. char *user;
  308.     {
  309.     struct utmp u;
  310.     int ufd;
  311.  
  312.     if ( ( ufd = open( _PATH_UTMP, O_RDONLY ) ) < 0 )
  313.     {
  314.     perror( _PATH_UTMP );
  315.     (void) cleanup( 1 );
  316.     }
  317.  
  318.     while ( read( ufd, (char *) &u, sizeof(u) ) == sizeof(u) )
  319.     {
  320.     if ( strncmp( user, u.ut_name, sizeof(u.ut_name) ) == 0 )
  321.         {
  322.         (void) close( ufd );
  323.         return 0;
  324.         }
  325.     }
  326.     (void) close( ufd );
  327.     return -1;
  328.     }
  329.  
  330. int
  331. cleanup( status )
  332. int status;
  333.     {
  334.     sw_cleanup( );
  335.     if ( unlinktmpfile )
  336.     (void) unlink( tmpfilename );
  337.     unlinktmpfile = 0;
  338.     exit( status );
  339.     }
  340.  
  341.