From d21c4ae194f84338cbcd85a878fdeefa46fff4f7 Mon Sep 17 00:00:00 2001 From: mascarenhas Date: Wed, 25 Mar 2009 19:14:17 +0000 Subject: locking via lockfiles in posix systems. API: lock, err = lfs.lock_dir(path, [seconds_stale]) Creates a lockfile (called lockfile.lfs) in if it does not exist and returns the lock. If the lock already exists checks it it's stale, using the second parameter (default for the second parameter is INT_MAX, which in practice means the lock will never be stale. To free the the lock call lock:free(). In case of any errors it returns nil and the error message. In particular, if the lock exists and is not stale it returns the "File exists" message. --- src/lfs.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/src/lfs.c b/src/lfs.c index a3bce1d..45adf4a 100644 --- a/src/lfs.c +++ b/src/lfs.c @@ -16,7 +16,7 @@ ** lfs.touch (filepath [, atime [, mtime]]) ** lfs.unlock (fh) ** -** $Id: lfs.c,v 1.56 2009/02/03 22:05:48 carregal Exp $ +** $Id: lfs.c,v 1.57 2009/03/25 19:14:17 mascarenhas Exp $ */ #ifndef _WIN32 @@ -84,6 +84,11 @@ typedef struct dir_data { #endif } dir_data; +#define LOCK_METATABLE "lock metatable" +typedef struct lfs_Lock { + int fd; + char *ln; +} lfs_Lock; #ifdef _WIN32 #ifdef __BORLANDC__ @@ -205,6 +210,64 @@ static int _file_lock (lua_State *L, FILE *fh, const char *mode, const long star return (code != -1); } +#ifdef _WIN32 +static int lfs_lock_dir(lua_State *L) { + luaL_error(L, "not implemented for Windows"); +} +static int lfs_unlock_dir(lua_State *L) { + luaL_error(L, "not implemented for Windows"); +} +#else +static int lfs_lock_dir(lua_State *L) { + struct stat statbuf; + lfs_Lock *lock; + size_t pathl; int fd; + char *tmpln, *ln; + const char *template = "/lockfile.XXXXXX"; + const char *lockfile = "/lockfile.lfs"; + const char *path = luaL_checklstring(L, 1, &pathl); + time_t expires = (time_t)luaL_optint(L, 2, INT_MAX); + tmpln = (char*)malloc(pathl + strlen(template) + 1); + if(!tmpln) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; } + strcpy(tmpln, path); strcat(tmpln, template); + fd = mkstemp(tmpln); + if(fd == -1) { + free(tmpln); lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; + } + ln = (char*)malloc(pathl + strlen(lockfile) + 1); + if(!ln) { + unlink(tmpln); free(tmpln); close(fd); lua_pushnil(L); + lua_pushstring(L, strerror(errno)); return 2; + } + strcpy(ln, path); strcat(ln, lockfile); + while(symlink(tmpln, ln) == -1) { + if(errno == EEXIST) { + if(lstat(ln, &statbuf) == -1) goto fail; + if(time(NULL) - statbuf.st_mtimespec.tv_sec > expires) { + unlink(ln); + continue; + } + } + fail: + unlink(tmpln); free(tmpln); free(ln); close(fd); + lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; + } + unlink(tmpln); free(tmpln); + lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); + lock->fd = fd; lock->ln = ln; + luaL_getmetatable (L, LOCK_METATABLE); + lua_setmetatable (L, -2); + return 1; +} +static int lfs_unlock_dir(lua_State *L) { + lfs_Lock *lock = luaL_checkudata(L, 1, LOCK_METATABLE); + unlink(lock->ln); + close(lock->fd); + free(lock->ln); + return 0; +} +#endif + #ifdef _WIN32 static int lfs_g_setmode (lua_State *L, FILE *f, int arg) { static const int mode[] = {_O_TEXT, _O_BINARY}; @@ -433,6 +496,21 @@ static int dir_create_meta (lua_State *L) { return 1; } +/* +** Creates lock metatable. +*/ +static int lock_create_meta (lua_State *L) { + luaL_newmetatable (L, LOCK_METATABLE); + /* set its __gc field */ + lua_newtable(L); + lua_pushcfunction(L, lfs_unlock_dir); + lua_setfield(L, -2, "free"); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, lfs_unlock_dir); + lua_setfield(L, -2, "__gc"); + return 1; +} + #ifdef _WIN32 #ifndef S_ISDIR @@ -688,11 +766,13 @@ static const struct luaL_reg fslib[] = { {"setmode", lfs_f_setmode}, {"touch", file_utime}, {"unlock", file_unlock}, + {"lock_dir", lfs_lock_dir}, {NULL, NULL}, }; int luaopen_lfs (lua_State *L) { dir_create_meta (L); + lock_create_meta (L); luaL_register (L, "lfs", fslib); set_info (L); return 1; -- cgit v1.2.3-55-g6feb