Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
HybridOS
hiDataBus
Commits
5a8e773e
Commit
5a8e773e
authored
Nov 08, 2019
by
Vincent Wei
Browse files
initial commit
parents
Pipeline
#93
canceled with stages
Changes
34
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
CMakeLists.txt
0 → 100644
View file @
5a8e773e
cmake_minimum_required
(
VERSION 2.6
)
PROJECT
(
ubus C
)
ADD_DEFINITIONS
(
-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations
)
OPTION
(
BUILD_LUA
"build Lua plugin"
ON
)
OPTION
(
BUILD_EXAMPLES
"build examples"
ON
)
SET
(
CMAKE_SHARED_LIBRARY_LINK_C_FLAGS
""
)
SET
(
UBUS_UNIX_SOCKET
"/var/run/ubus.sock"
)
SET
(
UBUS_MAX_MSGLEN 1048576
)
ADD_DEFINITIONS
(
-DUBUS_UNIX_SOCKET=
"
${
UBUS_UNIX_SOCKET
}
"
)
ADD_DEFINITIONS
(
-DUBUS_MAX_MSGLEN=
${
UBUS_MAX_MSGLEN
}
)
IF
(
BUILD_STATIC
)
FIND_LIBRARY
(
ubox_library NAMES ubox.a
)
FIND_LIBRARY
(
blob_library NAMES blobmsg_json.a
)
ELSE
(
BUILD_STATIC
)
FIND_LIBRARY
(
ubox_library NAMES ubox
)
FIND_LIBRARY
(
blob_library NAMES blobmsg_json
)
ENDIF
(
BUILD_STATIC
)
FIND_PATH
(
ubox_include_dir libubox/usock.h
)
INCLUDE_DIRECTORIES
(
${
ubox_include_dir
}
)
ADD_LIBRARY
(
ubus SHARED libubus.c libubus-io.c libubus-obj.c libubus-sub.c libubus-req.c libubus-acl.c
)
TARGET_LINK_LIBRARIES
(
ubus
${
ubox_library
}
)
find_library
(
json NAMES json-c json
)
ADD_EXECUTABLE
(
ubusd ubusd.c ubusd_id.c ubusd_obj.c ubusd_proto.c ubusd_event.c ubusd_acl.c ubusd_monitor.c
)
TARGET_LINK_LIBRARIES
(
ubusd
${
ubox_library
}
${
blob_library
}
${
json
}
)
ADD_EXECUTABLE
(
cli cli.c
)
SET_TARGET_PROPERTIES
(
cli PROPERTIES OUTPUT_NAME ubus
)
TARGET_LINK_LIBRARIES
(
cli ubus
${
ubox_library
}
${
blob_library
}
${
json
}
)
ADD_SUBDIRECTORY
(
lua
)
ADD_SUBDIRECTORY
(
examples
)
INSTALL
(
TARGETS ubus cli
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
INSTALL
(
TARGETS ubusd
RUNTIME DESTINATION sbin
)
INSTALL
(
FILES ubusmsg.h ubus_common.h libubus.h DESTINATION include
)
cli.c
0 → 100644
View file @
5a8e773e
/*
* Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <unistd.h>
#include <libubox/blobmsg_json.h>
#include "libubus.h"
static
struct
blob_buf
b
;
static
int
listen_timeout
;
static
int
timeout
=
30
;
static
bool
simple_output
=
false
;
static
int
verbose
=
0
;
static
int
monitor_dir
=
-
1
;
static
uint32_t
monitor_mask
;
static
const
char
*
const
monitor_types
[]
=
{
[
UBUS_MSG_HELLO
]
=
"hello"
,
[
UBUS_MSG_STATUS
]
=
"status"
,
[
UBUS_MSG_DATA
]
=
"data"
,
[
UBUS_MSG_PING
]
=
"ping"
,
[
UBUS_MSG_LOOKUP
]
=
"lookup"
,
[
UBUS_MSG_INVOKE
]
=
"invoke"
,
[
UBUS_MSG_ADD_OBJECT
]
=
"add_object"
,
[
UBUS_MSG_REMOVE_OBJECT
]
=
"remove_object"
,
[
UBUS_MSG_SUBSCRIBE
]
=
"subscribe"
,
[
UBUS_MSG_UNSUBSCRIBE
]
=
"unsubscribe"
,
[
UBUS_MSG_NOTIFY
]
=
"notify"
,
};
static
const
char
*
format_type
(
void
*
priv
,
struct
blob_attr
*
attr
)
{
static
const
char
*
const
attr_types
[]
=
{
[
BLOBMSG_TYPE_INT8
]
=
"
\"
Boolean
\"
"
,
[
BLOBMSG_TYPE_INT32
]
=
"
\"
Integer
\"
"
,
[
BLOBMSG_TYPE_STRING
]
=
"
\"
String
\"
"
,
[
BLOBMSG_TYPE_ARRAY
]
=
"
\"
Array
\"
"
,
[
BLOBMSG_TYPE_TABLE
]
=
"
\"
Table
\"
"
,
};
const
char
*
type
=
NULL
;
int
typeid
;
if
(
blob_id
(
attr
)
!=
BLOBMSG_TYPE_INT32
)
return
NULL
;
typeid
=
blobmsg_get_u32
(
attr
);
if
(
typeid
<
ARRAY_SIZE
(
attr_types
))
type
=
attr_types
[
typeid
];
if
(
!
type
)
type
=
"
\"
(unknown)
\"
"
;
return
type
;
}
static
void
receive_list_result
(
struct
ubus_context
*
ctx
,
struct
ubus_object_data
*
obj
,
void
*
priv
)
{
struct
blob_attr
*
cur
;
char
*
s
;
int
rem
;
if
(
simple_output
||
!
verbose
)
{
printf
(
"%s
\n
"
,
obj
->
path
);
return
;
}
printf
(
"'%s' @%08x
\n
"
,
obj
->
path
,
obj
->
id
);
if
(
!
obj
->
signature
)
return
;
blob_for_each_attr
(
cur
,
obj
->
signature
,
rem
)
{
s
=
blobmsg_format_json_with_cb
(
cur
,
false
,
format_type
,
NULL
,
-
1
);
printf
(
"
\t
%s
\n
"
,
s
);
free
(
s
);
}
}
static
void
receive_call_result_data
(
struct
ubus_request
*
req
,
int
type
,
struct
blob_attr
*
msg
)
{
char
*
str
;
if
(
!
msg
)
return
;
str
=
blobmsg_format_json_indent
(
msg
,
true
,
simple_output
?
-
1
:
0
);
printf
(
"%s
\n
"
,
str
);
free
(
str
);
}
static
void
print_event
(
const
char
*
type
,
struct
blob_attr
*
msg
)
{
char
*
str
;
str
=
blobmsg_format_json
(
msg
,
true
);
printf
(
"{
\"
%s
\"
: %s }
\n
"
,
type
,
str
);
fflush
(
stdout
);
free
(
str
);
}
static
int
receive_request
(
struct
ubus_context
*
ctx
,
struct
ubus_object
*
obj
,
struct
ubus_request_data
*
req
,
const
char
*
method
,
struct
blob_attr
*
msg
)
{
print_event
(
method
,
msg
);
return
0
;
}
static
void
receive_event
(
struct
ubus_context
*
ctx
,
struct
ubus_event_handler
*
ev
,
const
char
*
type
,
struct
blob_attr
*
msg
)
{
print_event
(
type
,
msg
);
}
static
int
ubus_cli_list
(
struct
ubus_context
*
ctx
,
int
argc
,
char
**
argv
)
{
const
char
*
path
=
NULL
;
if
(
argc
>
1
)
return
-
2
;
if
(
argc
==
1
)
path
=
argv
[
0
];
return
ubus_lookup
(
ctx
,
path
,
receive_list_result
,
NULL
);
}
static
int
ubus_cli_call
(
struct
ubus_context
*
ctx
,
int
argc
,
char
**
argv
)
{
uint32_t
id
;
int
ret
;
if
(
argc
<
2
||
argc
>
3
)
return
-
2
;
blob_buf_init
(
&
b
,
0
);
if
(
argc
==
3
&&
!
blobmsg_add_json_from_string
(
&
b
,
argv
[
2
]))
{
if
(
!
simple_output
)
fprintf
(
stderr
,
"Failed to parse message data
\n
"
);
return
-
1
;
}
ret
=
ubus_lookup_id
(
ctx
,
argv
[
0
],
&
id
);
if
(
ret
)
return
ret
;
return
ubus_invoke
(
ctx
,
id
,
argv
[
1
],
b
.
head
,
receive_call_result_data
,
NULL
,
timeout
*
1000
);
}
struct
cli_listen_data
{
struct
uloop_timeout
timeout
;
bool
timed_out
;
};
static
void
ubus_cli_listen_timeout
(
struct
uloop_timeout
*
timeout
)
{
struct
cli_listen_data
*
data
=
container_of
(
timeout
,
struct
cli_listen_data
,
timeout
);
data
->
timed_out
=
true
;
uloop_end
();
}
static
void
do_listen
(
struct
ubus_context
*
ctx
,
struct
cli_listen_data
*
data
)
{
memset
(
data
,
0
,
sizeof
(
*
data
));
data
->
timeout
.
cb
=
ubus_cli_listen_timeout
;
uloop_init
();
ubus_add_uloop
(
ctx
);
if
(
listen_timeout
)
uloop_timeout_set
(
&
data
->
timeout
,
listen_timeout
*
1000
);
uloop_run
();
uloop_done
();
}
static
int
ubus_cli_listen
(
struct
ubus_context
*
ctx
,
int
argc
,
char
**
argv
)
{
struct
ubus_event_handler
ev
=
{
.
cb
=
receive_event
,
};
struct
cli_listen_data
data
;
const
char
*
event
;
int
ret
=
0
;
if
(
argc
>
0
)
{
event
=
argv
[
0
];
}
else
{
event
=
"*"
;
argc
=
1
;
}
do
{
ret
=
ubus_register_event_handler
(
ctx
,
&
ev
,
event
);
if
(
ret
)
break
;
argv
++
;
argc
--
;
if
(
argc
<=
0
)
break
;
event
=
argv
[
0
];
}
while
(
1
);
if
(
ret
)
{
if
(
!
simple_output
)
fprintf
(
stderr
,
"Error while registering for event '%s': %s
\n
"
,
event
,
ubus_strerror
(
ret
));
return
-
1
;
}
do_listen
(
ctx
,
&
data
);
return
0
;
}
static
int
ubus_cli_subscribe
(
struct
ubus_context
*
ctx
,
int
argc
,
char
**
argv
)
{
struct
ubus_subscriber
sub
=
{
.
cb
=
receive_request
,
};
struct
cli_listen_data
data
;
const
char
*
event
;
int
ret
=
0
;
if
(
argc
>
0
)
{
event
=
argv
[
0
];
}
else
{
if
(
!
simple_output
)
fprintf
(
stderr
,
"You need to specify an object to subscribe to
\n
"
);
return
-
1
;
}
ret
=
ubus_register_subscriber
(
ctx
,
&
sub
);
for
(;
!
ret
&&
argc
>
0
;
argc
--
,
argv
++
)
{
uint32_t
id
;
ret
=
ubus_lookup_id
(
ctx
,
argv
[
0
],
&
id
);
if
(
ret
)
break
;
ret
=
ubus_subscribe
(
ctx
,
&
sub
,
id
);
}
if
(
ret
)
{
if
(
!
simple_output
)
fprintf
(
stderr
,
"Error while registering for event '%s': %s
\n
"
,
event
,
ubus_strerror
(
ret
));
return
-
1
;
}
do_listen
(
ctx
,
&
data
);
return
0
;
}
static
int
ubus_cli_send
(
struct
ubus_context
*
ctx
,
int
argc
,
char
**
argv
)
{
if
(
argc
<
1
||
argc
>
2
)
return
-
2
;
blob_buf_init
(
&
b
,
0
);
if
(
argc
==
2
&&
!
blobmsg_add_json_from_string
(
&
b
,
argv
[
1
]))
{
if
(
!
simple_output
)
fprintf
(
stderr
,
"Failed to parse message data
\n
"
);
return
-
1
;
}
return
ubus_send_event
(
ctx
,
argv
[
0
],
b
.
head
);
}
struct
cli_wait_data
{
struct
uloop_timeout
timeout
;
struct
ubus_event_handler
ev
;
char
**
pending
;
int
n_pending
;
};
static
void
wait_check_object
(
struct
cli_wait_data
*
data
,
const
char
*
path
)
{
int
i
;
for
(
i
=
0
;
i
<
data
->
n_pending
;
i
++
)
{
if
(
strcmp
(
path
,
data
->
pending
[
i
])
!=
0
)
continue
;
data
->
n_pending
--
;
if
(
i
==
data
->
n_pending
)
break
;
memmove
(
&
data
->
pending
[
i
],
&
data
->
pending
[
i
+
1
],
(
data
->
n_pending
-
i
)
*
sizeof
(
*
data
->
pending
));
i
--
;
}
if
(
!
data
->
n_pending
)
uloop_end
();
}
static
void
wait_event_cb
(
struct
ubus_context
*
ctx
,
struct
ubus_event_handler
*
ev
,
const
char
*
type
,
struct
blob_attr
*
msg
)
{
static
const
struct
blobmsg_policy
policy
=
{
"path"
,
BLOBMSG_TYPE_STRING
};
struct
cli_wait_data
*
data
=
container_of
(
ev
,
struct
cli_wait_data
,
ev
);
struct
blob_attr
*
attr
;
const
char
*
path
;
if
(
strcmp
(
type
,
"ubus.object.add"
)
!=
0
)
return
;
blobmsg_parse
(
&
policy
,
1
,
&
attr
,
blob_data
(
msg
),
blob_len
(
msg
));
if
(
!
attr
)
return
;
path
=
blobmsg_data
(
attr
);
wait_check_object
(
data
,
path
);
}
static
void
wait_list_cb
(
struct
ubus_context
*
ctx
,
struct
ubus_object_data
*
obj
,
void
*
priv
)
{
struct
cli_wait_data
*
data
=
priv
;
wait_check_object
(
data
,
obj
->
path
);
}
static
void
wait_timeout
(
struct
uloop_timeout
*
timeout
)
{
uloop_end
();
}
static
int
ubus_cli_wait_for
(
struct
ubus_context
*
ctx
,
int
argc
,
char
**
argv
)
{
struct
cli_wait_data
data
=
{
.
timeout
.
cb
=
wait_timeout
,
.
ev
.
cb
=
wait_event_cb
,
.
pending
=
argv
,
.
n_pending
=
argc
,
};
int
ret
;
if
(
argc
<
1
)
return
-
2
;
uloop_init
();
ubus_add_uloop
(
ctx
);
ret
=
ubus_register_event_handler
(
ctx
,
&
data
.
ev
,
"ubus.object.add"
);
if
(
ret
)
return
ret
;
if
(
!
data
.
n_pending
)
return
ret
;
ret
=
ubus_lookup
(
ctx
,
NULL
,
wait_list_cb
,
&
data
);
if
(
ret
)
return
ret
;
if
(
!
data
.
n_pending
)
return
ret
;
uloop_timeout_set
(
&
data
.
timeout
,
timeout
*
1000
);
uloop_run
();
uloop_done
();
if
(
data
.
n_pending
)
return
UBUS_STATUS_TIMEOUT
;
return
ret
;
}
static
const
char
*
ubus_cli_msg_type
(
uint32_t
type
)
{
const
char
*
ret
=
NULL
;
static
char
unk_type
[
16
];
if
(
type
<
ARRAY_SIZE
(
monitor_types
))
ret
=
monitor_types
[
type
];
if
(
!
ret
)
{
snprintf
(
unk_type
,
sizeof
(
unk_type
),
"%d"
,
type
);
ret
=
unk_type
;
}
return
ret
;
}
static
char
*
ubus_cli_get_monitor_data
(
struct
blob_attr
*
data
)
{
static
const
struct
blob_attr_info
policy
[
UBUS_ATTR_MAX
]
=
{
[
UBUS_ATTR_STATUS
]
=
{
.
type
=
BLOB_ATTR_INT32
},
[
UBUS_ATTR_OBJPATH
]
=
{
.
type
=
BLOB_ATTR_STRING
},
[
UBUS_ATTR_OBJID
]
=
{
.
type
=
BLOB_ATTR_INT32
},
[
UBUS_ATTR_METHOD
]
=
{
.
type
=
BLOB_ATTR_STRING
},
[
UBUS_ATTR_OBJTYPE
]
=
{
.
type
=
BLOB_ATTR_INT32
},
[
UBUS_ATTR_SIGNATURE
]
=
{
.
type
=
BLOB_ATTR_NESTED
},
[
UBUS_ATTR_DATA
]
=
{
.
type
=
BLOB_ATTR_NESTED
},
[
UBUS_ATTR_ACTIVE
]
=
{
.
type
=
BLOB_ATTR_INT8
},
[
UBUS_ATTR_NO_REPLY
]
=
{
.
type
=
BLOB_ATTR_INT8
},
[
UBUS_ATTR_USER
]
=
{
.
type
=
BLOB_ATTR_STRING
},
[
UBUS_ATTR_GROUP
]
=
{
.
type
=
BLOB_ATTR_STRING
},
};
static
const
char
*
const
names
[
UBUS_ATTR_MAX
]
=
{
[
UBUS_ATTR_STATUS
]
=
"status"
,
[
UBUS_ATTR_OBJPATH
]
=
"objpath"
,
[
UBUS_ATTR_OBJID
]
=
"objid"
,
[
UBUS_ATTR_METHOD
]
=
"method"
,
[
UBUS_ATTR_OBJTYPE
]
=
"objtype"
,
[
UBUS_ATTR_SIGNATURE
]
=
"signature"
,
[
UBUS_ATTR_DATA
]
=
"data"
,
[
UBUS_ATTR_ACTIVE
]
=
"active"
,
[
UBUS_ATTR_NO_REPLY
]
=
"no_reply"
,
[
UBUS_ATTR_USER
]
=
"user"
,
[
UBUS_ATTR_GROUP
]
=
"group"
,
};
struct
blob_attr
*
tb
[
UBUS_ATTR_MAX
];
int
i
;
blob_buf_init
(
&
b
,
0
);
blob_parse
(
data
,
tb
,
policy
,
UBUS_ATTR_MAX
);
for
(
i
=
0
;
i
<
UBUS_ATTR_MAX
;
i
++
)
{
const
char
*
n
=
names
[
i
];
struct
blob_attr
*
v
=
tb
[
i
];
if
(
!
tb
[
i
]
||
!
n
)
continue
;
switch
(
policy
[
i
].
type
)
{
case
BLOB_ATTR_INT32
:
blobmsg_add_u32
(
&
b
,
n
,
blob_get_int32
(
v
));
break
;
case
BLOB_ATTR_STRING
:
blobmsg_add_string
(
&
b
,
n
,
blob_data
(
v
));
break
;
case
BLOB_ATTR_INT8
:
blobmsg_add_u8
(
&
b
,
n
,
!!
blob_get_int8
(
v
));
break
;
case
BLOB_ATTR_NESTED
:
blobmsg_add_field
(
&
b
,
BLOBMSG_TYPE_TABLE
,
n
,
blobmsg_data
(
v
),
blobmsg_data_len
(
v
));
break
;
}
}
return
blobmsg_format_json
(
b
.
head
,
true
);
}
static
void
ubus_cli_monitor_cb
(
struct
ubus_context
*
ctx
,
uint32_t
seq
,
struct
blob_attr
*
msg
)
{
static
const
struct
blob_attr_info
policy
[
UBUS_MONITOR_MAX
]
=
{
[
UBUS_MONITOR_CLIENT
]
=
{
.
type
=
BLOB_ATTR_INT32
},
[
UBUS_MONITOR_PEER
]
=
{
.
type
=
BLOB_ATTR_INT32
},
[
UBUS_MONITOR_SEND
]
=
{
.
type
=
BLOB_ATTR_INT8
},
[
UBUS_MONITOR_TYPE
]
=
{
.
type
=
BLOB_ATTR_INT32
},
[
UBUS_MONITOR_DATA
]
=
{
.
type
=
BLOB_ATTR_NESTED
},
};
struct
blob_attr
*
tb
[
UBUS_MONITOR_MAX
];
uint32_t
client
,
peer
,
type
;
bool
send
;
char
*
data
;
blob_parse
(
msg
,
tb
,
policy
,
UBUS_MONITOR_MAX
);
if
(
!
tb
[
UBUS_MONITOR_CLIENT
]
||
!
tb
[
UBUS_MONITOR_PEER
]
||
!
tb
[
UBUS_MONITOR_SEND
]
||
!
tb
[
UBUS_MONITOR_TYPE
]
||
!
tb
[
UBUS_MONITOR_DATA
])
{
printf
(
"Invalid monitor msg
\n
"
);
return
;
}
send
=
blob_get_int32
(
tb
[
UBUS_MONITOR_SEND
]);
client
=
blob_get_int32
(
tb
[
UBUS_MONITOR_CLIENT
]);
peer
=
blob_get_int32
(
tb
[
UBUS_MONITOR_PEER
]);
type
=
blob_get_int32
(
tb
[
UBUS_MONITOR_TYPE
]);
if
(
monitor_mask
&&
type
<
32
&&
!
(
monitor_mask
&
(
1
<<
type
)))
return
;
if
(
monitor_dir
>=
0
&&
send
!=
monitor_dir
)
return
;
data
=
ubus_cli_get_monitor_data
(
tb
[
UBUS_MONITOR_DATA
]);
printf
(
"%s %08x #%08x %14s: %s
\n
"
,
send
?
"->"
:
"<-"
,
client
,
peer
,
ubus_cli_msg_type
(
type
),
data
);
free
(
data
);
fflush
(
stdout
);
}
static
int
ubus_cli_monitor
(
struct
ubus_context
*
ctx
,
int
argc
,
char
**
argv
)
{
int
ret
;
uloop_init
();
ubus_add_uloop
(
ctx
);
ctx
->
monitor_cb
=
ubus_cli_monitor_cb
;
ret
=
ubus_monitor_start
(
ctx
);
if
(
ret
)
return
ret
;
uloop_run
();
uloop_done
();
ubus_monitor_stop
(
ctx
);
return
0
;
}
static
int
add_monitor_type
(
const
char
*
type
)
{
int
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
monitor_types
);
i
++
)
{
if
(
!
monitor_types
[
i
]
||
strcmp
(
monitor_types
[
i
],
type
)
!=
0
)
continue
;
monitor_mask
|=
1
<<
i
;
return
0
;
}
return
-
1
;
}
static
int
usage
(
const
char
*
prog
)
{
fprintf
(
stderr
,
"Usage: %s [<options>] <command> [arguments...]
\n
"
"Options:
\n
"
" -s <socket>: Set the unix domain socket to connect to
\n
"
" -t <timeout>: Set the timeout (in seconds) for a command to complete
\n
"
" -S: Use simplified output (for scripts)
\n
"
" -v: More verbose output
\n
"
" -m <type>: (for monitor): include a specific message type
\n
"
" (can be used more than once)
\n
"
" -M <r|t> (for monitor): only capture received or transmitted traffic
\n
"
"
\n
"
"Commands:
\n
"
" - list [<path>] List objects
\n
"
" - call <path> <method> [<message>] Call an object method
\n
"
" - listen [<path>...] Listen for events
\n
"
" - send <type> [<message>] Send an event
\n
"
" - wait_for <object> [<object>...] Wait for multiple objects to appear on ubus
\n
"
" - monitor Monitor ubus traffic
\n
"
"
\n
"
,
prog
);
return
1
;
}
static
struct
{
const
char
*
name
;
int
(
*
cb
)(
struct
ubus_context
*
ctx
,
int
argc
,
char
**
argv
);
}
commands
[]
=
{
{
"list"
,
ubus_cli_list
},
{
"call"
,
ubus_cli_call
},
{
"listen"
,
ubus_cli_listen
},
{
"subscribe"
,
ubus_cli_subscribe
},
{
"send"
,
ubus_cli_send
},
{
"wait_for"
,
ubus_cli_wait_for
},
{
"monitor"
,
ubus_cli_monitor
},
};
int
main
(
int
argc
,
char
**
argv
)
{
const
char
*
progname
,
*
ubus_socket
=
NULL
;
struct
ubus_context
*
ctx
;
char
*
cmd
;