251 lines
8.0 KiB
C
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);
|
||
|
}
|