OpenWRT system relies on these projects to bind and integrate userspace applications with centralized configuration. So, basically OpenWRT = GNU/Linux + network apps + OpenWRT libraries (U- modules)
- U-BOX
- U-BUS
- UCI
- J-UCI
The foundation is the U-BOX framework. U-BUS depends on it. While UCI depends on U-BUS, and J-UCI depends on UCI.
The dependency:
J-UCI
|
|
+---> UCI
|
|
+---> UBUS
|
|
+---> UBOX
UBOX
UBox (Micro Box) is a collection of utilities intensively used by OpenWrt project. It provides facilities, such as polling, event handling, socket helper functions, logging, MD5 hashing, generating/parsing tagged binary data, AVL binary-tree balancing, JSON handling, task queueing/completion tracking, and others. The RPC module for inter-process communication is called ubus (http://git.openwrt.org/project/ubus.git).
Modules:
- ULoop
- UBus
GIT repos:
http://git.openwrt.org/project/ubox.git |
https://git.openwrt.org/project/ubox.git |
git://git.openwrt.org/project/ubox.git |
ULoop
Most used functions in this subsystem are:
int uloop_init(void); void uloop_run(void); void uloop_done(void);
Routine
|
Description
|
---|---|
int uloop_init(void) | Must be called before we can use uloop_run() |
void uloop_run(void) |
This routine does the main loop:
The routine runs continuously until we set the global variable uloop_cancelled to true.
|
void uloop_done(void) | Finally, we can this routine before quit to cleanup uloop's internal spaces used before. |
Compilation
Basically, we just need to tell GCC the path to libubox.so and the headers needed.
For example:
One-shot Timer Task
A one-time scheduled task is added via uloop_timout_add().
Routine Name
|
Description
|
---|---|
int uloop_timeout_add(struct uloop_timeout *timeout) | Add our timeout task into queue |
int uloop_timeout_cancel(struct uloop_timeout *timeout) | Delete our timeout task from queue |
int uloop_timeout_set(struct uloop_timeout *timeout, int msec) | Set milliseconds from now (e.g, to be executed later after mSecs elapsed) |
int uloop_timeout_remaining(struct uloop_timeout *timeout) | How many mSecs remained for the task to be executed |
int uloop_clear_timeouts(void) | Empty the one-shot queue completely |
To add an one-time shot event timer, we call uloop_timeout_set(struct uloop_timeout uto*, msecs) (as defined in libubox). In uloop_timeout, we set a member variable cb for callback, for example:
Another example:
To make a periodic task, in the callback we re-add our task:
The following example shows how to create a periodic task but only executed for limited time, then the uloop is finished:
External Processes
ULoop supports to queue up external processes to be executed in later time. The execution is one-time (once the process has been executed, it is not repeated unless we re-enter the entry into process queue).
To queue a process to be executed by uloop_run above, we need to call uloop_process_add() as shows below. Subsequent calls to uloop_process_add() will queue the processes and would be executed FIFO way. Once the process/task has been called, it is removed from the queue.
Routine Name
|
Description
|
---|---|
int uloop_process_add(struct uloop_process *p) | Insert our task into process queue |
int uloop_process_delete(struct uloop_process *p) | Delete our task from process queue |
Example:
uloop_init() is used to do main loop. A typical construct is as follow:
Client version (when client_main() routine above is executed):
All routines prepended with "ubus_" provided by ubus library.
To prepare ubus for RPC communication, first we create a context by calling: ctx = ubus_connect(path), where path is the path to pipe (e.g: "/tmp/compipe"). Once we have the context, we call ubus_add_uloop() to let uloop to handle ubus-related default events.
To prepare ubus for RPC communication, first we create a context by calling: ctx = ubus_connect(path), where path is the path to pipe (e.g: "/tmp/compipe"). Once we have the context, we call ubus_add_uloop() to let uloop to handle ubus-related default events.
Task Queue
As described in libubox/runqueue.h:
Normally, we first call runqueue_init first. Later we can add a task or process to the queue (which will be executed by uloop_run). To add a task, we call runqueue_task_add() and to add a process to the queue is runqueue_process_add(). The difference between task and process in this context is that process is an external job/program to be executed (similar to at or cron), while task is simply a routine to be called later.
For Example:
(&my_queue)
BLOB (Binary Large OBject)
BLOB submodule is used for message formatting, packaging and handling. There are several supported datatypes, and it creates blobs that could be sent over any socket, as endianess is taken care in the library itself.
The method is to create a type-value chained data, supporting nested data. This part basically covers putting and getting datatypes. Further functionality is implemented in blobmsg.h.
Blobmsg sits on top of blob.h, providing tables and arrays, providing it's own but parallel datatypes.
Some important routines:
Example:
BLOBMSG
Function Name
|
Description
|
---|---|
int blobmsg_hdrlen(unsigned int namelen)
| |
void blobmsg_clear_name(struct blob_attr *attr)
| |
const char *blobmsg_name(const struct blob_attr *attr)
| |
int blobmsg_type(const struct blob_attr *attr)
| |
void *blobmsg_data(const struct blob_attr *attr)
| |
int blobmsg_data_len(const struct blob_attr *attr)
| |
bool blobmsg_check_attr(const struct blob_attr *attr, bool name)
| |
bool blobmsg_check_attr_list(const struct blob_attr *attr, int type)
| |
int blobmsg_check_array(const struct blob_attr *attr, int type) | |
int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len,
struct blob_attr **tb, void *data, unsigned int len) | |
int blobmsg_parse_array(const struct blobmsg_policy *policy, int policy_len,
struct blob_attr **tb, void *data, unsigned int len) | |
int blobmsg_add_field(struct blob_buf *buf, int type, const char *name,
const void *data, unsigned int len) | |
int blobmsg_add_u8(struct blob_buf *buf, const char *name, uint8_t val)
| |
int blobmsg_add_u16(struct blob_buf *buf, const char *name, uint16_t val)
| |
int blobmsg_add_u32(struct blob_buf *buf, const char *name, uint32_t val)
| |
int blobmsg_add_u64(struct blob_buf *buf, const char *name, uint64_t val)
| |
int blobmsg_add_string(struct blob_buf *buf, const char *name, const char *string)
| |
int blobmsg_add_blob(struct blob_buf *buf, struct blob_attr *attr)
| |
void *blobmsg_open_nested(struct blob_buf *buf, const char *name, bool array);
| |
void *blobmsg_open_array(struct blob_buf *buf, const char *name)
| |
void *blobmsg_open_table(struct blob_buf *buf, const char *name)
| |
void
blobmsg_close_array(struct blob_buf *buf, void *cookie) | |
void
blobmsg_close_table(struct blob_buf *buf, void *cookie) | |
int blobmsg_buf_init(struct blob_buf *buf)
| |
uint8_t blobmsg_get_u8(struct blob_attr *attr)
| |
ULOG
The APIs provided are:
(...to be continued...)
A good summary of uloop, ubus and blob. Thanks a lot.
ReplyDeleteHi,
ReplyDeleteThanks for the detailed explanations.
I am trying to run multiple periodic tasks within a same uloop_run().
But somehow its not working.
If possible could you add the example as above.