lua-users home
lua-l archive

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


It was thus said that the Great Viacheslav Usov once stated:
> If anything like this has been done before, kindly point me to that.
> 
> I have an application that hosts one or more lua_State instances. I
> want to control what libraries a given instance can load. For that
> purpose, I have disabled the default searchers (except the preload)
> and have my own searcher that decides what can be loaded. I have also
> replaced the global 'require' with my own function, which currently
> just calls (eventually) into the original require. So I can already
> control what libraries a given instance can load, no problem.
> 
> Now I'd like to make this a bit more sophisticated. I want to be able
> to say that if library X can be loaded, then all the libraries that X
> loads directly or indirectly can also be loaded, without having to
> specify them upfront.

  So I suppose you have something like:

	do
	  local lua_require = require
	  local function new_require(modname)
	    if allowed_module(modname) then
	      return lua_require(modname)
	    else
	      error(string.format("module %s not allowed\n",modname))
	    end
	  end
	  require = new_require
	end

  This might do what you want, and cover all the cases:
  
  	do
  	  -- --------------------------------------------------------------
  	  -- Save the current _ENV into a table.  This copy will have the
  	  -- original Lua require function, and any allowed module that is
  	  -- loaded will get this new environment allowing it to always load
  	  -- any modules it wants.
  	  --
  	  -- The current _ENV will have a custom require that checks if a
  	  -- module is allowed to be loaded.
  	  -- --------------------------------------------------------------
  	  
  	  local newenv   = duptable(_ENV)
  	  newenv.require = lua_require
  	  
  	  local function new_require(modname)
  	    if package.loaded[modname] then
  	      return package.loaded[modname]
  	    end
  	    
  	    if allowed_module(modname) then
  	      for _,loader in ipairs(package.searchers) do
  	        local modfun,extra = loader(modname)
  	        if type(modfun) == 'function' then
  	          -- ---------------------
  	          -- XXX might want to double check that _ENV ius always the
		  -- first upvalue.  Or cycle through any upvalues, looking
		  -- for a table that is equal to the current _ENV.  But
		  -- anyway, the allowed module will have access to the
		  -- actual Lua require, not our custom one.
		  -- ----------------------
		  
		  debug.setupvalue(modfun,1,newenv)
		  local ret = modfun(modname,extra)
		  package.loaded[modname] = ret or true
		  return ret
		end
	      end
	    end
	    error("This shouldn't happen if a module is allowed, should it?")
	  end
	  require = new_require
	end

  This will hopefully handle cases like the following:
  
  	-- a silly module that is allowed
  	local foo = require "foo"  -- this is allowed
  	return {
  	  bar = function()
  	    local blah = require "bar" -- this is also allowed
  	    -- rest of function
  	  end
  	}

> For that I have to overcome a few hurdles. The biggest as it seems to
> me today is that I will have to understand that library Y is requested
> directly or indirectly by X (or not). I am not exactly sure how this
> can be done.

  The above code appears to cover the indirect method, but it's untested
code, so use at your own risk.  Hopefully, the code is clear enough in what
it's doing---which is calling the package.searchers[] directly, and
modifying the global environment of each module's loading function to
include the actual Lua require, not your custom one.

> In principle, I'd be interested in a design that works equally with
> native and pure Lua libraries. But one that works only with pure Lua
> libraries would also be a good step forward. 

  The trick I did above appears to work for Lua based modules [2], but I'm
unsure how well it would work for C based ones.  It would probably be wise
to do more error checking than I did.

  -spc (Hopefully this will point you in the right direction)

[1]	I wanted to make a single executable out of a Lua application and
	wanted to make sure all the modules it uses were included in the
	executable.  I used the code to log the modules as they were loaded.

[2]	By manually doing:

		fun,extra = package.searchers[2]("lua-based-module")
	
	and checking the upvalues of the returned function fun and yup,
	there's _ENV as the first upvalue.  But:

		fun,extra = package.searchers[3]("C-based-module")
		
	shows fun having no upvalues.  But I'm not aware of many C based
	modules that require other modules directly.