badvpn/udevmonitor/NCDUdevMonitor.c
2012-09-01 19:23:02 +00:00

251 lines
8.0 KiB
C

/**
* @file NCDUdevMonitor.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 <stddef.h>
#include <base/BLog.h>
#include <misc/find_program.h>
#include <udevmonitor/NCDUdevMonitor.h>
#include <generated/blog_channel_NCDUdevMonitor.h>
#define PARSER_BUF_SIZE 16384
#define PARSER_MAX_PROPERTIES 256
static void report_error (NCDUdevMonitor *o)
{
ASSERT(!o->process_running)
ASSERT(!o->input_running)
DEBUGERROR(&o->d_err, o->handler_error(o->user, (o->process_was_error || o->input_was_error)));
}
static void process_handler_terminated (NCDUdevMonitor *o, int normally, uint8_t normally_exit_status)
{
DebugObject_Access(&o->d_obj);
ASSERT(o->process_running)
BLog(BLOG_INFO, "process terminated");
// set process not running (so we don't try to kill it)
o->process_running = 0;
// remember process error
o->process_was_error = !(normally && normally_exit_status == 0);
if (!o->input_running) {
report_error(o);
return;
}
}
static void process_handler_closed (NCDUdevMonitor *o, int is_error)
{
DebugObject_Access(&o->d_obj);
ASSERT(o->input_running)
if (is_error) {
BLog(BLOG_ERROR, "pipe error");
} else {
BLog(BLOG_INFO, "pipe closed");
}
// disconnect connector
StreamRecvConnector_DisconnectInput(&o->connector);
// set input not running
o->input_running = 0;
// remember input error
o->input_was_error = is_error;
if (!o->process_running) {
report_error(o);
return;
}
}
static void parser_handler (NCDUdevMonitor *o)
{
DebugObject_Access(&o->d_obj);
o->handler_event(o->user);
return;
}
int NCDUdevMonitor_Init (NCDUdevMonitor *o, BReactor *reactor, BProcessManager *manager, int mode, void *user,
NCDUdevMonitor_handler_event handler_event,
NCDUdevMonitor_handler_error handler_error)
{
ASSERT(mode == NCDUDEVMONITOR_MODE_MONITOR_UDEV || mode == NCDUDEVMONITOR_MODE_INFO || mode == NCDUDEVMONITOR_MODE_MONITOR_KERNEL)
// init arguments
o->user = user;
o->handler_event = handler_event;
o->handler_error = handler_error;
// find programs
char *stdbuf_exec = badvpn_find_program("stdbuf");
char *udevadm_exec = badvpn_find_program("udevadm");
if (!stdbuf_exec) {
BLog(BLOG_ERROR, "failed to find stdbuf program");
goto fail0;
}
if (!udevadm_exec) {
BLog(BLOG_ERROR, "failed to find udevadm program");
goto fail0;
}
// construct arguments
const char *argv_monitor_udev[] = {stdbuf_exec, "-o", "L", udevadm_exec, "monitor", "--udev", "--environment", NULL};
const char *argv_monitor_kernel[] = {stdbuf_exec, "-o", "L", udevadm_exec, "monitor", "--kernel", "--environment", NULL};
const char *argv_info[] = {stdbuf_exec, "-o", "L", udevadm_exec, "info", "--query", "all", "--export-db", NULL};
// choose arguments based on mode
const char **argv = NULL; // to remove warning
switch (mode) {
case NCDUDEVMONITOR_MODE_MONITOR_UDEV: argv = argv_monitor_udev; break;
case NCDUDEVMONITOR_MODE_INFO: argv = argv_info; break;
case NCDUDEVMONITOR_MODE_MONITOR_KERNEL: argv = argv_monitor_kernel; break;
default: ASSERT(0);
}
// init process
if (!BInputProcess_Init(&o->process, reactor, manager, o,
(BInputProcess_handler_terminated)process_handler_terminated,
(BInputProcess_handler_closed)process_handler_closed
)) {
BLog(BLOG_ERROR, "BInputProcess_Init failed");
goto fail0;
}
// init connector
StreamRecvConnector_Init(&o->connector, BReactor_PendingGroup(reactor));
StreamRecvConnector_ConnectInput(&o->connector, BInputProcess_GetInput(&o->process));
// init parser
if (!NCDUdevMonitorParser_Init(&o->parser, StreamRecvConnector_GetOutput(&o->connector), PARSER_BUF_SIZE, PARSER_MAX_PROPERTIES,
(mode == NCDUDEVMONITOR_MODE_INFO), BReactor_PendingGroup(reactor), o,
(NCDUdevMonitorParser_handler)parser_handler
)) {
BLog(BLOG_ERROR, "NCDUdevMonitorParser_Init failed");
goto fail1;
}
// start process
if (!BInputProcess_Start(&o->process, stdbuf_exec, (char **)argv, NULL)) {
BLog(BLOG_ERROR, "BInputProcess_Start failed");
goto fail2;
}
// set process running, input running
o->process_running = 1;
o->input_running = 1;
free(udevadm_exec);
free(stdbuf_exec);
DebugError_Init(&o->d_err, BReactor_PendingGroup(reactor));
DebugObject_Init(&o->d_obj);
return 1;
fail2:
NCDUdevMonitorParser_Free(&o->parser);
fail1:
StreamRecvConnector_Free(&o->connector);
BInputProcess_Free(&o->process);
fail0:
free(udevadm_exec);
free(stdbuf_exec);
return 0;
}
void NCDUdevMonitor_Free (NCDUdevMonitor *o)
{
DebugObject_Free(&o->d_obj);
DebugError_Free(&o->d_err);
// free parser
NCDUdevMonitorParser_Free(&o->parser);
// free connector
StreamRecvConnector_Free(&o->connector);
// kill process it it's running
if (o->process_running) {
BInputProcess_Kill(&o->process);
}
// free process
BInputProcess_Free(&o->process);
}
void NCDUdevMonitor_Done (NCDUdevMonitor *o)
{
DebugObject_Access(&o->d_obj);
DebugError_AssertNoError(&o->d_err);
NCDUdevMonitorParser_AssertReady(&o->parser);
NCDUdevMonitorParser_Done(&o->parser);
}
int NCDUdevMonitor_IsReadyEvent (NCDUdevMonitor *o)
{
DebugObject_Access(&o->d_obj);
DebugError_AssertNoError(&o->d_err);
NCDUdevMonitorParser_AssertReady(&o->parser);
return NCDUdevMonitorParser_IsReadyEvent(&o->parser);
}
void NCDUdevMonitor_AssertReady (NCDUdevMonitor *o)
{
DebugObject_Access(&o->d_obj);
DebugError_AssertNoError(&o->d_err);
NCDUdevMonitorParser_AssertReady(&o->parser);
}
int NCDUdevMonitor_GetNumProperties (NCDUdevMonitor *o)
{
DebugObject_Access(&o->d_obj);
DebugError_AssertNoError(&o->d_err);
NCDUdevMonitorParser_AssertReady(&o->parser);
return NCDUdevMonitorParser_GetNumProperties(&o->parser);
}
void NCDUdevMonitor_GetProperty (NCDUdevMonitor *o, int index, const char **name, const char **value)
{
DebugObject_Access(&o->d_obj);
DebugError_AssertNoError(&o->d_err);
NCDUdevMonitorParser_AssertReady(&o->parser);
NCDUdevMonitorParser_GetProperty(&o->parser, index, name, value);
}