aboutsummaryrefslogtreecommitdiffhomepage
path: root/fishd.cpp
diff options
context:
space:
mode:
authorGravatar Peter Ammon <corydoras@ridiculousfish.com>2012-03-02 00:27:40 -0800
committerGravatar Peter Ammon <corydoras@ridiculousfish.com>2012-03-02 00:27:40 -0800
commit8b26d0104c1d85c271e5ce6e08bfe64a779beba7 (patch)
tree995568526f190084fd12d74bc6dcd0e828b1018a /fishd.cpp
parent36622c35781c3212c2102c45781a496f3e1b3659 (diff)
Some initial changes to use CLO_EXEC, with an eye towards some day using it correctly.
Diffstat (limited to 'fishd.cpp')
-rw-r--r--fishd.cpp256
1 files changed, 256 insertions, 0 deletions
diff --git a/fishd.cpp b/fishd.cpp
index c3ad0d15..edaa762a 100644
--- a/fishd.cpp
+++ b/fishd.cpp
@@ -73,6 +73,15 @@ time the original barrier request was sent have been received.
#include "path.h"
#include "print_help.h"
+#ifndef HOST_NAME_MAX
+/**
+ Maximum length of hostname return. It is ok if this is too short,
+ getting the actual hostname is not critical, so long as the string
+ is unique in the filesystem namespace.
+ */
+#define HOST_NAME_MAX 255
+#endif
+
/**
Maximum length of socket filename
*/
@@ -189,6 +198,252 @@ static void handle_term( int signal )
/**
+ Writes a pid_t in decimal representation to str.
+ str must contain sufficient space.
+ The conservatively approximate maximum number of characters a pid_t will
+ represent is given by: (int)(0.31 * sizeof(pid_t) + CHAR_BIT + 1)
+ Returns the length of the string
+ */
+static int sprint_pid_t( pid_t pid, char *str )
+{
+ int len, i = 0;
+ int dig;
+
+ /* Store digits in reverse order into string */
+ while( pid != 0 )
+ {
+ dig = pid % 10;
+ str[i] = '0' + dig;
+ pid = ( pid - dig ) / 10;
+ i++;
+ }
+ len = i;
+ /* Reverse digits */
+ i /= 2;
+ while( i )
+ {
+ i--;
+ dig = str[i];
+ str[i] = str[len - 1 - i];
+ str[len - 1 - i] = dig;
+ }
+ return len;
+}
+
+
+
+/**
+ Writes a pseudo-random number (between one and maxlen) of pseudo-random
+ digits into str.
+ str must point to an allocated buffer of size of at least maxlen chars.
+ Returns the number of digits written.
+ Since the randomness in part depends on machine time it has _some_ extra
+ strength but still not enough for use in concurrent locking schemes on a
+ single machine because gettimeofday may not return a different value on
+ consecutive calls when:
+ a) the OS does not support fine enough resolution
+ b) the OS is running on an SMP machine.
+ Additionally, gettimeofday errors are ignored.
+ Excludes chars other than digits since ANSI C only guarantees that digits
+ are consecutive.
+ */
+static int sprint_rand_digits( char *str, int maxlen )
+{
+ int i, max;
+ struct timeval tv;
+
+ /*
+ Seed the pseudo-random generator based on time - this assumes
+ that consecutive calls to gettimeofday will return different values
+ and ignores errors returned by gettimeofday.
+ Cast to unsigned so that wrapping occurs on overflow as per ANSI C.
+ */
+ (void)gettimeofday( &tv, NULL );
+ srand( (unsigned int)tv.tv_sec + (unsigned int)tv.tv_usec * 1000000UL );
+ max = 1 + (maxlen - 1) * (rand() / (RAND_MAX + 1.0));
+ for( i = 0; i < max; i++ )
+ {
+ str[i] = '0' + 10 * (rand() / (RAND_MAX + 1.0));
+ }
+ return i;
+}
+
+
+/**
+ Generate a filename unique in an NFS namespace by creating a copy of str and
+ appending .{hostname}.{pid} to it. If gethostname() fails then a pseudo-
+ random string is substituted for {hostname} - the randomness of the string
+ should be strong enough across different machines. The main assumption
+ though is that gethostname will not fail and this is just a "safe enough"
+ fallback.
+ The memory returned should be freed using free().
+ */
+static char *gen_unique_nfs_filename( const char *filename )
+{
+ int pidlen, hnlen, orglen = strlen( filename );
+ char hostname[HOST_NAME_MAX + 1];
+ char *newname;
+
+ if ( gethostname( hostname, HOST_NAME_MAX + 1 ) == 0 )
+ {
+ hnlen = strlen( hostname );
+ }
+ else
+ {
+ hnlen = sprint_rand_digits( hostname, HOST_NAME_MAX );
+ hostname[hnlen] = '\0';
+ }
+ newname = (char *)malloc( orglen + 1 /* period */ + hnlen + 1 /* period */ +
+ /* max possible pid size: 0.31 ~= log(10)2 */
+ (int)(0.31 * sizeof(pid_t) * CHAR_BIT + 1)
+ + 1 /* '\0' */ );
+
+ if ( newname == NULL )
+ {
+ debug( 1, L"gen_unique_nfs_filename: %s", strerror( errno ) );
+ return newname;
+ }
+ memcpy( newname, filename, orglen );
+ newname[orglen] = '.';
+ memcpy( newname + orglen + 1, hostname, hnlen );
+ newname[orglen + 1 + hnlen] = '.';
+ pidlen = sprint_pid_t( getpid(), newname + orglen + 1 + hnlen + 1 );
+ newname[orglen + 1 + hnlen + 1 + pidlen] = '\0';
+ /* debug( 1, L"gen_unique_nfs_filename returning with: newname = \"%s\"; "
+ L"HOST_NAME_MAX = %d; hnlen = %d; orglen = %d; "
+ L"sizeof(pid_t) = %d; maxpiddigits = %d; malloc'd size: %d",
+ newname, (int)HOST_NAME_MAX, hnlen, orglen,
+ (int)sizeof(pid_t),
+ (int)(0.31 * sizeof(pid_t) * CHAR_BIT + 1),
+ (int)(orglen + 1 + hnlen + 1 +
+ (int)(0.31 * sizeof(pid_t) * CHAR_BIT + 1) + 1) ); */
+ return newname;
+}
+
+
+/**
+ The number of milliseconds to wait between polls when attempting to acquire
+ a lockfile
+ */
+#define LOCKPOLLINTERVAL 10
+
+/**
+ Attempt to acquire a lock based on a lockfile, waiting LOCKPOLLINTERVAL
+ milliseconds between polls and timing out after timeout seconds,
+ thereafter forcibly attempting to obtain the lock if force is non-zero.
+ Returns 1 on success, 0 on failure.
+ To release the lock the lockfile must be unlinked.
+ A unique temporary file named by appending characters to the lockfile name
+ is used; any pre-existing file of the same name is subject to deletion.
+ */
+static int acquire_lock_file( const char *lockfile, const int timeout, int force )
+{
+ int fd, timed_out = 0;
+ int ret = 0; /* early exit returns failure */
+ struct timespec pollint;
+ struct timeval start, end;
+ double elapsed;
+ struct stat statbuf;
+
+ /*
+ (Re)create a unique file and check that it has one only link.
+ */
+ char *linkfile = gen_unique_nfs_filename( lockfile );
+ if( linkfile == NULL )
+ {
+ goto done;
+ }
+ (void)unlink( linkfile );
+ /* OK to not use CLO_EXEC here because fishd is single threaded */
+ if( ( fd = open( linkfile, O_CREAT|O_RDONLY, 0600 ) ) == -1 )
+ {
+ debug( 1, L"acquire_lock_file: open: %s", strerror( errno ) );
+ goto done;
+ }
+ /*
+ Don't need to check exit status of close on read-only file descriptors
+ */
+ close( fd );
+ if( stat( linkfile, &statbuf ) != 0 )
+ {
+ debug( 1, L"acquire_lock_file: stat: %s", strerror( errno ) );
+ goto done;
+ }
+ if ( statbuf.st_nlink != 1 )
+ {
+ debug( 1, L"acquire_lock_file: number of hardlinks on unique "
+ L"tmpfile is %d instead of 1.", (int)statbuf.st_nlink );
+ goto done;
+ }
+ if( gettimeofday( &start, NULL ) != 0 )
+ {
+ debug( 1, L"acquire_lock_file: gettimeofday: %s", strerror( errno ) );
+ goto done;
+ }
+ end = start;
+ pollint.tv_sec = 0;
+ pollint.tv_nsec = LOCKPOLLINTERVAL * 1000000;
+ do
+ {
+ /*
+ Try to create a hard link to the unique file from the
+ lockfile. This will only succeed if the lockfile does not
+ already exist. It is guaranteed to provide race-free
+ semantics over NFS which the alternative of calling
+ open(O_EXCL|O_CREAT) on the lockfile is not. The lock
+ succeeds if the call to link returns 0 or the link count on
+ the unique file increases to 2.
+ */
+ if( link( linkfile, lockfile ) == 0 ||
+ ( stat( linkfile, &statbuf ) == 0 &&
+ statbuf.st_nlink == 2 ) )
+ {
+ /* Successful lock */
+ ret = 1;
+ break;
+ }
+ elapsed = end.tv_sec + end.tv_usec/1000000.0 -
+ ( start.tv_sec + start.tv_usec/1000000.0 );
+ /*
+ The check for elapsed < 0 is to deal with the unlikely event
+ that after the loop is entered the system time is set forward
+ past the loop's end time. This would otherwise result in a
+ (practically) infinite loop.
+ */
+ if( timed_out || elapsed >= timeout || elapsed < 0 )
+ {
+ if ( timed_out == 0 && force )
+ {
+ /*
+ Timed out and force was specified - attempt to
+ remove stale lock and try a final time
+ */
+ (void)unlink( lockfile );
+ timed_out = 1;
+ continue;
+ }
+ else
+ {
+ /*
+ Timed out and final try was unsuccessful or
+ force was not specified
+ */
+ debug( 1, L"acquire_lock_file: timed out "
+ L"trying to obtain lockfile %s using "
+ L"linkfile %s", lockfile, linkfile );
+ break;
+ }
+ }
+ nanosleep( &pollint, NULL );
+ } while( gettimeofday( &end, NULL ) == 0 );
+done:
+ /* The linkfile is not needed once the lockfile has been created */
+ (void)unlink( linkfile );
+ free( linkfile );
+ return ret;
+}
+
+/**
Acquire the lock for the socket
Returns the name of the lock file if successful or
NULL if unable to obtain lock.
@@ -502,6 +757,7 @@ static void load_or_save( int save)
save?"saving":"loading",
name );
+ /* OK to not use CLO_EXEC here because fishd is single threaded */
fd = open( name, save?(O_CREAT | O_TRUNC | O_WRONLY):O_RDONLY, 0600);
free( name );