home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume7 / gosip2 / util.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-07-08  |  9.6 KB  |  334 lines

  1. #include "global.h"
  2. #include "util.h"
  3.  
  4. #include <errno.h>
  5. #include <ctype.h>
  6. #include <pwd.h>
  7. #include <unistd.h>
  8.  
  9. #ifdef LOCKF_BROKEN
  10. extern long gethostid();
  11. #endif LOCKF_BROKEN
  12.  
  13. extern char *strncpy();
  14.  
  15. /*  char *usercode()
  16.  *
  17.  *  Usercode() tries to return the usercode of the person using gosip.  It
  18.  *  uses getuid() to obtain the id, then obtains the usercode from the
  19.  *  password file. It returns two question marks if this doesn't succeed.
  20.  */
  21. char *usercode()
  22. {
  23.   static char text[MAX_CODE_LEN], *code = (char *) 0;
  24.   
  25.   if ( ! code )
  26.   {
  27.     struct passwd *pwent = getpwuid ( getuid() );
  28.  
  29.     if ( ! pwent )
  30.       return " ?? ";
  31.     (void) strncpy ( text, pwent->pw_name, MAX_CODE_LEN );
  32.     text[MAX_CODE_LEN - 1] = '\0';
  33.     code = text;
  34.   }
  35.   return code;
  36. }
  37.  
  38. /*  byte valid ( name )
  39.  *
  40.  *  Scans string pointed to by name, returning zero if it contains any
  41.  *  non-printing characters, 1 otherwise.
  42.  *
  43.  *  name  :  string to be scanned
  44.  */
  45. static byte valid ( name )
  46. char *name;
  47. {
  48.   while ( *name )
  49.     if ( ! ( isascii ( *name ) && isprint ( *name++ ) ) )
  50.       return 0;
  51.   return 1;
  52. }
  53.  
  54. /*  char *gosname()
  55.  *
  56.  *  This will return the user's name, taken from the environment variable
  57.  *  GOSNAME, or NAME if GOSNAME is not defined.  If neither is defined, or the
  58.  *  name so obtained contains non-printable characters, the user is prompted
  59.  *  to enter his name.
  60.  */
  61. char *gosname()
  62. {
  63.   static char *name = 0, input[MAX_NAME_LEN];
  64.  
  65.   if ( ! name )
  66.   {
  67.     if ( ! ( name = getenv ( "GOSNAME" ) ) ) 
  68.       name = getenv ( "NAME" );
  69.     if ( ! name || ! valid ( name ) )
  70.     {
  71.       puts ( "If you wish to remain anonymous, just press return." );
  72.       (void) printf ( "Please enter your name: " );
  73.       if ( ! fgets ( input, MAX_NAME_LEN, stdin ) )
  74.         name = "<UNKNOWN>";
  75.       else
  76.       {
  77.         input[strlen ( input ) - 1] = '\0';
  78.         if ( *input == '\0' )
  79.           name = "<ANON>";
  80.         else
  81.           name = input;
  82.       }
  83.     }
  84.     else
  85.     {
  86.       (void) strncpy ( input, name, MAX_NAME_LEN );
  87.       name = input;
  88.     }
  89.     name[MAX_NAME_LEN - 1] = '\0';    /* ensure name is not too long */
  90.     if ( ! valid ( name ) )
  91.       name = "<UNKNOWN>";
  92.   }
  93.   return name;
  94. }
  95.  
  96. #ifdef LOCKF_BROKEN
  97. /*  static byte try_flock()
  98.  *
  99.  *  This provides a secondary locking mechanism.  The trouble with lock files
  100.  *  is that they can often be left around.  I've never quite understood why -
  101.  *  the only obvious cause would be if the computer crashed or gosip was sent
  102.  *  a SIGKILL - but it happened with annoying regularity.  Now a lock_file has
  103.  *  is flock()ed as well, but this flock() can only be checked for on the same
  104.  *  computer as it was applied.
  105.  *
  106.  *  Hence the lock_file contains the hostid of the computer from which gosip
  107.  *  was invoked.  This function checks whether that hostid matches the one of
  108.  *  the current machine.  If so, it checks for the flock if the lock_file is
  109.  *  more than 12 seconds old.  This delay is much more than is needed, and
  110.  *  serves to allow a flock() to be applied to a freshly created lock_file.
  111.  *  This is needed if two people invoke gosip at the same time.
  112.  *
  113.  *  Now a flock() is guaranteed to be removed when a process dies, therefore
  114.  *  if a lock_file older than 12 seconds exists without a flock on it on the
  115.  *  same machine as the one on which it was created, it is deemed to be
  116.  *  incorrect and removed.  This system has so far worked very well, with no
  117.  *  complaints of either more than one person editing a particular gosip file,
  118.  *  or of a free file being considered unfree.
  119.  *
  120.  *  This system does depend on gosip being used fairly regularly on every
  121.  *  machine from which it is available, which is not a problem here.
  122.  *
  123.  *  If the lock was valid, 1 is returned: 0 if the lock_file was removed.
  124.  */
  125. static byte try_flock()
  126. {
  127.   struct stat info;
  128.   long host;
  129.   int fd;
  130.   byte retval = 1;
  131.  
  132.   if ( ( fd = open ( lock_file, O_RDONLY ) ) != -1 )
  133.     if ( read ( fd, (char *) &host, sizeof ( host ) ) == sizeof ( host ) )
  134.       if ( host == gethostid() )
  135.         if ( fstat ( fd, &info ) == 0 )
  136.           if ( time ( (time_t *) 0 ) - info.st_mtime > 12 )
  137.             if ( flock ( fd, LOCK_SH | LOCK_NB ) != -1 )
  138.             {
  139.               retval = 0;
  140.               (void) unlink ( lock_file );
  141.             }
  142.   (void) close ( fd );
  143.   return retval;
  144. }
  145.  
  146. /*  byte lock ( type )
  147.  *
  148.  *  General function to manipulate locks on the gosip file to prevent
  149.  *  simultaneous editing.  The parameter type controls the exact behaviour.
  150.  *  If type is REMVE a previously applied lock will be removed, which involves
  151.  *  removing the lock_file and the flock() on history.gosip.
  152.  *
  153.  *  Type CHECK will first check for a lock file.  If present, 1 is returned if
  154.  *  try_flock() agrees the lock is genuine.  Otherwise, 0 is returned to
  155.  *  indicate gosip is free for use.
  156.  *
  157.  *  Type CREAT makes similar checks as CHECK, but it will create a lock if
  158.  *  there isn't one.  That is it creates lock_file and uses flock() to give it
  159.  *  an exclusive lock.  It returns 0 on success, and 1 if a lock already
  160.  *  existed.  Return value of -1 if the lock could not be created.
  161.  *
  162.  *  type  :  type of function to perform as detailed above.
  163.  */
  164. byte lock ( type )
  165. byte type;
  166. {
  167.   static int lfd;
  168.   long host;
  169.  
  170.   switch ( type )
  171.   {
  172.   case REMVE:
  173.     if ( ! lock ( CHECK ) )
  174.     {
  175.       puts ( "Internal error: tried to remove non-existant lock." );
  176.       return -1;
  177.     }
  178.     (void) close ( lfd );
  179.     if ( unlink ( lock_file ) )
  180.       perror ( "Couldn't remove lock file" );
  181.     return 0;
  182.   case CREAT:
  183.     lfd = open ( lock_file, O_CREAT | O_EXCL | O_WRONLY, 0644 );
  184.     if ( lfd == -1 )
  185.       if ( errno == EEXIST )
  186.         if ( try_flock() ) 
  187.           return 1;             /* valid lock exists */
  188.         else
  189.           return lock ( CREAT ); /* lock was invalid, try again */
  190.       else
  191.       {
  192.         perror ( "Couldn't create lock file" );
  193.         return -1;
  194.       }
  195.     if ( flock ( lfd, LOCK_EX | LOCK_NB ) )
  196.     {
  197.       perror ( "Flock failed" );
  198.       (void) fprintf ( stderr, "This may be a bug in %s!\n", file );
  199.       (void) lock ( REMVE );
  200.       return -1;
  201.     }
  202.     if ( fchmod ( lfd, 0644 ) )
  203.       perror ( "Couldn't alter lock file permissions" );
  204.     host = gethostid();
  205.     if ( write ( lfd, (char *) &host, sizeof ( host ) ) < sizeof ( host ) )
  206.       perror ( "Error writing host id" );
  207.     return 0;
  208.   case CHECK:
  209.     if ( access ( lock_file, F_OK ) == 0 )
  210.       return try_flock();
  211.     else
  212.       return 0; 
  213.   }
  214.   puts ( "Internal error: lock called with invalid argument." );
  215.   return -1;
  216. }
  217.  
  218. #else LOCKF_BROKEN
  219.  
  220. /*  byte lock ( type )
  221.  *
  222.  *  General function to manipulate locks on the gosip file to prevent
  223.  *  simultaneous editing.  The parameter type controls the exact behaviour.
  224.  *  If type is REMVE a previously applied lock will be removed, which involves
  225.  *  simply releasing the lockf().
  226.  *
  227.  *  Type CHECK returns 1 if lockf() indicates a lock on history.gosip,
  228.  *  otherwise 0.
  229.  *
  230.  *  Type CREAT creates a lock (on history.gosip) by calling flock() It returns
  231.  *  0 on success, and 1 if a lock already existed.  Return value of -1 if the
  232.  *  lock could not be created.
  233.  *
  234.  *  type  :  type of function to perform as detailed above.
  235.  */
  236. byte lock ( type )
  237. byte type;
  238. {
  239.   static int lfd = -2, fd, ret;
  240.  
  241.   switch ( type )
  242.   {
  243.   case REMVE:
  244.     switch ( lock ( CHECK ) )
  245.     {
  246.     case 0:
  247.       puts ( "Internal error: tried to remove non-existant lock." );
  248.       return -1;
  249.     case -1:
  250.       puts ( "Lock checking error." );
  251.       return -1;
  252.     }
  253.     (void) lockf ( lfd, F_ULOCK, 0L );
  254.     (void) close ( lfd );
  255.     return 0;
  256.   case CREAT:
  257.     fd = open ( history_file, O_CREAT | O_WRONLY, 0666 );
  258.     if ( fd == -1 )
  259.     {
  260.       perror ( "Couldn't create lock" );
  261.       return -1;
  262.     }
  263.     if ( lockf ( fd, F_TLOCK, 0L ) == -1 )
  264.       if ( errno == EAGAIN || errno == EACCES ) /* check EACCES because of */
  265.         return 1;                               /* lockf() bug */
  266.       else
  267.       {
  268.         perror ( "Lockf() failed" );
  269.         (void) close ( fd );
  270.         return -1;
  271.       }
  272.     lfd = fd;
  273.     return 0;
  274.   case CHECK:
  275.     if ( lfd > -2 )
  276.       return 1;
  277.     if ( ( fd = open ( history_file, O_CREAT | O_WRONLY, 0666 ) ) == -1 )
  278.     {
  279.       perror ( "Couldn't check lock" );
  280.       return -1;
  281.     }                                 /* ret = lockf ( fd, F_TEST, 0L ); */ 
  282.     ret = lockf ( fd, F_TLOCK, 0L );  /* cannot be used: it has a bug. */   
  283.     (void) close ( fd );        /* releases the lock */
  284.     if ( ret == -1 )
  285.       return ( errno == EAGAIN || errno == EACCES ) ? 1 : -1;
  286.     else
  287.       return 0; 
  288.   }
  289.   puts ( "Internal error: lock called with invalid argument." );
  290.   return -1;
  291. }
  292. #endif LOCKF_BROKEN
  293.  
  294. /*  void lastedit ( format )
  295.  *
  296.  *  Lastedit prints out the name and usercode of the last person to edit the
  297.  *  gosip file.  The exact form of the message depends on format, which also
  298.  *  controls whether elapsed edit time is displayed.
  299.  *
  300.  *  format  :  indicates what type of message to print.
  301.  */
  302. void lastedit ( format )
  303. byte format;
  304. {
  305.   FILE *last;
  306.   int c;
  307.   time_t edit_time;
  308.   struct stat info;
  309.  
  310.   if ( format == AUTO )
  311.     format = lock ( CHECK ) ? EDIT : INFO;
  312.   if ( last = fopen ( last_file , "r" ) )
  313.   {
  314.     if ( ( c = getc ( last ) ) != EOF )
  315.     {
  316.       if ( format == EDIT )
  317.         (void) printf ( "The %s file is being edited by ", file );
  318.       else
  319.         (void) printf ( "Last %s edit by ", file );
  320.       do
  321.         (void) putchar ( (char) c );
  322.       while ( ( c = getc ( last ) ) != '\n' && c != EOF );
  323.     }
  324.     (void) fclose ( last );
  325.   }
  326.   if ( format == EDIT)
  327.     if ( stat ( last_file, &info ) == 0 )
  328.     {
  329.       edit_time = time ( (time_t *) 0 ) - info.st_mtime;
  330.       (void) printf ( " [%d:%.2d]", edit_time / 60, edit_time % 60 );
  331.     }
  332.   puts ( "." );
  333. }
  334.