lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]



El 09/09/2004, a las 22:56, Leonardo Castanheira escribió:

	Its my first time in this list, so hello to everybody. [...]

You're welcome ;-)

I am trying to implement a lock file in lua, but it seems not so easy because the function io.open() works like fopen and using fopen I cannot try to open a file exclusively - like open(..., "O_CREAT|O_EXCL"). Is there another way to do that?

Lua's io.open() uses fopen() internally, as you guessed, and there's no way to access the POSIXish open() system call from a stock Lua interpreter. You might want to bind that function and related ones to Lua, I didn't see any POSIX module that wraps filesystem calls like open(), close(), dup(), etc. I think someone was coding such a thing, but can't remember.

In the meanwhile you can use the snippet of code attached. They're some misc utilities I use in the webserver I have at home (I use Lua for some ad-hoc administration scripts and some ad-hoc template-based HTML generation). It hadn't open() but I added it for you and other people that might want to use it. You get the waitpid, fork, exec, realpath, unlink, dup, pipe and close POSIX system calls for free, haha :D

Note that you cannot use Lua I/O functions to write/read on file descriptors opened with this module (my open() returns a number, the plain descriptor, not a FILE* wrapped in userdata as Lua standard library does!!!). I'm releasing this because it might be useful to the people out there. It's MIT-licensed, the same license as Lua itself.

I repeat:
  - this code is an ad-hoc hack
  - code tested with Lua 5.0 and OpenBSD 3.5 *only*
- it works for me, but cannot ensure that will work for others (but it should)

Save the attached (ale.c, defs.h)
/*
 * ALE - Adrian's Lua Extensions
 * 
 * IMPORTANT:
 * Note that I use this as a disaster box where I put ad-hoc functions
 * that I need occassionally, it's not a complete library. It's just
 * code that worked for me when I needed it.
 *
 * Copyright (c) 2004, Adrian Perez <mobius@iespana.es>
 *
 * ----- 8< ----- Cut & paste MIT license here ----- 8< -----
 */

#include "defs.h"
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#define _pusherrno(_l)	lua_pushstring(_l, strerror(errno))


static int ale_waitpid(lua_State *l)
{
	pid_t wpid = (pid_t) luaL_checknumber(l, 1);
	int flags  = lua_toboolean(l, 2) ? WNOHANG : 0;
	int status;

	wpid = waitpid(wpid, &status, flags);
	if (wpid == (pid_t)-1) {
		lua_pushnil(l);
		_pusherrno(l);
		return 2;
	}

	lua_pushnumber(l, wpid);
	if (WIFEXITED(status)) {
		lua_pushstring(l, "exited");
		lua_pushnumber(l, WEXITSTATUS(status));
	}
	else if (WIFSIGNALED(status)) {
		lua_pushstring(l, "signaled");
		lua_pushnumber(l, WTERMSIG(status));
	}
	else if (WIFSTOPPED(status)) {
		lua_pushstring(l, "stopped");
		lua_pushnumber(l, WSTOPSIG(status));
	}
	else return 1;

	return 3;
}


static int ale_fork(lua_State *l)
{
	pid_t p = fork();
	if (p == (pid_t) -1) {
		lua_pushnil(l);
		_pusherrno(l);
		return 2;
	}
	lua_pushnumber(l, p);
	return 1;
}


static int ale_exec(lua_State *l)
{
	char **argv;
	const char *exename = luaL_checkstring(l, 1);
	int narg = lua_gettop(l);
	int i;
	
	argv = (char**) xmalloc(sizeof(char*) * narg);
	
	argv[0] = (char*) exename;
	argv[narg-1] = NULL;

	for (i = 2; i <= narg; i++) 
		argv[i-1] = (char*) lua_tostring(l, i);

	if (execvp(exename, argv) == -1) {
		_pusherrno(l);
		return 1;
	}
	return 0;
}


static int ale_realpath(lua_State *l)
{
	char fullpath[MAXPATHLEN];
	
	if (realpath(luaL_checkstring(l, 1), fullpath)) {
		lua_pop(l, 1);
		lua_pushstring(l, fullpath);
		return 1;
	}

	lua_pushnil(l);
	_pusherrno(l);
	return 2;
}


static int ale_unlink(lua_State *l)
{
	int ret = unlink(luaL_checkstring(l, 1));
	if (!ret) {
		lua_pushboolean(l, 1);
		return 1;
	}
	else {
		lua_pushnil(l);
		_pusherrno(l);
		return 2;
	}
}
			

