home *** CD-ROM | disk | FTP | other *** search
- #include "global.h"
- #include "util.h"
-
- #include <errno.h>
- #include <ctype.h>
- #include <pwd.h>
- #include <unistd.h>
-
- #ifdef LOCKF_BROKEN
- extern long gethostid();
- #endif LOCKF_BROKEN
-
- extern char *strncpy();
-
- /* char *usercode()
- *
- * Usercode() tries to return the usercode of the person using gosip. It
- * uses getuid() to obtain the id, then obtains the usercode from the
- * password file. It returns two question marks if this doesn't succeed.
- */
- char *usercode()
- {
- static char text[MAX_CODE_LEN], *code = (char *) 0;
-
- if ( ! code )
- {
- struct passwd *pwent = getpwuid ( getuid() );
-
- if ( ! pwent )
- return " ?? ";
- (void) strncpy ( text, pwent->pw_name, MAX_CODE_LEN );
- text[MAX_CODE_LEN - 1] = '\0';
- code = text;
- }
- return code;
- }
-
- /* byte valid ( name )
- *
- * Scans string pointed to by name, returning zero if it contains any
- * non-printing characters, 1 otherwise.
- *
- * name : string to be scanned
- */
- static byte valid ( name )
- char *name;
- {
- while ( *name )
- if ( ! ( isascii ( *name ) && isprint ( *name++ ) ) )
- return 0;
- return 1;
- }
-
- /* char *gosname()
- *
- * This will return the user's name, taken from the environment variable
- * GOSNAME, or NAME if GOSNAME is not defined. If neither is defined, or the
- * name so obtained contains non-printable characters, the user is prompted
- * to enter his name.
- */
- char *gosname()
- {
- static char *name = 0, input[MAX_NAME_LEN];
-
- if ( ! name )
- {
- if ( ! ( name = getenv ( "GOSNAME" ) ) )
- name = getenv ( "NAME" );
- if ( ! name || ! valid ( name ) )
- {
- puts ( "If you wish to remain anonymous, just press return." );
- (void) printf ( "Please enter your name: " );
- if ( ! fgets ( input, MAX_NAME_LEN, stdin ) )
- name = "<UNKNOWN>";
- else
- {
- input[strlen ( input ) - 1] = '\0';
- if ( *input == '\0' )
- name = "<ANON>";
- else
- name = input;
- }
- }
- else
- {
- (void) strncpy ( input, name, MAX_NAME_LEN );
- name = input;
- }
- name[MAX_NAME_LEN - 1] = '\0'; /* ensure name is not too long */
- if ( ! valid ( name ) )
- name = "<UNKNOWN>";
- }
- return name;
- }
-
- #ifdef LOCKF_BROKEN
- /* static byte try_flock()
- *
- * This provides a secondary locking mechanism. The trouble with lock files
- * is that they can often be left around. I've never quite understood why -
- * the only obvious cause would be if the computer crashed or gosip was sent
- * a SIGKILL - but it happened with annoying regularity. Now a lock_file has
- * is flock()ed as well, but this flock() can only be checked for on the same
- * computer as it was applied.
- *
- * Hence the lock_file contains the hostid of the computer from which gosip
- * was invoked. This function checks whether that hostid matches the one of
- * the current machine. If so, it checks for the flock if the lock_file is
- * more than 12 seconds old. This delay is much more than is needed, and
- * serves to allow a flock() to be applied to a freshly created lock_file.
- * This is needed if two people invoke gosip at the same time.
- *
- * Now a flock() is guaranteed to be removed when a process dies, therefore
- * if a lock_file older than 12 seconds exists without a flock on it on the
- * same machine as the one on which it was created, it is deemed to be
- * incorrect and removed. This system has so far worked very well, with no
- * complaints of either more than one person editing a particular gosip file,
- * or of a free file being considered unfree.
- *
- * This system does depend on gosip being used fairly regularly on every
- * machine from which it is available, which is not a problem here.
- *
- * If the lock was valid, 1 is returned: 0 if the lock_file was removed.
- */
- static byte try_flock()
- {
- struct stat info;
- long host;
- int fd;
- byte retval = 1;
-
- if ( ( fd = open ( lock_file, O_RDONLY ) ) != -1 )
- if ( read ( fd, (char *) &host, sizeof ( host ) ) == sizeof ( host ) )
- if ( host == gethostid() )
- if ( fstat ( fd, &info ) == 0 )
- if ( time ( (time_t *) 0 ) - info.st_mtime > 12 )
- if ( flock ( fd, LOCK_SH | LOCK_NB ) != -1 )
- {
- retval = 0;
- (void) unlink ( lock_file );
- }
- (void) close ( fd );
- return retval;
- }
-
- /* byte lock ( type )
- *
- * General function to manipulate locks on the gosip file to prevent
- * simultaneous editing. The parameter type controls the exact behaviour.
- * If type is REMVE a previously applied lock will be removed, which involves
- * removing the lock_file and the flock() on history.gosip.
- *
- * Type CHECK will first check for a lock file. If present, 1 is returned if
- * try_flock() agrees the lock is genuine. Otherwise, 0 is returned to
- * indicate gosip is free for use.
- *
- * Type CREAT makes similar checks as CHECK, but it will create a lock if
- * there isn't one. That is it creates lock_file and uses flock() to give it
- * an exclusive lock. It returns 0 on success, and 1 if a lock already
- * existed. Return value of -1 if the lock could not be created.
- *
- * type : type of function to perform as detailed above.
- */
- byte lock ( type )
- byte type;
- {
- static int lfd;
- long host;
-
- switch ( type )
- {
- case REMVE:
- if ( ! lock ( CHECK ) )
- {
- puts ( "Internal error: tried to remove non-existant lock." );
- return -1;
- }
- (void) close ( lfd );
- if ( unlink ( lock_file ) )
- perror ( "Couldn't remove lock file" );
- return 0;
- case CREAT:
- lfd = open ( lock_file, O_CREAT | O_EXCL | O_WRONLY, 0644 );
- if ( lfd == -1 )
- if ( errno == EEXIST )
- if ( try_flock() )
- return 1; /* valid lock exists */
- else
- return lock ( CREAT ); /* lock was invalid, try again */
- else
- {
- perror ( "Couldn't create lock file" );
- return -1;
- }
- if ( flock ( lfd, LOCK_EX | LOCK_NB ) )
- {
- perror ( "Flock failed" );
- (void) fprintf ( stderr, "This may be a bug in %s!\n", file );
- (void) lock ( REMVE );
- return -1;
- }
- if ( fchmod ( lfd, 0644 ) )
- perror ( "Couldn't alter lock file permissions" );
- host = gethostid();
- if ( write ( lfd, (char *) &host, sizeof ( host ) ) < sizeof ( host ) )
- perror ( "Error writing host id" );
- return 0;
- case CHECK:
- if ( access ( lock_file, F_OK ) == 0 )
- return try_flock();
- else
- return 0;
- }
- puts ( "Internal error: lock called with invalid argument." );
- return -1;
- }
-
- #else LOCKF_BROKEN
-
- /* byte lock ( type )
- *
- * General function to manipulate locks on the gosip file to prevent
- * simultaneous editing. The parameter type controls the exact behaviour.
- * If type is REMVE a previously applied lock will be removed, which involves
- * simply releasing the lockf().
- *
- * Type CHECK returns 1 if lockf() indicates a lock on history.gosip,
- * otherwise 0.
- *
- * Type CREAT creates a lock (on history.gosip) by calling flock() It returns
- * 0 on success, and 1 if a lock already existed. Return value of -1 if the
- * lock could not be created.
- *
- * type : type of function to perform as detailed above.
- */
- byte lock ( type )
- byte type;
- {
- static int lfd = -2, fd, ret;
-
- switch ( type )
- {
- case REMVE:
- switch ( lock ( CHECK ) )
- {
- case 0:
- puts ( "Internal error: tried to remove non-existant lock." );
- return -1;
- case -1:
- puts ( "Lock checking error." );
- return -1;
- }
- (void) lockf ( lfd, F_ULOCK, 0L );
- (void) close ( lfd );
- return 0;
- case CREAT:
- fd = open ( history_file, O_CREAT | O_WRONLY, 0666 );
- if ( fd == -1 )
- {
- perror ( "Couldn't create lock" );
- return -1;
- }
- if ( lockf ( fd, F_TLOCK, 0L ) == -1 )
- if ( errno == EAGAIN || errno == EACCES ) /* check EACCES because of */
- return 1; /* lockf() bug */
- else
- {
- perror ( "Lockf() failed" );
- (void) close ( fd );
- return -1;
- }
- lfd = fd;
- return 0;
- case CHECK:
- if ( lfd > -2 )
- return 1;
- if ( ( fd = open ( history_file, O_CREAT | O_WRONLY, 0666 ) ) == -1 )
- {
- perror ( "Couldn't check lock" );
- return -1;
- } /* ret = lockf ( fd, F_TEST, 0L ); */
- ret = lockf ( fd, F_TLOCK, 0L ); /* cannot be used: it has a bug. */
- (void) close ( fd ); /* releases the lock */
- if ( ret == -1 )
- return ( errno == EAGAIN || errno == EACCES ) ? 1 : -1;
- else
- return 0;
- }
- puts ( "Internal error: lock called with invalid argument." );
- return -1;
- }
- #endif LOCKF_BROKEN
-
- /* void lastedit ( format )
- *
- * Lastedit prints out the name and usercode of the last person to edit the
- * gosip file. The exact form of the message depends on format, which also
- * controls whether elapsed edit time is displayed.
- *
- * format : indicates what type of message to print.
- */
- void lastedit ( format )
- byte format;
- {
- FILE *last;
- int c;
- time_t edit_time;
- struct stat info;
-
- if ( format == AUTO )
- format = lock ( CHECK ) ? EDIT : INFO;
- if ( last = fopen ( last_file , "r" ) )
- {
- if ( ( c = getc ( last ) ) != EOF )
- {
- if ( format == EDIT )
- (void) printf ( "The %s file is being edited by ", file );
- else
- (void) printf ( "Last %s edit by ", file );
- do
- (void) putchar ( (char) c );
- while ( ( c = getc ( last ) ) != '\n' && c != EOF );
- }
- (void) fclose ( last );
- }
- if ( format == EDIT)
- if ( stat ( last_file, &info ) == 0 )
- {
- edit_time = time ( (time_t *) 0 ) - info.st_mtime;
- (void) printf ( " [%d:%.2d]", edit_time / 60, edit_time % 60 );
- }
- puts ( "." );
- }
-