//
//	DAYLockFile.h -- a generic class to simplify keeping an atomic lock file
//		Written by Don Yacktman (c) 1993 by Don Yacktman.
//				Version 1.0.  All rights reserved.
//
//		This is a free object!  Contact the author for the latest version.
//		Don Yacktman, 4279 N. Ivy Lane, Provo, UT, 84604
//		e-mail:  Don_Yacktman@byu.edu
//
//		You may use and copy this class freely as long as you
//		comply with the following terms:
//			(1) Do not remove the author's name or any of the
//				copyright notices from this file.
//			(2) If you redistribute an application which uses this
//				object, you must either include the source code for
//				this object with the application or state in your
//				application's documentation that you (a) use this
//				object and (b) where to obtain the source code for
//				the object.
//			(3) In no way shall the author or his employer(s) be held
//				responsible for any damages caused by what this object
//				does or does not do.
//			(4) You have no warranty whatsoever that this object is
//				good for any purpose at all.  If you find it useful
//				for something, consider yourself lucky and leave it at that.
//

#import <daymisckit/daymisckit.h>
#import <sys/file.h>
#import <sys/vnode.h>

@implementation DAYLockFile

- init
{
	id ret = [super init];
	haveLock = NO;
	return ret;
}

- fileName { return lockFileName; }
- setFileName:aString
{
	if (haveLock) [self unlock];
	lockFileName = aString;
	return self;
}

- lock  // atomically create a lock file.  returns YES on success
{	// lock file contains the PID of the locking process.
	int lockFileFD; char buffer[16];
	if (!lockFileName || ![lockFileName stringValue])
		return self; // always "have" lock if no lock file used
	if (haveLock) return self; // already been done
	lockFileFD = open([lockFileName stringValue],	// file name
			(O_EXCL | O_CREAT | O_RDWR),	// error if exists; create if doesn't
			(VREAD | VWRITE)); // mode is owner r/w
	if (lockFileFD < 0) return nil; // error opening the lock file
	sprintf(buffer, "%d", getpid());
	if (write(lockFileFD, buffer, strlen(buffer)) < 0) return nil; // couldn't write
	if (close(lockFileFD)) return nil; // error closing the lock file
	haveLock = YES;
	return self;
}

- unlock	// remove the lock file.
{
	if (!lockFileName || ![lockFileName stringValue])
		return self; // always "had" lock if no lock file used
	if (!haveLock) return nil;
	if (unlink([lockFileName stringValue])) return nil;  // error in unlink()
	haveLock = NO; // the lock was successfully removed.
	return self;
}

- (BOOL)haveLock { return (haveLock || !lockFileName); }

- copy
{	// copies do not have locks or open logs... only original can!
	id myCopy = [[DAYLockFile alloc] init];
	[myCopy setFileName:lockFileName];
	return myCopy;
}

- read:(NXTypedStream *)stream
{
	[super read:stream];
	lockFileName = NXReadObject(stream);
	haveLock = NO;
	return self;
}

- write:(NXTypedStream *)stream
{
	[super write:stream];
	NXWriteObject(stream, lockFileName);
	return self;
}

// NXTransport protocol implementation:
- encodeUsing:(id <NXEncoding>)portal
{
	[portal encodeObjectBycopy:lockFileName];
	return self;
}

- decodeUsing:(id <NXDecoding>)portal
{
	lockFileName = [portal decodeObject];
	return self;
}

- encodeRemotelyFor:(NXConnection *)connection
	freeAfterEncoding:(BOOL *)flagp isBycopy:(BOOL)isByCopy
{
	if (isByCopy) {
		*flagp = NO; // object will copy.
		return self; //encode object (copy it)
	}
	*flagp = NO; // object will copy.
	// super will encode the proxy otherwise
	return [super encodeRemotelyFor:connection
				freeAfterEncoding:flagp isBycopy:isByCopy];
}

- free
{
	if (haveLock) [self unlock];
	[lockFileName free];
	return [super free];
}

@end
