359 lines
9.8 KiB
C
359 lines
9.8 KiB
C
|
/**
|
||
|
* @file NCDUdevMonitorParser.c
|
||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||
|
*
|
||
|
* @section LICENSE
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions are met:
|
||
|
* 1. Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer in the
|
||
|
* documentation and/or other materials provided with the distribution.
|
||
|
* 3. Neither the name of the author nor the
|
||
|
* names of its contributors may be used to endorse or promote products
|
||
|
* derived from this software without specific prior written permission.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <misc/string_begins_with.h>
|
||
|
#include <misc/balloc.h>
|
||
|
#include <base/BLog.h>
|
||
|
|
||
|
#include <udevmonitor/NCDUdevMonitorParser.h>
|
||
|
|
||
|
#include <generated/blog_channel_NCDUdevMonitorParser.h>
|
||
|
|
||
|
#define PROPERTY_REGEX "^([^=]+)=(.*)$"
|
||
|
|
||
|
static uint8_t * find_end (uint8_t *buf, size_t len)
|
||
|
{
|
||
|
while (len >= 2) {
|
||
|
if (buf[0] == '\n' && buf[1] == '\n') {
|
||
|
return (buf + 2);
|
||
|
}
|
||
|
buf++;
|
||
|
len--;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static int parse_property (NCDUdevMonitorParser *o, char *data)
|
||
|
{
|
||
|
ASSERT(o->ready_num_properties >= 0)
|
||
|
ASSERT(o->ready_num_properties <= o->max_properties)
|
||
|
|
||
|
if (o->ready_num_properties == o->max_properties) {
|
||
|
BLog(BLOG_ERROR, "too many properties");
|
||
|
return 0;
|
||
|
}
|
||
|
struct NCDUdevMonitorParser_property *prop = &o->ready_properties[o->ready_num_properties];
|
||
|
|
||
|
// execute property regex
|
||
|
regmatch_t matches[3];
|
||
|
if (regexec(&o->property_regex, data, 3, matches, 0) != 0) {
|
||
|
BLog(BLOG_ERROR, "failed to parse property");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// extract components
|
||
|
prop->name = data + matches[1].rm_so;
|
||
|
*(data + matches[1].rm_eo) = '\0';
|
||
|
prop->value = data + matches[2].rm_so;
|
||
|
*(data + matches[2].rm_eo) = '\0';
|
||
|
|
||
|
// register property
|
||
|
o->ready_num_properties++;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int parse_message (NCDUdevMonitorParser *o)
|
||
|
{
|
||
|
ASSERT(!o->is_ready)
|
||
|
ASSERT(o->ready_len >= 2)
|
||
|
ASSERT(o->buf[o->ready_len - 2] == '\n')
|
||
|
ASSERT(o->buf[o->ready_len - 1] == '\n')
|
||
|
|
||
|
// zero terminate message (replacing the second newline)
|
||
|
o->buf[o->ready_len - 1] = '\0';
|
||
|
|
||
|
// start parsing
|
||
|
char *line = (char *)o->buf;
|
||
|
int first_line = 1;
|
||
|
|
||
|
// set is not ready event
|
||
|
o->ready_is_ready_event = 0;
|
||
|
|
||
|
// init properties
|
||
|
o->ready_num_properties = 0;
|
||
|
|
||
|
do {
|
||
|
// find end of line
|
||
|
char *line_end = strchr(line, '\n');
|
||
|
ASSERT(line_end)
|
||
|
|
||
|
// zero terminate line
|
||
|
*line_end = '\0';
|
||
|
|
||
|
if (o->is_info_mode) {
|
||
|
// ignore W: entries with missing space
|
||
|
if (string_begins_with(line, "W:")) {
|
||
|
goto nextline;
|
||
|
}
|
||
|
|
||
|
// parse prefix
|
||
|
if (strlen(line) < 3 || line[1] != ':' || line[2] != ' ') {
|
||
|
BLog(BLOG_ERROR, "failed to parse head");
|
||
|
return 0;
|
||
|
}
|
||
|
char line_type = line[0];
|
||
|
char *line_value = line + 3;
|
||
|
|
||
|
if (first_line) {
|
||
|
if (line_type != 'P') {
|
||
|
BLog(BLOG_ERROR, "wrong first line type");
|
||
|
return 0;
|
||
|
}
|
||
|
} else {
|
||
|
if (line_type == 'E') {
|
||
|
if (!parse_property(o, line_value)) {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
if (first_line) {
|
||
|
// is this the initial informational message?
|
||
|
if (string_begins_with(line, "monitor")) {
|
||
|
o->ready_is_ready_event = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// check first line
|
||
|
if (!string_begins_with(line, "UDEV ") && !string_begins_with(line, "KERNEL")) {
|
||
|
BLog(BLOG_ERROR, "failed to parse head");
|
||
|
return 0;
|
||
|
}
|
||
|
} else {
|
||
|
if (!parse_property(o, line)) {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
nextline:
|
||
|
|
||
|
first_line = 0;
|
||
|
line = line_end + 1;
|
||
|
} while (*line);
|
||
|
|
||
|
// set ready
|
||
|
o->is_ready = 1;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static void process_data (NCDUdevMonitorParser *o)
|
||
|
{
|
||
|
ASSERT(!o->is_ready)
|
||
|
|
||
|
while (1) {
|
||
|
// look for end of event
|
||
|
uint8_t *c = find_end(o->buf, o->buf_used);
|
||
|
if (!c) {
|
||
|
// check for out of buffer condition
|
||
|
if (o->buf_used == o->buf_size) {
|
||
|
BLog(BLOG_ERROR, "out of buffer");
|
||
|
o->buf_used = 0;
|
||
|
}
|
||
|
|
||
|
// receive more data
|
||
|
StreamRecvInterface_Receiver_Recv(o->input, o->buf + o->buf_used, o->buf_size - o->buf_used);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// remember message length
|
||
|
o->ready_len = c - o->buf;
|
||
|
|
||
|
// parse message
|
||
|
if (parse_message(o)) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// shift buffer
|
||
|
memmove(o->buf, o->buf + o->ready_len, o->buf_used - o->ready_len);
|
||
|
o->buf_used -= o->ready_len;
|
||
|
}
|
||
|
|
||
|
// call handler
|
||
|
o->handler(o->user);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static void input_handler_done (NCDUdevMonitorParser *o, int data_len)
|
||
|
{
|
||
|
DebugObject_Access(&o->d_obj);
|
||
|
ASSERT(!o->is_ready)
|
||
|
ASSERT(data_len > 0)
|
||
|
ASSERT(data_len <= o->buf_size - o->buf_used)
|
||
|
|
||
|
// increment buffer position
|
||
|
o->buf_used += data_len;
|
||
|
|
||
|
// process data
|
||
|
process_data(o);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static void done_job_handler (NCDUdevMonitorParser *o)
|
||
|
{
|
||
|
DebugObject_Access(&o->d_obj);
|
||
|
ASSERT(o->is_ready)
|
||
|
|
||
|
// shift buffer
|
||
|
memmove(o->buf, o->buf + o->ready_len, o->buf_used - o->ready_len);
|
||
|
o->buf_used -= o->ready_len;
|
||
|
|
||
|
// set not ready
|
||
|
o->is_ready = 0;
|
||
|
|
||
|
// process data
|
||
|
process_data(o);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int NCDUdevMonitorParser_Init (NCDUdevMonitorParser *o, StreamRecvInterface *input, int buf_size, int max_properties,
|
||
|
int is_info_mode, BPendingGroup *pg, void *user,
|
||
|
NCDUdevMonitorParser_handler handler)
|
||
|
{
|
||
|
ASSERT(buf_size > 0)
|
||
|
ASSERT(max_properties >= 0)
|
||
|
ASSERT(is_info_mode == 0 || is_info_mode == 1)
|
||
|
|
||
|
// init arguments
|
||
|
o->input = input;
|
||
|
o->buf_size = buf_size;
|
||
|
o->max_properties = max_properties;
|
||
|
o->is_info_mode = is_info_mode;
|
||
|
o->user = user;
|
||
|
o->handler = handler;
|
||
|
|
||
|
// init input
|
||
|
StreamRecvInterface_Receiver_Init(o->input, (StreamRecvInterface_handler_done)input_handler_done, o);
|
||
|
|
||
|
// init property regex
|
||
|
if (regcomp(&o->property_regex, PROPERTY_REGEX, REG_EXTENDED) != 0) {
|
||
|
BLog(BLOG_ERROR, "regcomp failed");
|
||
|
goto fail1;
|
||
|
}
|
||
|
|
||
|
// init done job
|
||
|
BPending_Init(&o->done_job, pg, (BPending_handler)done_job_handler, o);
|
||
|
|
||
|
// allocate buffer
|
||
|
if (!(o->buf = malloc(o->buf_size))) {
|
||
|
BLog(BLOG_ERROR, "malloc failed");
|
||
|
goto fail2;
|
||
|
}
|
||
|
|
||
|
// set buffer position
|
||
|
o->buf_used = 0;
|
||
|
|
||
|
// set not ready
|
||
|
o->is_ready = 0;
|
||
|
|
||
|
// allocate properties
|
||
|
if (!(o->ready_properties = BAllocArray(o->max_properties, sizeof(o->ready_properties[0])))) {
|
||
|
BLog(BLOG_ERROR, "BAllocArray failed");
|
||
|
goto fail3;
|
||
|
}
|
||
|
|
||
|
// start receiving
|
||
|
StreamRecvInterface_Receiver_Recv(o->input, o->buf, o->buf_size);
|
||
|
|
||
|
DebugObject_Init(&o->d_obj);
|
||
|
return 1;
|
||
|
|
||
|
fail3:
|
||
|
free(o->buf);
|
||
|
fail2:
|
||
|
BPending_Free(&o->done_job);
|
||
|
regfree(&o->property_regex);
|
||
|
fail1:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void NCDUdevMonitorParser_Free (NCDUdevMonitorParser *o)
|
||
|
{
|
||
|
DebugObject_Free(&o->d_obj);
|
||
|
|
||
|
// free properties
|
||
|
BFree(o->ready_properties);
|
||
|
|
||
|
// free buffer
|
||
|
free(o->buf);
|
||
|
|
||
|
// free done job
|
||
|
BPending_Free(&o->done_job);
|
||
|
|
||
|
// free property regex
|
||
|
regfree(&o->property_regex);
|
||
|
}
|
||
|
|
||
|
void NCDUdevMonitorParser_AssertReady (NCDUdevMonitorParser *o)
|
||
|
{
|
||
|
DebugObject_Access(&o->d_obj);
|
||
|
ASSERT(o->is_ready)
|
||
|
}
|
||
|
|
||
|
void NCDUdevMonitorParser_Done (NCDUdevMonitorParser *o)
|
||
|
{
|
||
|
DebugObject_Access(&o->d_obj);
|
||
|
ASSERT(o->is_ready)
|
||
|
|
||
|
// schedule done job
|
||
|
BPending_Set(&o->done_job);
|
||
|
}
|
||
|
|
||
|
int NCDUdevMonitorParser_IsReadyEvent (NCDUdevMonitorParser *o)
|
||
|
{
|
||
|
DebugObject_Access(&o->d_obj);
|
||
|
ASSERT(o->is_ready)
|
||
|
|
||
|
return o->ready_is_ready_event;
|
||
|
}
|
||
|
|
||
|
int NCDUdevMonitorParser_GetNumProperties (NCDUdevMonitorParser *o)
|
||
|
{
|
||
|
DebugObject_Access(&o->d_obj);
|
||
|
ASSERT(o->is_ready)
|
||
|
|
||
|
return o->ready_num_properties;
|
||
|
}
|
||
|
|
||
|
void NCDUdevMonitorParser_GetProperty (NCDUdevMonitorParser *o, int index, const char **name, const char **value)
|
||
|
{
|
||
|
DebugObject_Access(&o->d_obj);
|
||
|
ASSERT(o->is_ready)
|
||
|
ASSERT(index >= 0)
|
||
|
ASSERT(index < o->ready_num_properties)
|
||
|
|
||
|
*name = o->ready_properties[index].name;
|
||
|
*value = o->ready_properties[index].value;
|
||
|
}
|