Hi all
I'd like to discuss the issue of JSON. I'm noticing that some API's employ this format (mainly thinking of ubus here). I understand that it may be convenient to emit data in this format. I also understand that this is a decent way to store and transfer complex data structures. At the same time, parsing JSON with the built-in command-line tools on OpenWrt is far from convenient and takes quite a bit of code (if using jshn), and/or requires performance sacrifices (if using jsonfilter), both of which I think in some cases could be avoided.
I'm not familiar enough with most of what's going on in ubus but recently I was dealing with parsing JSON data emitted by the command service dnsmasq info
. I believe this command outputs a subset of what you can get from ubus call service list
. All I actually wanted to get from it was a list of existing dnsmasq instances and some properties of each instance: index, name, conf-dir, running, interfaces. This could be easily provided by a simple API, e.g.
dnsmasq_get_instances INSTANCES
for instance in $INSTANCES; do
dnsmasq_get_instance INDEX RUNNING IFACES CONFDIR "$instance"
<do_actually_useful_stuff>
done
However the data is only available as JSON (and some of it not even included in the data structure but rather has to be pulled from a file).
So I ended up with this:
get_dnsmasq_instances() {
local nonempty='' instance instances instance_index l1_conf_file l1_conf_files conf_dirs conf_dirs_cnt IFS_OLD i s f dir
DNSMASQ_INSTANCES=
DNSMASQ_INSTANCES_CNT=0
reg_action -blue "Checking dnsmasq instances."
. /usr/share/libubox/jshn.sh
json_load "$(/etc/init.d/dnsmasq info)" &&
json_get_keys nonempty &&
[ -n "${nonempty}" ] &&
json_select dnsmasq &&
json_select instances &&
json_get_keys instances || return 1
instance_index=0
for instance in ${instances}
do
json_is_a "${instance}" object || continue # skip if $instance is not object
json_select "${instance}" &&
json_get_var "${instance}_RUNNING" running &&
json_is_a command array &&
json_select command || return 1
add2list DNSMASQ_INSTANCES "${instance}" || return 1
eval "${instance}_INDEX=${instance_index}"
instance_index=$((instance_index+1))
l1_conf_files=
# look for '-C' in values, get next value which is instance's conf file
i=0
while json_is_a $((i+1)) string
do
i=$((i+1))
json_get_var s ${i}
[ "${s}" = '-C' ] || continue
json_get_var l1_conf_file $((i+1)) || return 1
add2list l1_conf_files "${l1_conf_file}" || return 1
done
json_select ..
json_select ..
IFS_OLD="$IFS"
# get ifaces for instance
IFS="${_NL_}"
ifaces="$(
for f in ${l1_conf_files}
do
$SED_CMD -nE '/^\s*interface=/{s/.*=//;p;}' "${f}"
done | $SORT_CMD -u | $SED_CMD -z 's/\n/, /g'
)"
eval "${instance}_IFACES=\"${ifaces%, }\""
# get conf-dirs for instance
conf_dirs="$(
for f in ${l1_conf_files}
do
$SED_CMD -n '/^\s*conf-dir=/{s/.*=//;/[^\s]/p;}' "${f}"
done | $SORT_CMD -u
)"
for dir in ${conf_dirs}
do
add2list ALL_CONF_DIRS "${dir}"
done
IFS="${IFS_OLD}"
eval "${instance}_CONF_DIRS=\"${conf_dirs}\""
cnt_lines conf_dirs_cnt "${conf_dirs}"
eval "${instance}_CONF_DIRS_CNT=\"${conf_dirs_cnt}\""
done
IFS="${IFS_OLD}"
json_cleanup
cnt_lines DNSMASQ_INSTANCES_CNT "${DNSMASQ_INSTANCES}"
:
}
Which leads me to think: why? And also, how many other similar cases of simple API replaced by JSON are there?
So specifically for this case of dnsmasq instances, I've been thinking that perhaps it makes sense to add the simple API to the dnsmasq init script. I could even go ahead and implement that, but I'm not sure if the result has any chance to get merged.
And in general, I'd like to hear what other people think about this.
I started to chat about this with @efahl in another topic but a separate topic is more appropriate for this discussion.