static int ale_dup(lua_State *l)
{
	int oldd = (int) luaL_checknumber(l, 1);
	int newd = (int) luaL_optnumber(l, 2, -1);
	newd = (newd == -1) ? dup(oldd) : dup2(oldd, newd);
	if (newd == -1) {
		lua_pushnil(l);
		_pusherrno(l);
		return 2;
	}
	lua_pushnumber(l, 1);
	return 1;
}


static int ale_pipe(lua_State *l)
{
	int fds[2];
	if (pipe(fds) == -1) {
		lua_pushnil(l);
		_pusherrno(l);
	}
	lua_pushnumber(l, fds[0]);
	lua_pushnumber(l, fds[1]);
	return 2;
}


static int ale_open(lua_State *l)
{
	int fd = open(
			luaL_checkstring(l, 1),
			luaL_optint(l, 2, O_RDONLY),
			luaL_optint(l, 3, 0644)
			);
	if (fd == -1) {
		lua_pushnil(l);
		_pusherrno(l);
		return 2;
	}
	lua_pushnumber(l, fd);
	return 1;
}


static int ale_close(lua_State *l)
{
	if (close(luaL_checkint(l, 1)) == -1) {
		lua_pushnil(l);
		_pusherrno(l);
		return 2;
	}
	lua_pushboolean(l, 1);
	return 1;
}


struct ale_Reg_tag
{
	const char   *name;
	lua_CFunction func;
};
typedef struct ale_Reg_tag ale_Reg;


static ale_Reg ale_funcs[] =
{
	{ "open",     ale_open     },
	{ "close",    ale_close    },
	{ "waitpid",  ale_waitpid  },
	{ "exec",     ale_exec     },
	{ "dup",      ale_dup      },
	{ "pipe",     ale_pipe     },
	{ "unlink",   ale_unlink   },
	{ "realpath", ale_realpath },
	{ NULL,       NULL         },
};


