if not modules then modules = { } end modules ['cont-run'] = { version = 1.001, comment = "companion to cont-yes.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- When a style is loaded there is a good change that we never enter -- this code. local type, tostring = type, tostring local report_sandbox = logs.reporter("sandbox","call") local report_system = logs.reporter("system") local fastserialize = table.fastserialize local quoted = string.quoted local possiblepath = sandbox.possiblepath local context = context local implement = interfaces.implement local texset = tex.set local qualified = { } local writeable = { } local readable = { } local blocked = { } local trace_files = false local trace_calls = false local nofcalls = 0 local nofrejected = 0 local logfilename = "sandbox.log" local function registerstats() statistics.register("sandboxing", function() if trace_files then return string.format("%i calls, %i rejected, logdata in '%s'",nofcalls,nofrejected,logfilename) else return string.format("%i calls, %i rejected",nofcalls,nofrejected) end end) registerstats = false end local function logsandbox(details) local comment = details.comment local result = details.result local arguments = details.arguments for i=1,#arguments do local argument = arguments[i] local t = type(argument) if t == "string" then arguments[i] = quoted(argument) if trace_files and possiblepath(argument) then local q = qualified[argument] if q then local c = q[comment] if c then local r = c[result] if r then c[result] = r + 1 else c[result] = r end else q[comment] = { [result] = 1 } end else qualified[argument] = { [comment] = { [result] = 1 } } end end elseif t == "table" then arguments[i] = fastserialize(argument) else arguments[i] = tostring(argument) end end if trace_calls then report_sandbox("%s(%,t) => %l",details.comment,arguments,result) end nofcalls = nofcalls + 1 if not result then nofrejected = nofrejected + 1 end end local ioopen = sandbox.original(io.open) -- dummy call local function logsandboxfiles(name,what,asked,okay) -- we're only interested in permitted access if not okay then blocked [asked] = blocked [asked] or 0 + 1 elseif what == "*" or what == "w" then writeable[asked] = writeable[asked] or 0 + 1 else readable [asked] = readable [asked] or 0 + 1 end end function sandbox.logcalls() if not trace_calls then trace_calls = true sandbox.setlogger(logsandbox) if registerstats then registerstats() end end end function sandbox.logfiles() if not trace_files then trace_files = true sandbox.setlogger(logsandbox) sandbox.setfilenamelogger(logsandboxfiles) luatex.registerstopactions(function() table.save(logfilename,{ calls = { nofcalls = nofcalls, nofrejected = nofrejected, filenames = qualified, }, checkednames = { readable = readable, writeable = writeable, blocked = blocked, }, }) end) if registerstats then registerstats() end end end trackers.register("sandbox.tracecalls",sandbox.logcalls) trackers.register("sandbox.tracefiles",sandbox.logfiles) local sandboxing = environment.arguments.sandbox local debugging = environment.arguments.debug if sandboxing then report_system("enabling sandbox") sandbox.enable() if type(sandboxing) == "string" then sandboxing = utilities.parsers.settings_to_hash(sandboxing) if sandboxing.calls then sandbox.logcalls() end if sandboxing.files then sandbox.logfiles() end end -- Nicer would be if we could just disable write 18 and keep os.execute -- which in fact we can do by defining write18 as macro instead of -- primitive ... todo ... well, it has been done now. -- We block some potential escapes from protection. context [[\let\primitive\relax\let\normalprimitive\relax]] debug = { traceback = debug.traceback, } package.loaded.debug = debug elseif debugging then -- we keep debug else debug = { traceback = debug.traceback, getinfo = debug.getinfo, sethook = debug.sethook, } package.loaded.debug = debug end local function setoverloadmode(overloadmode) if overloadmode == "warning" then overloadmode = 3 -- 5 elseif overloadmode == "error" then overloadmode = 4 -- 6 else overloadmode = tonumber(overloadmode) end if overloadmode then texset("overloadmode",overloadmode) end end directives.register("overloadmode", setoverloadmode) local function processjob() tokens.setters.macro("processjob","","permanent") environment.initializefilenames() -- todo: check if we really need to pre-prep the filename local arguments = environment.arguments local suffix = environment.suffix local filename = environment.filename -- hm, not inputfilename ! environment.lmtxmode = CONTEXTLMTXMODE -- should be true -- directives -- silent -- batchmode if arguments.directives then directives.enable(arguments.directives) end if arguments.nosynctex then luatex.synctex.setup { state = interfaces.variables.never, } elseif arguments.synctex then luatex.synctex.setup { state = interfaces.variables.start, method = interfaces.variables.max, } end logs.registerfinalactions(function() logs.pushtarget("log") statistics.showusage("finish") logs.poptarget() end) setoverloadmode(arguments.overloadmode) if not filename or filename == "" then -- skip elseif suffix == "svg" or arguments.forcesvg then report_system("processing svg output: %s",filename) context.starttext() context.startTEXpage() context.externalfigure ( { filename }, { conversion = "mp" } ) context.stopTEXpage() context.stoptext() elseif suffix == "xml" or arguments.forcexml then -- Maybe we should move the preamble parsing here as it -- can be part of (any) loaded (sub) file. The \starttext -- wrapping might go away. report_system("processing as xml: %s",filename) context.starttext() context.xmlprocess("main",filename,"") context.stoptext() elseif suffix == "cld" or arguments.forcecld then report_system("processing as cld: %s",filename) context.runfile(filename) elseif suffix == "lua" or arguments.forcelua then -- The wrapping might go away. Why is is it there in the -- first place. report_system("processing as lua: %s",filename) context.starttext() context.ctxlua(string.format('dofile("%s")',filename)) context.stoptext() elseif suffix == "mp" or arguments.forcemp then report_system("processing as metapost: %s",filename) context.starttext() context.processMPfigurefile(filename) context.stoptext() -- elseif suffix == "prep" then -- -- -- Why do we wrap here. Because it can be xml? Let's get rid -- -- of prepping in general. -- -- context.starttext() -- context.input(filename) -- context.stoptext() elseif suffix == "mps" or arguments.forcemps then report_system("processing metapost output: %s",filename) context.starttext() context.startTEXpage() context.externalfigure { filename } context.stopTEXpage() context.stoptext() else -- \writestatus{system}{processing as tex} -- We have a regular tex file so no \starttext yet as we can -- load fonts. -- context.enabletrackers { "resolvers.*" } if type(arguments.forceinput) == "string" then filename = arguments.forceinput or filename end context.input(filename) -- context.disabletrackers { "resolvers.*" } end context.finishjob() end implement { name = "processjob", -- protected = true public = true, onlyonce = true, actions = processjob, } texconfig.firstline = "\\processjob " -- experiment, yet undocumented