#!/usr/bin/lua --[[ UCI Validation Layer - Command Line Utility (c) 2008 Jo-Philipp Wich (c) 2008 Steven Barth Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 $Id$ ]]-- require("luci.uvl") require("luci.util") function getopt( arg, options ) options = options or "" local tab = {} local args = {} for k, v in ipairs(arg) do if v:sub(1, 2) == "--" then local x = v:find( "=", 1, true ) if x then tab[ v:sub( 3, x-1 ) ] = v:sub( x+1 ) else tab[ v:sub( 3 ) ] = true end elseif v:sub( 1, 1 ) == "-" then local y = 2 local l = #v local jopt while ( y <= l ) do jopt = v:sub( y, y ) if options:find( jopt, 1, true ) then if y < l then tab[ jopt ] = v:sub( y+1 ) y = l else tab[ jopt ] = arg[ k + 1 ] arg[ k + 1 ] = "" end else tab[ jopt ] = true end y = y + 1 end elseif #v > 0 then table.insert(args, v) end end return tab, args end function genspec(conf) require("luci.model.uci") require("luci.uvl.datatypes") local uci = luci.model.uci.cursor() local ok, err = uci:load(conf) if not ok then print("Can not load config:", err) os.exit(1) else local function _guess_datatype(v) if type(v) == "table" then v = v[1] end for _, type in ipairs({ "boolean", "integer", "float", "ip4addr", "ip6addr", "macaddr", "directory", "file" }) do if luci.uvl.datatypes[type](v) then return type end end return "string" end local co = uci:get_all(conf) local ct = { } local ca = { } local so = { } local to = { } -- count section types for _, section in pairs(co) do ct[section['.type']] = ( ct[section['.type']] or 0 ) + 1 ca[section['.type']] = section['.anonymous'] so[section['.type']] = so[section['.type']] or { } to[section['.type']] = to[section['.type']] or { } for option, value in pairs(section) do if option:sub(1,1) ~= "." then so[section['.type']][option] = _guess_datatype(value) to[section['.type']][option] = ( type(value) == "table" and "list" or "variable" ) end end end -- package name print( "package %s" % conf ) -- write section schemes for type, count in luci.util.kspairs(ct) do print( "\nconfig section" ) print( "\toption name '%s'" % type ) print( "\toption title 'Section %s'" % type ) print( "\toption package '%s'"% conf ) print( "\toption named %s" % ( ca[type] and 'false' or 'true' ) ) print( "\toption unique %s" % ( ct[type] > 1 and 'false' or ( ca[type] and 'false' or 'true' ) ) ) print( "\toption dynamic false" ) print( "\toption required false" ) -- write option schemes for opt, val in luci.util.kspairs(so[type]) do print( "\nconfig variable" ) print( "\toption name '%s'" % opt ) print( "\toption title 'Option %s'" % opt ) print( "\toption section '%s.%s'" %{ conf, type } ) print( "\toption datatype '%s'" % so[type][opt] ) if to[type][opt] ~= "variable" then print( "\toption type '%s'" % to[type][opt] ) end end print("") end end end local options, arguments = getopt( arg ) if #arguments ~= 2 or options.help then print([=[ uvl - UCI Validation Layer $Id$ (c) 2008 Jo-Philipp Wich, Steven Barth Usage: uvl --help uvl [--silent] [--schemedir=DIR] [--configdir=DIR] [--no-strict-sections] \ [--no-strict-options] [--no-strict-validators] [--no-strict-lists] \ {verify|verify-scheme|genspec} config[.section[.option]] Options: --help Display this help message. --silent Don't produce any output. --schemedir=DIR Use DIR as scheme directory. --configdir=DIR Use DIR as config directory. --no-strict-sections Don't treat sections found in config but not in scheme as error. --no-strict-options Don't treat options found in config but not in scheme as error. --no-strict-validators Don't invalidate config if an external validator fails. --no-strict-lists Don't invalidate lists that are stored options. Actions: verify Validate given configuration, section or option. verify-scheme Validate given scheme against the reference meta scheme. genspec Generate a scheme skeleton from given configuration. ]=]) os.exit(255) elseif arguments[1] == "verify" or arguments[1] == "verify-scheme" then luci.uvl.STRICT_UNKNOWN_SECTIONS = ( not options['no-strict-sections'] and true or false ) luci.uvl.STRICT_UNKNOWN_OPTIONS = ( not options['no-strict-options'] and true or false ) luci.uvl.STRICT_EXTERNAL_VALIDATORS = ( not options['no-strict-validators'] and true or false ) luci.uvl.STRICT_LIST_TYPE = ( not options['no-strict-lists'] and true or false ) local uvl = luci.uvl.UVL( type(options.schemedir) == "string" and options.schemedir, type(options.configdir) == "string" and options.configdir ) local cso = luci.util.split( arguments[2], "." ) local ok, err if arguments[1] == "verify-scheme" then uvl:read_scheme( 'schema', cso[1] ) local uci = uvl.uci.cursor( type(options.configdir) == "string" and options.configdir or uvl.schemedir .. '/default' ) ok, err = uvl:validate_config( cso[1], uci:get_all(cso[1]) ) if err then err.code = luci.uvl.errors.ERR_SCHEME end else ok, err = uvl:validate( unpack(cso) ) end if ok then if not options.silent then print( string.format( '%s "%s" validates fine!', ( arguments[1] == "verify-scheme" and "Scheme" or ( #cso == 1 and "Config" or ( #cso == 2 and "Section" or "Option" ) ) ), table.concat(cso, ".") ) ) end os.exit( 0 ) else if not options.silent then print( err and err:string() or "Unknown error" ) end os.exit( 1 ) end else genspec( arguments[2] ) end