#define _const(_l, _var) \
	do { \
		lua_pushstring((_l), #_var); \
		lua_pushnumber((_l), (_var)); \
		lua_settable((_l), -3); \
	} while (0)


int lua_ale_open(lua_State *l)
{
	unsigned i;
	ASSERT(l);

	lua_pushstring(l, "ale");
	lua_newtable(l);
	for (i = 0; ale_funcs[i].name && ale_funcs[i].func; i++) {
		lua_pushstring(l, ale_funcs[i].name);
		lua_pushcfunction(l, ale_funcs[i].func);
		lua_settable(l, -3);
	}

	_const(l, O_RDONLY);
	_const(l, O_WRONLY);
	_const(l, O_NONBLOCK);
	_const(l, O_APPEND);
	_const(l, O_RDWR);
	_const(l, O_CREAT);
	_const(l, O_TRUNC);
	_const(l, O_EXCL);
	
	lua_settable(l, LUA_GLOBALSINDEX);
	
	return 0;
}


int luaLM_import(lua_State *l)
{
	return lua_ale_open(l);
}

const char* luaLM_version(void)
{
	return LUA_VERSION;
}

/*
 * Common definitions.
 * Copyright (C) 2003 Adrian Perez de Castro
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef __defs__h
#define __defs__h


/* Possible actions to perform on failed assertion. */
#define ASSERT_ABORT abort()
#define ASSERT_EXIT  exit(-1)

/*
 * Action to perform on assertion fail. May be 
 * ASSERT_EXIT or ASSERT_ABORT.
 */
#ifndef ASSERT_FAIL_ACTION
# define ASSERT_FAIL_ACTION ASSERT_ABORT
#endif /* !ASSERT_FAIL_ACTION */

/*
 * The ASSERT macro.
 * The GCC '__PRETTY_FUNCTION__' pseudo-macro is used to
 * include function names in output of assertions, when
 * compiling with it.
 */
#if defined(_DEBUG) || defined(DEBUG)
# ifdef __GNUC__
#  include <stdio.h>
	 extern void abort(void);
	 /* Extended macro, used with GCC */ 
#  define ASSERT(__expr) \
			( \
				(__expr) ? (void)0 \
				: (fprintf(stderr, "%s: function %s: line %u:\n" \
							     "   assertion '%s' failed\n", \
									 __FILE__, __PRETTY_FUNCTION__, __LINE__, \
									  #__expr), fflush(stderr), \
					 ASSERT_FAIL_ACTION) \
			)
# else  /* __GNUC__ */
	 /* Standard macro, used with other compilers */
#  define DEBUG
#  include <assert.h>
#  define ASSERT assert
# endif /* __GNUC__ */
	/* 
	 * Define to empty when not compiling with debugging
	 * enabled.
	 */
#else  /* (_DEBUG || DEBUG) */
# define ASSERT(__ignore) ((void)0)
#endif /* (_DEBUG || DEBUG) */


/*
 * Header declarations start/end macros. These protect definitions
 * to be mangled by C++ compilers, so linking with plain C functions
 * and variables is done correctly.
 */
#if defined(__cplusplus) || defined(c_plusplus)
# define BEGIN_DECLS	extern "C" {
# define END_DECLS		}
#else
# define BEGIN_DECLS
# define END_DECLS
#endif


/*
 * Chain of fools to detect wether we are using Win32.
 */
#if defined(__WINDOWS__) || defined(_WINDOWS) || defined(_Windows) || \
		defined(__WIN32__)   || defined(_WIN32)   || defined(WIN32)    || \
		defined(__NT__) || defined(__NT_DLL__) || defined(__WINDOWS_386__)
# define PLAT_WIN32
#else
# define PLAT_POSIX
#endif


/*
 * The API macro is used to mark functions that must be exported to
 * the world. Under Win32 it's expanded to export symbols in DLLs
 * when appropiate.
 */
#if	defined(PLAT_WIN32) && defined(BUILD_DLL)
# define API extern __declspec(dllexport)
#elif defined(PLAT_WIN32) && defined(USE_DLL)
# define API extern __declspec(dllimport)
#else
# define API extern
#endif


/*
 * Exits the program printing a string to stdout.
 */
extern void exit();
#include <stdio.h>
#define __die(__str) \
	( fprintf(stderr, "fatal: %s\n", __str), exit(-1) )

/*
 * The xmalloc(), xrealloc() and xfree() act as expected, but they
 * do pointer checks when needed. They are defined as inlines or
 * macros, so using them does not involve extra function calls.
 * "__inline" is used to prevent complains of old silly compilers.
 */
extern void* malloc();
extern void* realloc();
extern void free();

#ifdef __GNUC__
# define INLINE inline
#else
# define INLINE __inline
#endif

static INLINE void* xmalloc(size_t sz)
{
	register void *p = malloc(sz);
	return (p ? p : (__die("out of memory"), (void*)0));
}

#define xrealloc(__p, __nsz) \
		( (__p) = realloc(__p, __nsz), (__p) ? __p : (__die("out of memory"), (void*)0) )

#define xfree(__p) \
	( free(__p), (__p) = NULL )

	
/*
 * strdup() replacement
 */
#if !defined(HAVE_STRDUP) && (defined(_OSX) || defined(_LINUX))
# define HAVE_STRDUP
#endif

#if defined(HAVE_STRDUP) && defined(__GNUC__)
# define xstrdup(__strp) 																		\
   ({	char *__$strp = strdup(__strp); 											\
  		(__$strp) ? : (__die("out of memory"), (char*)0); })
#else
# ifdef __GNUC__
#  define xstrdup(__strp)                                  	\
		({const char* __$strp = __strp;                        	\
			strcpy(                                              	\
				(char*) xmalloc(sizeof(char)*(strlen(__$strp)+1)), 	\
				__$strp); })
# else
static INLINE char* xstrdup(const char *strp)
{
	return strcpy(
			(char*) xmalloc(sizeof(char) * (strlen(strp) + 1)), 
			strp);
}
# endif
#endif

#endif /* !__defs__h */
files to a directory and compile it as into a shared library (.so). Load the module from Lua as usual:

$ gcc -shared -o libale.so ale.c -I. -llua -llualib   # Linux/*BSD
$ gcc -bundle -o libale.so ale.c -I. -llua -llualib   # MacOS X

And test with this Lua snippet:

-------- 8< ------------------------- Lua code snippet, cut here -------
-- Open "ale" module
local init, err = loadlib("./libale.so", "lua_ale_open")
assert (init, err)

-- Call initialization function and make Lua collect unneeded vars
init()
init, err = nil, nil

-- Use ale.open() to create your lockfile
descriptor, error = ale.open("/tmp/lock", ale.O_RDWR + ale.O_CREAT + ale.O_EXCL)
if (not descriptor) then
   -- open failed, print error
   print("failed to acquire lockfile: " .. error)
else
   print("got lockfile")
end

-- do something here with the lockfile opened

-- close the lockfile
ale.close(descriptor)
-------- 8< ------------------------- Lua code snippet, cut here -------

Hope it will be useful.

-ap

Attachment: PGP.sig
Description: Mensaje firmado digitalmente