From d55ce5ffc9387d788983e19d68449b7a5d8e4312 Mon Sep 17 00:00:00 2001 From: Rafael Gago Date: Wed, 7 Feb 2018 12:55:19 +0100 Subject: [PATCH 1/4] interface: Don't mutate user options This patch fixes cases where the 'opts' table passed by the user is modified, making the external table unable to be reused in some cases. This was causing problems e.g. when dealing with properties, as the user was having a predefined dbus options table, as the "interface" key was changed internally to 'org.freedesktop.DBus.Properties' without being changed back on return. --- init.lua | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/init.lua b/init.lua index 5d81802..e4fbe87 100644 --- a/init.lua +++ b/init.lua @@ -17,6 +17,22 @@ else dbus.exit = dbus.raw.exit end +local function optscopy(opts) + if type(opts) ~= "table" then return nil end + return { + args = opts.args, + bus = opts.bus, + callback = opts.callback, + destination = opts.destination, + handler = opts.handler, + interface = opts.interface, + origin = opts.origin, + path = opts.path, + sender = opts.sender, + type = opts.type, + } +end + function dbus.signal_handler(signal, ...) signal.events = ((dbus.signals[signal.bus] or {})[signal.interface] or {}).events if not signal.events then return end @@ -58,7 +74,7 @@ function dbus.on(name, callback, opts) elseif callback and type(callback) ~= 'function' then callback, opts = nil, callback end - opts = opts or {} + local opts = optscopy(opts) or {} opts.type = opts.type or "signal" opts.bus = opts.bus or "session" callback = callback or opts.callback @@ -115,7 +131,7 @@ end function dbus.off(name, callback, opts) - opts = opts or {} + local opts = optscopy(opts) or {} opts.type = opts.type or "signal" opts.bus = opts.bus or "session" local signal = (dbus.signals[opts.bus] or {})[opts.interface] @@ -156,7 +172,7 @@ function dbus.call(name, callback, opts) if callback and type(callback) ~= 'function' then callback, opts = nil, callback end - opts = opts or {} + local opts = optscopy(opts) or {} opts.type = opts.type or "method_call" opts.bus = opts.bus or "session" callback = callback or opts.callback @@ -179,9 +195,8 @@ function dbus.call(name, callback, opts) return serial end - function dbus.property.get(name, callback, opts) - opts = opts or {} + local opts = optscopy(opts) or {} opts.args = {'s', opts.interface, 's', name} -- actual arguments to Get opts.interface = 'org.freedesktop.DBus.Properties' callback = callback or opts.callback @@ -190,7 +205,7 @@ end function dbus.property.set(name, value, opts) - opts = opts or {} + local opts = optscopy(opts) or {} opts.args = {'s', opts.interface, 's', name, 'v', value} -- actual arguments to Get opts.interface = 'org.freedesktop.DBus.Properties' return dbus.call('Set', opts) From 657f3d81c0c013e5b72fddc52317537f54b5b2d3 Mon Sep 17 00:00:00 2001 From: Rafael Gago Date: Fri, 9 Feb 2018 14:44:53 +0100 Subject: [PATCH 2/4] ldbus: fix append_arg "append_arg" was broken for some nested types like "a{sa{sv}}" on the ldbus backend. This reimplements append_arg, fixing the bugs. --- awesome/dbus.lua | 84 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/awesome/dbus.lua b/awesome/dbus.lua index 4809fbe..ad83ad7 100644 --- a/awesome/dbus.lua +++ b/awesome/dbus.lua @@ -175,41 +175,69 @@ function dbus.type(value) return typ end -function dbus.append_arg(iter, value, typ, subtyp) - for _, v in pairs(ldbus.basic_types) do - if v == typ then - iter:append_basic(value, typ) - return - end +dbus.set_of_basic_types = {} +for _, v in pairs (ldbus.basic_types) do + dbus.set_of_basic_types[v] = true +end + +function dbus.consume_type(dtype) + if not dtype then + return nil end - local subiter - if string.sub(typ, 1, 1) == ldbus.types.array then - local subtyp = string.sub(typ, 2) - subiter = iter:open_container(string.sub(typ, 1, 1), subtyp) - if string.sub(subtyp, 1, 1) ~= '{' then - for _, v in ipairs(value) do - dbus.append_arg(subiter, v, subtyp) + local fchar = dtype:sub(1, 1) + if fchar == "{" then + assert(dtype:sub(-1) == "}") + return ldbus.types.dict_entry, dtype:sub(2, -2) + elseif dbus.set_of_basic_types[fchar] or fchar == ldbus.types.array then + return fchar, dtype:sub(2) + elseif fchar == ldbus.types.variant then + return fchar, nil -- The type of a variant can't be detected + end + error("structs unimplemented for now, type: "..dtype, 2) +end + +local function error_on(condition, text, lvl) + if condition then + error(text, lvl and lvl + 1 or 2) + end +end + +function dbus.append_arg(iter, value, dbus_type) + local dt, dt_next = dbus.consume_type(dbus_type) + if dbus.set_of_basic_types[dt] then + error_on(type(value) == "table", "expected a basic type, got a table") + iter:append_basic(value, dt) + elseif dt == ldbus.types.array then + error_on(type(value) ~= "table", "expected a table") + local arr_iter = iter:open_container(dt, dt_next) + local arr_dt, arr_dt_next = dbus.consume_type(dt_next) + if arr_dt == ldbus.types.dict_entry then + local key_dt, value_dt = dbus.consume_type(arr_dt_next) + error_on( + dbus.set_of_basic_types[key_dt] == nil, + "the key of a dict entry has to be a basic type" + ) + for k, v in pairs(value) do + local dict_iter = arr_iter:open_container(arr_dt) + dict_iter:append_basic(k, key_dt) + dbus.append_arg(dict_iter, v, value_dt) + arr_iter:close_container(dict_iter) end else - subtyp = subtyp:match('{(%w+)}') - for k, v in pairs(value) do - dbus.append_arg(subiter, {k,v}, ldbus.types.dict_entry, subtyp) + for _, v in ipairs(value) do + dbus.append_arg(arr_iter, v, dt_next) end end - elseif typ == ldbus.types.variant then - local subtyp = dbus.type(value) - subiter = iter:open_container(string.sub(typ, 1, 1), subtyp) - dbus.append_arg(subiter, value, subtyp) + iter:close_container(arr_iter) + elseif dt == ldbus.types.variant then + local val, var_dt = value, dbus.type(value) + local var_iter = iter:open_container(dt, var_dt) + dbus.append_arg(var_iter, val, var_dt) + iter:close_container(var_iter) else - subiter = iter:open_container(string.sub(typ, 1, 1)) - end - if typ == ldbus.types.dict_entry then - dbus.append_arg(subiter, value[1], string.sub(subtyp, 1, 1)) - dbus.append_arg(subiter, value[2], string.sub(subtyp, 2, 2)) - elseif typ == ldbus.types.struct then - -- TODO + error_on(dt == ldbus.types.dict_entry, "dict_entry outside of array") + error_on(dt == ldbus.types.struct, "structs are unsupported") end - iter:close_container(subiter) end function dbus.get_bus(name) From c681e153aa676b0bcc4daa7a26daa0d810536ae2 Mon Sep 17 00:00:00 2001 From: Rafael Gago Date: Mon, 12 Feb 2018 10:33:07 +0100 Subject: [PATCH 3/4] ldbus: Allow specifying variant types for args On the ldbus backend the function dbus.new_variant has been added. This allows overriding the variant type autodetection for types not expressable as arguments on the LUA type system, e.g. 'o', which gets translated to string. Note that this function gets available as dbus.raw.new_variant Example usage: local variant = dbus.raw.new_variant myvariant = { 'v', variant('aa{sv}', { { k1 = 'v1' }, --val is string variant (autodetected) { k2 = variant ('o', '/some/dbus/path')} }) } --- awesome/dbus.lua | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/awesome/dbus.lua b/awesome/dbus.lua index ad83ad7..5a92d62 100644 --- a/awesome/dbus.lua +++ b/awesome/dbus.lua @@ -180,6 +180,13 @@ for _, v in pairs (ldbus.basic_types) do dbus.set_of_basic_types[v] = true end +dbus.variant_mt = {} +function dbus.new_variant(vtype, value) + assert(value) + vtype = vtype or dbus.type(value) + return setmetatable ({ t = vtype, v = value }, dbus.variant_mt) +end + function dbus.consume_type(dtype) if not dtype then return nil @@ -230,7 +237,15 @@ function dbus.append_arg(iter, value, dbus_type) end iter:close_container(arr_iter) elseif dt == ldbus.types.variant then - local val, var_dt = value, dbus.type(value) + local val, var_dt + if type(value) == "table" and + getmetatable(value) == dbus.variant_mt then + val = value.v + var_dt = value.t + else + val = value + var_dt = dbus.type(value) + end local var_iter = iter:open_container(dt, var_dt) dbus.append_arg(var_iter, val, var_dt) iter:close_container(var_iter) From 63fea7c100fd0ed5c796e22c101e747a97f742e7 Mon Sep 17 00:00:00 2001 From: Rafael Gago Date: Tue, 13 Feb 2018 08:59:53 +0100 Subject: [PATCH 4/4] ldbus: Fix iter_args on the ldbus backend "iter_args" was completely broken for complex types, calling a function returning "a{sa{sv}}" like e.g. "org.freedesktop.NetworkmManager.Settings.Connection.GetSettings" failed completely. This patch reimplements "iter_args" correctly. --- awesome/dbus.lua | 80 ++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/awesome/dbus.lua b/awesome/dbus.lua index 5a92d62..f9b7cd2 100644 --- a/awesome/dbus.lua +++ b/awesome/dbus.lua @@ -102,52 +102,52 @@ function dbus.process_request(req) return true end -function dbus.iter_args(iter, alltype) - local args = { len = 0 } +function dbus.iter_args(iter, args_dst) + local args = args_dst or {} if not iter then return args end - typ = alltype or iter:get_arg_type() - while true do - if not typ then - args.len = args.len + 1 - args[args.len] = nil - elseif typ == ldbus.types.variant then - local nargs = dbus.iter_args(iter:recurse()) - for i = 1, nargs.len do - args[args.len + i] = nargs[i] - end - args.len = args.len + nargs.len - elseif typ == ldbus.types.dict_entry then - local nargs = dbus.iter_args(iter:recurse()) - local kwargs = {} - for i = 1, nargs.len, 2 do - kwargs[nargs[i]] = nargs[i + 1] + local typ = iter:get_arg_type() + + while typ do + local nextval = {} + if typ == ldbus.types.array then + local arr_typ = iter:get_element_type() + if arr_typ == ldbus.types.dict_entry then + local arr_it = iter:recurse() + while arr_it:get_arg_type() do + local de_it = arr_it:recurse() + --assert (dbus.raw.set_of_basic_types[de_it:get_arg_type()]) + local key = de_it:get_basic() + --assert (de_it:has_next()) --value is mandatory + de_it:next() + local val = dbus.iter_args(de_it) + --assert(#val <= 1) --recursing on one dbus type + nextval[key] = val[1] + --assert (not de_it:has_next()) + arr_it:next() + end + elseif arr_typ then + local arr_it = iter:recurse() + while arr_it:get_arg_type() do + -- more than one type can be returned, direct "nextval" write + dbus.iter_args(arr_it, nextval) + arr_it:next() + end end - args.packed = true - args.len = args.len + 1 - args[args.len] = kwargs + elseif typ == ldbus.types.variant then + local val = dbus.iter_args(iter:recurse()) + --assert(#val <= 1) --recursing on only one dbus type + nextval = val[1] elseif typ == ldbus.types.struct then - local nargs = dbus.iter_args(iter:recurse()) - args.len = args.len + 1 - args[args.len] = nargs - elseif typ == ldbus.types.array then - local nargs = dbus.iter_args(iter:recurse(), iter:get_element_type()) - args.len = args.len + 1 - args[args.len] = nargs - else - args.len = args.len + 1 - args[args.len] = iter:get_basic() - end - if iter:next() then - typ = alltype or iter:get_arg_type() + --struct representation is an array of "n" values + nextval = dbus.iter_args(iter:recurse()) else - break + nextval = iter:get_basic() end + table.insert(args, nextval) + iter:next() + typ = iter:get_arg_type() end - if args.packed then - return unpack(args) - else - return args - end + return args end function dbus.type(value)