diff --git a/xcpmd/configure.ac b/xcpmd/configure.ac index f495076c..7214b0a9 100644 --- a/xcpmd/configure.ac +++ b/xcpmd/configure.ac @@ -40,6 +40,8 @@ LT_INIT # Check for libraries AC_SEARCH_LIBS([dlopen], [dl]) AC_SEARCH_LIBS([yajl_tree_parse], [yajl]) +AC_SEARCH_LIBS([round], [m]) +AC_SEARCH_LIBS([udev_new], [udev]) # Required modules. PKG_CHECK_MODULES([LIBPCI], [libpci]) diff --git a/xcpmd/src/Makefile.am b/xcpmd/src/Makefile.am index 46452355..975bb09e 100644 --- a/xcpmd/src/Makefile.am +++ b/xcpmd/src/Makefile.am @@ -24,12 +24,14 @@ noinst_HEADERS = \ battery.h \ parser.h \ db-helper.h \ - vm-utils.h + vm-utils.h \ + backlight.h sbin_PROGRAMS = xcpmd xcpmd_SOURCES = \ acpi-events.c \ + backlight.c \ battery.c \ db-helper.c \ modules.c \ @@ -57,7 +59,7 @@ xcpmd_LDFLAGS = -rdynamic # # RPC generated stubs. # -DBUS_CLIENT_IDLS = xenmgr xenmgr_vm db vglass input_daemon +DBUS_CLIENT_IDLS = xenmgr xenmgr_vm xenmgr_host db vglass input_daemon DBUS_SERVER_IDLS = xcpmd BUILT_SOURCES = \ @@ -79,9 +81,11 @@ rpcgen/%_server_marshall.h rpcgen/%_server_obj.h rpcgen/%_server_obj.c: $(IDLDIR # pkglib_LTLIBRARIES = \ acpi-module.la \ + db-events-module.la \ default-actions-module.la \ default-inputs-module.la \ displayhandler-module.la \ + host-power-module.la \ idle-detect-module.la \ vm-actions-module.la \ vm-events-module.la @@ -140,3 +144,21 @@ vm_events_module_la_SOURCES = \ vm-utils.h \ xcpmd.h vm_events_module_la_LDFLAGS = -avoid-version -module -shared + +host_power_module_la_SOURCES = \ + host-power-module.c \ + project.h \ + xcpmd.h \ + rules.h \ + vm-utils.h +host_power_module_la_LDFLAGS = -avoid-version -module -shared + +db_events_module_la_SOURCES = \ + db-events-module.c \ + project.h \ + xcpmd.h \ + modules.h \ + rules.h \ + db-events-module.h \ + db-helper.h +db_events_module_la_LDFLAGS = -avoid-version -module -shared diff --git a/xcpmd/src/acpi-events.c b/xcpmd/src/acpi-events.c index c360b08b..84ea771d 100644 --- a/xcpmd/src/acpi-events.c +++ b/xcpmd/src/acpi-events.c @@ -154,7 +154,7 @@ static void handle_battery_status_event(int battery_index) { */ -static void handle_lid_event(int status) { +void handle_lid_event(int status) { int lid_status; char * lid_status_string; @@ -460,20 +460,7 @@ static void process_acpi_message(char *acpi_buffer, ssize_t len) { return; } - if (!strcmp(subclass, ACPI_BUTTON_SUBCLASS_LID)) { - - if (tokens[2] == NULL) - data = LID_UNKNOWN; - else if (!strcmp(tokens[2], "open")) - data = LID_OPEN; - else if (!strcmp(tokens[2], "close")) - data = LID_CLOSED; - else - data = LID_UNKNOWN; - - handle_lid_event(data); - } - else if (!strcmp(subclass, ACPI_BUTTON_SUBCLASS_POWER)) { + if (!strcmp(subclass, ACPI_BUTTON_SUBCLASS_POWER)) { handle_power_button_event(); } else if (!strcmp(subclass, ACPI_BUTTON_SUBCLASS_SLEEP)) { @@ -630,8 +617,9 @@ int acpi_events_initialize(void) { //notifications before data is ready on a hardware level. If a quirk is added //to the battery driver for these platforms, we can move to an event-driven //model. - event_set(&refresh_battery_event, -1, EV_TIMEOUT | EV_PERSIST, wrapper_refresh_battery_event, NULL); - wrapper_refresh_battery_event(0, 0, NULL); + event_set(&refresh_battery_event, -1, EV_TIMEOUT | EV_PERSIST, wrapper_refresh_battery_event, + acpi_event_table); + wrapper_refresh_battery_event(0, 0, acpi_event_table); //State must be initialized after acpi-module is loaded--call it from main(). @@ -666,4 +654,3 @@ void handle_battery_events(void) { handle_events(status_e); } } - diff --git a/xcpmd/src/acpi-events.h b/xcpmd/src/acpi-events.h index 88ee5bf0..d0945c90 100644 --- a/xcpmd/src/acpi-events.h +++ b/xcpmd/src/acpi-events.h @@ -130,4 +130,6 @@ #define ACPI_VIDEO_SUBCLASS_BRTCYCLE "brightnesscycle" #define ACPI_VIDEO_SUBCLASS_TABLETMODE "tabletmode" +void handle_lid_event(int status); + #endif diff --git a/xcpmd/src/acpi-module.c b/xcpmd/src/acpi-module.c index 49c30764..1427b0f6 100644 --- a/xcpmd/src/acpi-module.c +++ b/xcpmd/src/acpi-module.c @@ -29,6 +29,9 @@ #include "rules.h" #include "acpi-module.h" #include "battery.h" +#include "backlight.h" +#include "vm-utils.h" +#include "acpi-events.h" /** * This module listens for ACPI events from acpid. @@ -53,6 +56,10 @@ bool battery_present (struct ev_wrapper * event, struct arg_node * bool overall_battery_greater_than (struct ev_wrapper * event, struct arg_node * args); bool overall_battery_less_than (struct ev_wrapper * event, struct arg_node * args); bool overall_battery_equal_to (struct ev_wrapper * event, struct arg_node * args); +void set_backlight (struct arg_node * args); +void increase_backlight (struct arg_node * args); +void decrease_backlight (struct arg_node * args); +static DBusHandlerResult lid_event_handler(DBusConnection * connection, DBusMessage * dbus_message, void * user_data); //Private data structures @@ -73,6 +80,13 @@ struct cond_table_row { void (* on_instantiate)(struct condition *); }; +struct action_table_row { + char * name; + void (* func)(struct arg_node *); + char * prototype; + char * pretty_prototype; +}; + //Private data static struct event_data_row event_data[] = { @@ -110,8 +124,15 @@ static struct cond_table_row condition_data[] = { {"whileOverallBattEqualTo" , overall_battery_equal_to , "i" , "int percentage" , EVENT_BATT_STATUS , NULL } }; +static struct action_table_row action_table[] = { + {"setBacklight" , set_backlight , "i" , "int backlight_percent" } , + {"increaseBacklight" , increase_backlight , "i" , "int percent_to_increase"} , + {"decreaseBacklight" , decrease_backlight , "i" , "int percent_to_decrease"} +}; + static unsigned int num_conditions = sizeof(condition_data) / sizeof(condition_data[0]); static unsigned int num_events = sizeof(event_data) / sizeof(event_data[0]); +static unsigned int num_action_types = sizeof(action_table) / sizeof(action_table[0]); //Public data @@ -143,6 +164,15 @@ __attribute__((constructor)) static void init_module() { struct cond_table_row entry = condition_data[i]; add_condition_type(entry.name, entry.func, entry.prototype, entry.pretty_prototype, _acpi_event_table[entry.event_index], entry.on_instantiate); } + + //Add all action_types to the action list + for (i=0; i < num_action_types; ++i) { + add_action_type(action_table[i].name, action_table[i].func, action_table[i].prototype, action_table[i].pretty_prototype); + } + + //initialize backlight module + backlight_init(); + add_dbus_filter("type='signal',interface='com.citrix.xenclient.input',member='lid_state_changed'", lid_event_handler, NULL, NULL); } @@ -150,8 +180,11 @@ __attribute__((constructor)) static void init_module() { //The destructor attribute causes this to run at unload (dlclose()) time. __attribute__((destructor)) static void uninit_module() { + //cleanup backlight module + backlight_destroy(); //Free event table. free(_acpi_event_table); + remove_dbus_filter("type='signal',interface='com.citrix.xenclient.input',member='lid_state_changed'", lid_event_handler, NULL); } @@ -211,6 +244,42 @@ bool lid_closed(struct ev_wrapper * event, struct arg_node * args) { } +/* + * lid_event_handler handles lid_state_changed signals when they are received + * over dbus. The lid_state_changed signal is emitted from vglass after libinput + * detects SW_LID and EV_SW. The signal includes one boolean argument + * indicating whether the lid was opened or closed. The argument is retrieved + * from the dbus signal, and passed along to the handle_lid_event function, + * which will then update xenstore accordingly. + * + * The passed argument will include either a boolean true (1) to indicate + * a closed lid or false (0) to indicate an open lid. These values come from + * LIBINPUT_SWITCH_STATE_ON/OFF and match the enum values of LID_STATE located + * in xcpmd.h. This matters because handle_lid_event checks the value passed to + * it against that enum, and then sets xenstore based on that value. + */ +DBusHandlerResult lid_event_handler(DBusConnection * connection, DBusMessage * dbus_message, void * user_data) { + + DBusError error; + bool lid_status; + DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (dbus_message_is_signal(dbus_message, "com.citrix.xenclient.input", "lid_state_changed")) { + + dbus_error_init(&error); + if (!dbus_message_get_args(dbus_message, &error, DBUS_TYPE_BOOLEAN, &lid_status, DBUS_TYPE_INVALID)) { + xcpmd_log(LOG_ERR, "dbus_message_get_args() failed: %s (%s).\n", error.name, error.message); + } + dbus_error_free(&error); + + handle_lid_event(lid_status); + ret = DBUS_HANDLER_RESULT_HANDLED; + } + + return ret; +} + + bool tablet_mode(struct ev_wrapper * event, struct arg_node * args) { return event->value.i == TABLET_MODE; @@ -270,3 +339,21 @@ bool overall_battery_equal_to(struct ev_wrapper * event, struct arg_node * args) int percentage = get_arg(args, 0)->arg.i; return get_overall_battery_percentage() == percentage; } + +void set_backlight(struct arg_node * args) { + + struct arg_node * node = get_arg(args, 0); + backlight_set(node->arg.i); +} + +void increase_backlight(struct arg_node * args) { + + struct arg_node * node = get_arg(args, 0); + backlight_increase(node->arg.i); +} + +void decrease_backlight(struct arg_node * args) { + + struct arg_node * node = get_arg(args, 0); + backlight_decrease(node->arg.i); +} diff --git a/xcpmd/src/backlight.c b/xcpmd/src/backlight.c new file mode 100644 index 00000000..d9783db7 --- /dev/null +++ b/xcpmd/src/backlight.c @@ -0,0 +1,210 @@ +// +// Backlight +// +// Copyright (C) 2016 - 2019 Assured Information Security, Inc. All rights reserved. +// + +// ============================================================================ +// Includes +// ============================================================================ + +#include // open +#include // open +#include // open +#include // write +#include // round +#include // bool, false +#include // udev_new, + // udev_device_new_from_subsystem_sysname, + // udev_device_get_syspath, + // udev_device_get_sysattr_value +#include // strtol + +#include "backlight.h" +#include "project.h" +#include "xcpmd.h" + +static inline uint32_t minu(uint32_t a, uint32_t b) { + return ab?a:b; +} +static inline float maxf(float a, float b) { + return a>b?a:b; +} + +static uint32_t m_max = 0; +static uint32_t m_level = 0; +static struct udev *m_udev = NULL; +static struct udev_device *m_udev_device = NULL; + +static const char *brightness_str = "brightness"; +static const char *max_brightness_str = "max_brightness"; + +// ============================================================================ +// Backlight Implementation +// ============================================================================ + +static const char * to_string(const value_t v) { + + switch (v) { + case BRIGHTNESS: + return brightness_str; + break; + case MAX_BRIGHTNESS: + return max_brightness_str; + break; + default: + xcpmd_log(LOG_ERR, "brightness to_string() failed. value %u is invalid", v); + break; + } + + // Shouldn't get here + return ""; +} + +static uint32_t backlight_value(const value_t v) { + + uint32_t value; + + if (m_udev_device == NULL) { + return 0; + } + + const char *path = to_string(v); + const char *value_str = udev_device_get_sysattr_value(m_udev_device, path); + + if (value_str == NULL || !strlen(value_str)) { + return 0; + } + + //intel backlight kernel interface only returns 32 bit unsigned integer + //This truncation is harmless + value = strtoul(value_str, NULL, 10); + + return value; +} + +bool backlight_init(void) { + + m_udev=udev_new(); + if (m_udev == NULL) { + return false; + } + + m_udev_device=udev_device_new_from_subsystem_sysname(m_udev, "backlight", "intel_backlight"); + if (m_udev_device == NULL) { + return false; + } + + m_max = backlight_value(MAX_BRIGHTNESS); + m_level = backlight_value(BRIGHTNESS); + + return true; +} + +void backlight_destroy(void) { + + if (m_udev_device != NULL) { + udev_device_unref(m_udev_device); + m_udev = NULL; + } + if (m_udev != NULL) { + udev_unref(m_udev); + m_udev_device = NULL; + } +} + +uint32_t backlight_get(void) { + + if (m_max == 0) { + xcpmd_log(LOG_ERR, "divide by zero error with brightness. Max is 0"); + return 100; + } + + float tmpf = m_level * 100.f / m_max; + + return round(tmpf); +} + +void backlight_set(const uint32_t level) { + + uint32_t i_level, fd; + char levelstr[16]; + char *fullpath; + const char *path = "/brightness"; + + if (m_udev_device == NULL) { + return; + } + + const char *syspath = udev_device_get_syspath(m_udev_device); + if (syspath == NULL || !strlen(syspath)) { + return; + } + + i_level = minu(level, 100u); + i_level = maxu(i_level, 1u); + i_level = round(i_level * m_max / 100.f); + + //max_brightness can be > 100, so allocate enough space here to hold 4+ digit integers + snprintf(levelstr, 16, "%d", i_level); + + fullpath = malloc(strlen(syspath)+strlen(path)+1); + if (fullpath == NULL) { + return; + } + + snprintf(fullpath, strlen(syspath)+strlen(path)+1, "%s%s", syspath, path); + fd = open(fullpath, O_RDWR); + + if (fd <= 0) { + xcpmd_log(LOG_ERR, "Failed to open: %s", fullpath); + return; + } else { + size_t len1 = strlen(levelstr); + size_t len2 = write(fd, levelstr, len1); + if (len1 == len2) { + m_level = level; + } + close(fd); + } + + free(fullpath); +} + +void backlight_increase(const uint32_t step) { + + uint32_t i_step, level; + + if (m_max == 0) { + xcpmd_log(LOG_ERR, "divide by zero error with brightness. Max is 0"); + return; + } + + i_step = minu(step, 100u); + i_step = maxu(i_step, 1u); + level = round(minf((m_level*100.f/m_max)+i_step, 100.f)); + + backlight_set(level); +} + +void backlight_decrease(const uint32_t step) { + + uint32_t i_step, level; + + if (m_max == 0) { + xcpmd_log(LOG_ERR, "divide by zero error with brightness. Max is 0"); + return; + } + + i_step = minu(step, 100u); + i_step = maxu(i_step, 1u); + level = round(maxf((m_level*100.f/m_max)-i_step, 1.f)); + + backlight_set(level); +} diff --git a/xcpmd/src/backlight.h b/xcpmd/src/backlight.h new file mode 100644 index 00000000..9b5a7a0e --- /dev/null +++ b/xcpmd/src/backlight.h @@ -0,0 +1,36 @@ +#ifndef BACKLIGHT_H +#define BACKLIGHT_H + +// +// Backlight +// +// Copyright (C) 2016 - 2019 Assured Information Security, Inc. All rights reserved. +// + +// ============================================================================ +// Includes +// ============================================================================ + +#include // uint32_t +#include // udev, udev_device +#include // bool + +// ============================================================================ +// Backlight Definition +// ============================================================================ + +typedef enum { + BRIGHTNESS, + MAX_BRIGHTNESS +} value_t; + +bool backlight_init(void); +void backlight_destroy(void); + +uint32_t backlight_get(void); +void backlight_set(const uint32_t level); + +void backlight_increase(const uint32_t step); +void backlight_decrease(const uint32_t step); + +#endif // BACKLIGHT_H diff --git a/xcpmd/src/battery.c b/xcpmd/src/battery.c index 1d651d3e..c324f0d2 100644 --- a/xcpmd/src/battery.c +++ b/xcpmd/src/battery.c @@ -29,6 +29,7 @@ #include "xcpmd.h" #include "battery.h" #include "modules.h" +#include "acpi-module.h" #include @@ -356,7 +357,7 @@ static void make_xenstore_battery_dir(unsigned int battery_index) { flag = false; for (i = 0; i < num_entries; ++i) { - if (!strcmp(dir_entries[i], xenstore_path)) { + if ((dir_entries[i] != NULL) && !strcmp(dir_entries[i], xenstore_path)) { flag = true; break; } @@ -1022,9 +1023,15 @@ int battery_slot_exists(unsigned int battery_index) { void wrapper_refresh_battery_event(int fd, short event, void *opaque) { struct timeval tv; + struct ev_wrapper **acpi_event_table = (struct ev_wrapper **)opaque; + struct ev_wrapper *info_e = acpi_event_table[EVENT_BATT_INFO]; + struct ev_wrapper *status_e = acpi_event_table[EVENT_BATT_STATUS]; + memset(&tv, 0, sizeof(tv)); update_batteries(); + handle_events(info_e); + handle_events(status_e); tv.tv_sec = 4; evtimer_add(&refresh_battery_event, &tv); diff --git a/xcpmd/src/db-events-module.c b/xcpmd/src/db-events-module.c new file mode 100644 index 00000000..8866a849 --- /dev/null +++ b/xcpmd/src/db-events-module.c @@ -0,0 +1,229 @@ +/* + * idle-detect-module.c + * + * XCPMD module that provides display power management actions. + * + * Copyright (c) 2015 Assured Information Security, Inc. + * + * Author: + * Jennifer Temkin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "project.h" +#include "xcpmd.h" +#include "rules.h" +#include "modules.h" +#include "vm-utils.h" +#include "db-events-module.h" +#include "db-helper.h" + +//Function prototypes +bool var_was_written(struct ev_wrapper * event, struct arg_node * args); +bool var_int_equals(struct ev_wrapper * event, struct arg_node * args); +bool var_int_less_than(struct ev_wrapper * event, struct arg_node * args); +bool var_int_greater_than(struct ev_wrapper * event, struct arg_node * args); +bool var_float_less_than(struct ev_wrapper * event, struct arg_node * args); +bool var_float_greater_than(struct ev_wrapper * event, struct arg_node * args); +bool var_is_true(struct ev_wrapper * event, struct arg_node * args); +bool var_is_false(struct ev_wrapper * event, struct arg_node * args); +bool var_str_equals(struct ev_wrapper * event, struct arg_node * args); + +//Private data structures +struct event_data_row { + char * name; + bool is_stateless; + enum arg_type value_type; + union arg_u reset_value; + unsigned int index; +}; + +struct cond_table_row { + char * name; + bool (* func)(struct ev_wrapper *, struct arg_node *); + char * prototype; + char * pretty_prototype; + unsigned int event_index; + void (* on_instantiate)(struct condition *); +}; + +struct timer { + struct list_head list; + char * name; +}; + + +//Private data +static struct event_data_row event_data[] = { + { "event_var_written" , IS_STATEFUL , ARG_STR, { .str = "" }, EVENT_VAR_WRITTEN } , + { "event_var_written_edge", IS_STATELESS, ARG_STR, { .str = "" }, EVENT_VAR_WRITTEN_EDGE } +}; + +static struct cond_table_row condition_data[] = { + { "whenVarWritten" , var_was_written , "s" , "string var_name" , EVENT_VAR_WRITTEN_EDGE , NULL } , + { "intEqualTo" , var_int_equals , "i i" , "int a, int b" , EVENT_VAR_WRITTEN , NULL } , + { "intGreaterThan" , var_int_greater_than , "i i" , "int a, int b" , EVENT_VAR_WRITTEN , NULL } , + { "intLessThan" , var_int_less_than , "i i" , "int a, int b" , EVENT_VAR_WRITTEN , NULL } , + { "floatGreaterThan" , var_float_greater_than , "f f" , "float a, float b" , EVENT_VAR_WRITTEN , NULL } , + { "floatLessThan" , var_float_less_than , "f f" , "float a, float b" , EVENT_VAR_WRITTEN , NULL } , + { "isTrue" , var_is_true , "b" , "bool n" , EVENT_VAR_WRITTEN , NULL } , + { "isFalse" , var_is_false , "b" , "bool n" , EVENT_VAR_WRITTEN , NULL } +}; + +static unsigned int num_events = sizeof(event_data) / sizeof(event_data[0]); +static unsigned int num_conditions = sizeof(condition_data) / sizeof(condition_data[0]); + + +//Public data +struct ev_wrapper ** _db_event_table; + + +//Initializes the module. +//The constructor attribute causes this function to run at load (dlopen()) time. +__attribute__((constructor)) static void init_module() { + + unsigned i; + + //Allocate space for event tables. + _db_event_table = (struct ev_wrapper **)malloc(num_events * sizeof(struct ev_wrapper *)); + if (!(_db_event_table)) { + xcpmd_log(LOG_ERR, "Failed to allocate memory\n"); + return; + } + + //Add all events to the event list. + for (i=0; i < num_events; ++i) { + struct event_data_row entry = event_data[i]; + _db_event_table[entry.index] = add_event(entry.name, entry.is_stateless, entry.value_type, entry.reset_value); + } + + //Add all condition_types to the condition_type list. + for (i=0; i < num_conditions; ++i) { + struct cond_table_row entry = condition_data[i]; + add_condition_type(entry.name, entry.func, entry.prototype, entry.pretty_prototype, _db_event_table[entry.event_index], entry.on_instantiate); + } +} + + +//Cleans up after this module. +//The destructor attribute causes this to run at unload (dlclose()) time. +__attribute__((destructor)) static void uninit_module() { + + //Free event tables. + free(_db_event_table); +} + + +//Condition checkers +bool var_was_written(struct ev_wrapper * event, struct arg_node * args) { + + char *var_watched, *var_written; + + var_watched = get_arg(args, 0)->arg.str; + var_written = event->value.str; + + if (var_watched == NULL || var_written == NULL) { + return false; + } + + if (strcmp(var_watched, var_written) == 0) { + return true; + } + else { + return false; + } +} + + +bool var_int_equals(struct ev_wrapper * event, struct arg_node * args) { + + int var_watched, compare_to; + + var_watched = get_arg(args, 0)->arg.i; + compare_to = get_arg(args, 1)->arg.i; + + return (var_watched == compare_to); +} + + +bool var_int_less_than(struct ev_wrapper * event, struct arg_node * args) { + + int var_watched, compare_to; + + var_watched = get_arg(args, 0)->arg.i; + compare_to = get_arg(args, 1)->arg.i; + + return (var_watched < compare_to); +} + + +bool var_int_greater_than(struct ev_wrapper * event, struct arg_node * args) { + + int var_watched, compare_to; + + var_watched = get_arg(args, 0)->arg.i; + compare_to = get_arg(args, 1)->arg.i; + + return (var_watched > compare_to); +} + + +bool var_float_less_than(struct ev_wrapper * event, struct arg_node * args) { + + float var_watched, compare_to; + + var_watched = get_arg(args, 0)->arg.f; + compare_to = get_arg(args, 1)->arg.f; + + return (var_watched < compare_to); +} + + +bool var_float_greater_than(struct ev_wrapper * event, struct arg_node * args) { + + float var_watched, compare_to; + + var_watched = get_arg(args, 0)->arg.f; + compare_to = get_arg(args, 1)->arg.f; + + return (var_watched > compare_to); +} + + +bool var_is_true(struct ev_wrapper * event, struct arg_node * args) { + + return get_arg(args, 0)->arg.b; +} + + +bool var_is_false(struct ev_wrapper * event, struct arg_node * args) { + + return !(get_arg(args, 0)->arg.b); +} + + +bool var_str_equals(struct ev_wrapper * event, struct arg_node * args) { + + char *var_watched, *compare_to; + + var_watched = get_arg(args, 0)->arg.str; + compare_to = get_arg(args, 1)->arg.str; + + if (var_watched == NULL || compare_to == NULL || (strlen(compare_to) != strlen(var_watched))) { + return false; + } + + return (strcmp(var_watched, compare_to) == 0); +} diff --git a/xcpmd/src/db-events-module.h b/xcpmd/src/db-events-module.h new file mode 100644 index 00000000..2c188cfb --- /dev/null +++ b/xcpmd/src/db-events-module.h @@ -0,0 +1,31 @@ +/* +* Copyright (c) 2015 Assured Information Security, Inc. +* +* Author: +* Jennifer Temkin +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __DB_EVENTS_MODULES_H__ +#define __DB_EVENTS_MODULES_H__ + +#define EVENT_VAR_WRITTEN 0 +#define EVENT_VAR_WRITTEN_EDGE 1 + +//Names required for dynamic loading +#define DB_EVENTS_MODULE_SONAME "db-events-module.so" +#define DB_EVENTS "_db_event_table" + +#endif diff --git a/xcpmd/src/db-helper.c b/xcpmd/src/db-helper.c index 6f2e9735..e074323d 100644 --- a/xcpmd/src/db-helper.c +++ b/xcpmd/src/db-helper.c @@ -27,7 +27,9 @@ #include "rpcgen/db_client.h" #include "project.h" #include "xcpmd.h" +#include "modules.h" #include "db-helper.h" +#include "db-events-module.h" /** * This file contains functions for reading from and writing to the DB, both @@ -53,6 +55,8 @@ * variable, db_vars, is set up and torn down in rules.c. */ +static struct ev_wrapper ** db_event_table = NULL; + //Function prototypes static void db_write(char * path, char * value); static void db_inject(char * path, char * json); @@ -213,7 +217,21 @@ bool parse_db_vars(struct parse_data * parse_data) { xcpmd_log(LOG_WARNING, "Couldn't get variables from DB."); return false; } + xcpmd_log(LOG_DEBUG, "Received json: %s", json); + + //There are no vars in the DB. + if (*json == '\0' || !strncmp(json, "null", 4)) { + xcpmd_log(LOG_DEBUG, "DB vars node is empty\n"); + free(json); + return true; + } + yajl = yajl_tree_parse(json, err, sizeof(err)); + if (yajl == NULL) { + xcpmd_log(LOG_WARNING, "Error parsing DB vars: %s", err); + free(json); + return false; + } if (YAJL_IS_OBJECT(yajl)) { @@ -332,11 +350,13 @@ bool parse_db_rules(struct parse_data * data) { continue; } rule_arr = json_rule_to_parseable(rule_name, json_rule); + xcpmd_log(LOG_DEBUG, "JSON to parse: %s", json_rule); free(json_rule); if (rule_arr == NULL) { xcpmd_log(LOG_WARNING, "Error parsing DB rule - rule %d is malformed", i); continue; } + xcpmd_log(LOG_DEBUG, "Parsed rule: %s | %s | %s | %s", rule_arr[0], rule_arr[1], rule_arr[2], rule_arr[3]); name = rule_arr[0]; conditions = rule_arr[1]; actions = rule_arr[2]; @@ -369,6 +389,7 @@ static char ** json_rule_to_parseable(char * name, char * json) { int i, j, num_entries, num_args; char *str = NULL; char err[1024]; + char buf[16]; yajl_val yajl, yconditions, yactions, yundos; yajl_val ycond, yact, yundo; yajl_val yinverted, ytype, yargs, yarg; @@ -479,7 +500,10 @@ static char ** json_rule_to_parseable(char * name, char * json) { num_args = yargs->u.object.len; for (j = 0; j < num_args; ++j) { - yarg = yargs->u.object.values[j]; + //yarg = yargs->u.object.values[j]; + snprintf(buf, 16, "%u", j); + yajl_path[0] = buf; + yarg = yajl_tree_get(yargs, yajl_path, yajl_t_any); if (!YAJL_IS_STRING(yarg)) { xcpmd_log(LOG_WARNING, "Error parsing JSON: empty arg in condition %s in rule %s.\n", YAJL_GET_STRING(ytype), name); @@ -543,7 +567,10 @@ static char ** json_rule_to_parseable(char * name, char * json) { num_args = yargs->u.object.len; for (j = 0; j < num_args; ++j) { - yarg = yargs->u.object.values[j]; + //yarg = yargs->u.object.values[j]; + snprintf(buf, 16, "%u", j); + yajl_path[0] = buf; + yarg = yajl_tree_get(yargs, yajl_path, yajl_t_any); if (!YAJL_IS_STRING(yarg)) { xcpmd_log(LOG_WARNING, "Error parsing JSON: empty arg in action %s in rule %s.\n", YAJL_GET_STRING(ytype), name); @@ -608,7 +635,10 @@ static char ** json_rule_to_parseable(char * name, char * json) { num_args = yargs->u.object.len; for (j = 0; j < num_args; ++j) { - yarg = yargs->u.object.values[j]; + //yarg = yargs->u.object.values[j]; + snprintf(buf, 16, "%u", j); + yajl_path[0] = buf; + yarg = yajl_tree_get(yargs, yajl_path, yajl_t_any); if (!YAJL_IS_STRING(yarg)) { xcpmd_log(LOG_WARNING, "Error parsing JSON: empty arg in undo %s in rule %s.\n", YAJL_GET_STRING(ytype), name); @@ -836,6 +866,13 @@ static char * rule_to_json(struct rule * rule) { struct db_var * add_var(char * name, enum arg_type type, union arg_u value, char ** parse_error) { struct db_var * var = lookup_var(name); + struct rule *rule; + struct ev_wrapper * e; + + //Try to get the event table. + if (db_event_table == NULL) { + db_event_table = get_event_table(DB_EVENTS, MODULE_PATH DB_EVENTS_MODULE_SONAME); + } //A var with this name exists already. Check the type. if (var != NULL) { @@ -864,7 +901,26 @@ struct db_var * add_var(char * name, enum arg_type type, union arg_u value, char } if (var != NULL) { + xcpmd_log(LOG_DEBUG, "Writing db var!"); write_db_var(var->name, var->value.type, var->value.arg); + + if (db_event_table != NULL) { + e = db_event_table[EVENT_VAR_WRITTEN]; + e->value.str = var->name; + handle_events(e); + e->value.str = ""; + + e = db_event_table[EVENT_VAR_WRITTEN_EDGE]; + e->value.str = var->name; + handle_events(e); + e->value.str = ""; + } + + list_for_each_entry(rule, &rules.list, list) { + if (rule_has_var(rule, var->name)) { + refresh_rule(rule); + } + } } return var; diff --git a/xcpmd/src/default.rules b/xcpmd/src/default.rules index fd9faec5..d50063c9 100644 --- a/xcpmd/src/default.rules +++ b/xcpmd/src/default.rules @@ -1,11 +1,22 @@ -# This file contains a default set of rules for XCPMD that will be loaded -# when no rules or vars exist in the DB. Customize for your use case. +# Default set of rules for XCPMD. These are only loaded when no rules or vars +# exist in the DB. # Variable section -#samplevar(10) - -# Section separator +critical_batt(11) +normal_batt(10) +critical_backlight(60) +batt_dim_backlight(70) +ac_dim_backlight(100) +shutdown_on_idle(0) +shutdown_idle(15) = # Rules section -#samplerule | sampleCondition() | sampleAction($sampleVar) | sampleUndo($sampleVar) +battBacklight | whileUsingBatt() whileOverallBattGreaterThan($normal_batt) | setBacklight($batt_dim_backlight) +lidScreenOff | whileLidClosed() | screenOff() +lidScreenOn | whileLidOpen() | screenOn() +acBacklight | whileUsingAc() | setBacklight($ac_dim_backlight) +darShutdown | intEqualTo($shutdown_on_idle 1) whenDarIdleTimeout($shutdown_idle) | logString("DAR idle timeout expired; shutting down...") shutdownHostIdle() +dimScreenCritical | whileUsingBatt() whileOverallBattLessThan($critical_batt) | setBacklight($critical_backlight) +battBacklightChanged | whileUsingBatt() whenVarWritten("batt_dim_backlight") | setBacklight($batt_dim_backlight) +acBacklightChanged | whileUsingAc() whenVarWritten("ac_dim_backlight") | setBacklight($ac_dim_backlight) diff --git a/xcpmd/src/host-power-module.c b/xcpmd/src/host-power-module.c new file mode 100644 index 00000000..66181c62 --- /dev/null +++ b/xcpmd/src/host-power-module.c @@ -0,0 +1,93 @@ +/* + * host-power-module.c + * + * XCPMD module that provides display power management actions. + * + * Copyright (c) 2016 Assured Information Security, Inc. + * + * Author: + * Jennifer Temkin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "project.h" +#include "xcpmd.h" +#include "rules.h" +#include "vm-utils.h" +#include "rpcgen/xenmgr_host_client.h" + +#define XENMGR_HOST_PATH "/host" + +//Function prototypes +void shutdown_host (struct arg_node *); +void shutdown_host_idle (struct arg_node *); +void restart_host (struct arg_node *); +//sleep and hibernate, should they ever be supported, would go in this module + + +//Private data structures +struct action_table_row { + char * name; + void (* func)(struct arg_node *); + char * prototype; + char * pretty_prototype; +}; + + +//Private data +static struct action_table_row action_table[] = { + {"shutdownHost", shutdown_host, "n" , "void"} , + {"shutdownHostIdle", shutdown_host_idle, "n" , "void"} , + {"restartHost" , restart_host , "n" , "void"} +}; + +static unsigned int num_action_types = sizeof(action_table) / sizeof(action_table[0]); + + +//Registers this module's action types. +//The constructor attribute causes this function to run at load (dlopen()) time. +__attribute__ ((constructor)) static void init_module() { + + unsigned int i; + + for (i=0; i < num_action_types; ++i) { + add_action_type(action_table[i].name, action_table[i].func, action_table[i].prototype, action_table[i].pretty_prototype); + } +} + + +//Cleans up after this module. +//The destructor attribute causes this to run at unload (dlclose()) time. +__attribute__ ((destructor)) static void uninit_module() { + + //nothing to do + return; +} + + +//Actions +//Shuts down the host. +void shutdown_host(struct arg_node * args) { + dbus_async_call("com.citrix.xenclient.xenmgr", XENMGR_HOST_PATH, "com.citrix.xenclient.xenmgr.host", com_citrix_xenclient_xenmgr_host_shutdown_async, NULL); +} + +void shutdown_host_idle(struct arg_node * args) { + dbus_async_call("com.citrix.xenclient.xenmgr", XENMGR_HOST_PATH, "com.citrix.xenclient.xenmgr.host", com_citrix_xenclient_xenmgr_host_shutdown_idle_async, NULL); +} + +//Restarts the host. +void restart_host(struct arg_node * args) { + dbus_async_call("com.citrix.xenclient.xenmgr", XENMGR_HOST_PATH, "com.citrix.xenclient.xenmgr.host", com_citrix_xenclient_xenmgr_host_reboot_async, NULL); +} diff --git a/xcpmd/src/modules.c b/xcpmd/src/modules.c index b1acdb7d..1e4dd583 100644 --- a/xcpmd/src/modules.c +++ b/xcpmd/src/modules.c @@ -45,8 +45,10 @@ //Private data static char * _module_list[] = { MODULE_PATH "acpi-module.so", + MODULE_PATH "db-events-module.so", MODULE_PATH "default-actions-module.so", MODULE_PATH "displayhandler-module.so", + MODULE_PATH "host-power-module.so", MODULE_PATH "idle-detect-module.so", MODULE_PATH "vm-actions-module.so", MODULE_PATH "vm-events-module.so", @@ -161,7 +163,7 @@ void handle_events(struct ev_wrapper * event) { list_for_each_entry(node, &(event->listeners.list), list) { condition = node->condition; condition_was_true = condition->is_true; - condition_is_true = condition->type->check(event, &condition->args); + condition_is_true = (condition->type->check(event, &condition->args) != condition->is_inverted); //logical XOR //If this condition has changed, add its rule to a rundown list. if (condition_was_true != condition_is_true) { @@ -315,8 +317,10 @@ void evaluate_policy(void) { //Then perform their actions. list_for_each_entry(rule, &rules.list, list) { if (evaluate_rule(rule) == true) { + if (!rule->is_active) { + do_actions(rule); + } rule->is_active = true; - do_actions(rule); } } } diff --git a/xcpmd/src/prototypes.h b/xcpmd/src/prototypes.h index d4b3eac5..a611007b 100644 --- a/xcpmd/src/prototypes.h +++ b/xcpmd/src/prototypes.h @@ -20,7 +20,6 @@ /* acpi-events.c */ int xcpmd_process_input(int input_value); -void adjust_brightness(int increase, int force); int get_ac_adapter_status(void); int get_lid_status(void); int acpi_events_initialize(void); diff --git a/xcpmd/src/rules.c b/xcpmd/src/rules.c index 5a959477..8e5afb4e 100644 --- a/xcpmd/src/rules.c +++ b/xcpmd/src/rules.c @@ -586,6 +586,25 @@ void delete_rules(void) { } +//Reruns all hooks for this rule. +void refresh_rule(struct rule * rule) { + + struct condition *cond; + + if (rule == NULL) { + xcpmd_log(LOG_DEBUG, "Couldn't refresh null rule\n"); + return; + } + + //Right now, the only hook is condition on_instantiate(). + list_for_each_entry(cond, &(rule->conditions.list), list) { + if (cond->type->on_instantiate != NULL) { + cond->type->on_instantiate(cond); + } + } +} + + //Deletes a condition from an event's list of listeners. void delete_condition_from_listeners(struct condition * condition) { @@ -866,6 +885,39 @@ struct rule * get_rule_tail() { } } +//Checks if a rule has a reference to a particular var. +bool rule_has_var(struct rule * rule, char * var) { + + struct condition * cond; + struct action * act; + struct arg_node * arg; + + list_for_each_entry(cond, &rule->conditions.list, list) { + list_for_each_entry(arg, &cond->args.list, list) { + if (arg->type == ARG_VAR && strcmp(arg->arg.var_name, var) == 0) { + return true; + } + } + } + + list_for_each_entry(act, &rule->actions.list, list) { + list_for_each_entry(arg, &act->args.list, list) { + if (arg->type == ARG_VAR && strcmp(arg->arg.var_name, var) == 0) { + return true; + } + } + } + + list_for_each_entry(act, &rule->undos.list, list) { + list_for_each_entry(arg, &act->args.list, list) { + if (arg->type == ARG_VAR && strcmp(arg->arg.var_name, var) == 0) { + return true; + } + } + } + + return false; +} //Allocates memory! //Convenience function to create a new condition from a type string. diff --git a/xcpmd/src/rules.h b/xcpmd/src/rules.h index f98bc516..2f62d8cd 100644 --- a/xcpmd/src/rules.h +++ b/xcpmd/src/rules.h @@ -240,6 +240,7 @@ void add_undo_to_rule(struct rule * rule, struct action * action); void add_rule(struct rule * rule); void delete_rule(struct rule * rule); void delete_rules(void); +void refresh_rule(struct rule * rule); void delete_condition_from_listeners(struct condition * condition); @@ -258,6 +259,8 @@ struct action_type * lookup_action_type(char * type); struct rule * lookup_rule(char * id); struct rule * get_rule_tail(); +bool rule_has_var(struct rule * rule, char * var); + struct condition * new_condition_from_string(char *); struct action * new_action_from_string(char *);