<commit_before>-- Use of this source code is governed by the Apache 2.0 license; see COPYING.
module(..., package.seeall)

local S = require("syscall")
local ffi = require("ffi")
local lib = require("core.lib")
local shm = require("core.shm")
local rpc = require("lib.yang.rpc")
local yang = require("lib.yang.yang")
local data = require("lib.yang.data")
local path_resolver = require("lib.yang.path").resolver

function show_usage(command, status, err_msg)
   if err_msg then print('error: '..err_msg) end
   print(require("program.config."..command:gsub('-','_')..".README_inc"))
   main.exit(status)
end

local parse_command_line_opts = {
   command = { required=true },
   with_config_file = { default=false },
   with_path = { default=false },
   with_value = { default=false },
   require_schema = { default=false }
}

local function path_grammar(schema_name, path)
   local schema = yang.load_schema_by_name(schema_name)
   local grammar = data.data_grammar_from_schema(schema)
   local getter, subgrammar = path_resolver(grammar, path)
   return subgrammar
end

function data_parser(schema_name, path)
   return data.data_parser_from_grammar(path_grammar(schema_name, path))
end

function parse_command_line(args, opts)
   opts = lib.parse(opts, parse_command_line_opts)
   local function err(msg) show_usage(opts.command, 1, msg) end
   local ret = {}
   local handlers = {}
   function handlers.h() show_usage(opts.command, 0) end
   function handlers.s(arg) ret.schema_name = arg end
   function handlers.r(arg) ret.revision_date = arg end
   function handlers.c(arg) ret.socket = arg end
   args = lib.dogetopt(args, handlers, "hs:r:c:",
                       {help="h", ['schema-name']="s", schema="s",
                        ['revision-date']="r", revision="r", socket="c"})
   if #args == 0 then err() end
   ret.instance_id = table.remove(args, 1)
   local descr = call_leader(ret.instance_id, 'describe', {})
   if not ret.schema_name then
      if opts.require_schema then err("missing --schema arg") end
      ret.schema_name = descr.native_schema
   end
   require('lib.yang.schema').set_default_capabilities(descr.capability)
   if opts.with_config_file then
      if #args == 0 then err("missing config file argument") end
      local file = table.remove(args, 1)
      local opts = {schema_name=ret.schema_name,
                    revision_date=ret.revision_date}
      ret.config_file = file
      ret.config = yang.load_configuration(file, opts)
   end
   if opts.with_path then
      if #args == 0 then err("missing path argument") end
      -- FIXME: Validate path?
      ret.path = table.remove(args, 1)
   end
   if opts.with_value then
      local parser = data_parser(ret.schema_name, ret.path)
      if #args == 0 then
         ret.value_str = io.stdin:read('*a')
      else
         ret.value_str = table.remove(args, 1)
      end
      ret.value = parser(ret.value_str)
   end
   if #args ~= 0 then err("too many arguments") end
   return ret
end

function open_socket_or_die(instance_id)
   S.signal('pipe', 'ign')
   local socket = assert(S.socket("unix", "stream"))
   local tail = instance_id..'/config-leader-socket'
   local by_name = S.t.sockaddr_un(shm.root..'/by-name/'..tail)
   local by_pid = S.t.sockaddr_un(shm.root..'/'..tail)
   if not socket:connect(by_name) and not socket:connect(by_pid) then
      io.stderr:write(
         "Could not connect to config leader socket on Snabb instance '"..
            instance_id.."'.\n")
      main.exit(1)
   end<commit_msg>Fix #638 - Validate path on snabb config get<commit_after>-- Use of this source code is governed by the Apache 2.0 license; see COPYING.
module(..., package.seeall)

local S = require("syscall")
local ffi = require("ffi")
local lib = require("core.lib")
local shm = require("core.shm")
local rpc = require("lib.yang.rpc")
local yang = require("lib.yang.yang")
local data = require("lib.yang.data")
local path_resolver = require("lib.yang.path").resolver

function show_usage(command, status, err_msg)
   if err_msg then print('error: '..err_msg) end
   print(require("program.config."..command:gsub('-','_')..".README_inc"))
   main.exit(status)
end

local parse_command_line_opts = {
   command = { required=true },
   with_config_file = { default=false },
   with_path = { default=false },
   with_value = { default=false },
   require_schema = { default=false }
}

local function path_grammar(schema_name, path)
   local schema = yang.load_schema_by_name(schema_name)
   local grammar = data.data_grammar_from_schema(schema)
   local getter, subgrammar = path_resolver(grammar, path)
   return subgrammar
end

function data_parser(schema_name, path)
   return data.data_parser_from_grammar(path_grammar(schema_name, path))
end

function error_and_quit(err)
   io.stderr:write(err .. "\n")
   io.stderr:flush()
   os.exit(1)
end

function validate_path(schema_name, path)
   local succ, err = pcall(path_grammar, schema_name, path)
   if succ == false then
      error_and_quit(err)
   end
end

function parse_command_line(args, opts)
   opts = lib.parse(opts, parse_command_line_opts)
   local function err(msg) show_usage(opts.command, 1, msg) end
   local ret = {}
   local handlers = {}
   function handlers.h() show_usage(opts.command, 0) end
   function handlers.s(arg) ret.schema_name = arg end
   function handlers.r(arg) ret.revision_date = arg end
   function handlers.c(arg) ret.socket = arg end
   args = lib.dogetopt(args, handlers, "hs:r:c:",
                       {help="h", ['schema-name']="s", schema="s",
                        ['revision-date']="r", revision="r", socket="c"})
   if #args == 0 then err() end
   ret.instance_id = table.remove(args, 1)
   local descr = call_leader(ret.instance_id, 'describe', {})
   if not ret.schema_name then
      if opts.require_schema then err("missing --schema arg") end
      ret.schema_name = descr.native_schema
   end
   require('lib.yang.schema').set_default_capabilities(descr.capability)
   if opts.with_config_file then
      if #args == 0 then err("missing config file argument") end
      local file = table.remove(args, 1)
      local opts = {schema_name=ret.schema_name,
                    revision_date=ret.revision_date}
      ret.config_file = file
      ret.config = yang.load_configuration(file, opts)
   end
   if opts.with_path then
      if #args == 0 then err("missing path argument") end
      ret.path = table.remove(args, 1)
      validate_path(ret.schema_name, ret.path)
   end
   if opts.with_value then
      local parser = data_parser(ret.schema_name, ret.path)
      if #args == 0 then
         ret.value_str = io.stdin:read('*a')
      else
         ret.value_str = table.remove(args, 1)
      end
      ret.value = parser(ret.value_str)
   end
   if #args ~= 0 then err("too many arguments") end
   return ret
end

function open_socket_or_die(instance_id)
   S.signal('pipe', 'ign')
   local socket = assert(S.socket("unix", "stream"))
   local tail = instance_id..'/config-leader-socket'
   local by_name = S.t.sockaddr_un(shm.root..'/by-name/'..tail)
   local by_pid = S.t.sockaddr_un(shm.root..'/'..tail)
   if not socket:connect(by_name) and not socket:connect(by_pid) then
      io.stderr:write(
         "Could not connect to config leader socket on Snabb instance '"..
            instance_id.."'.\n")
      main.exit(1)
   end
{'fraction_non_alphanumeric': 0.1282505105513955, 'fraction_numerical': 0.004356705241660994, 'mean_word_length': 3.4199759326113117, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5900018', 'n_tokens_mistral': 2479, 'n_tokens_neox': 2401, 'n_words': 588}