From f57572b678f20d269cd1ea474a42c2eb4c727221 Mon Sep 17 00:00:00 2001 From: "Carsten Haitzler (Rasterman)" Date: Sun, 12 Jan 2020 21:38:54 +0000 Subject: [PATCH] e system - add new uber setuid tool to replace all the previous ones so e has had a bit of a mess of setuid root tools to do things that are essential to maintaing a functioning desktop/laptop/tablet/phone like device like shutting it down or suspending or sqizzling the cpu governor or messing with the backlight or... you get the idea. this has been spread around enlightenment_sys and other special purpose tools. this now unifies it into a single "always there" enlightenment_system backend setuid root slave process whose job it is to do all these things via a stdio protocol in an organized way. this means latency to do something is lower, but at the expense of consuming ram and a lurking process. unfortunately the lurking will be needed soon when i add ddc support to make it even vaguely efficient, so it's a cost i guess we have to pay now. we'll need this in future as well for some stats collection and more. still need to port existing code to use this instead of the existing stuff, and then remove of the old stuff. --- data/etc/meson.build | 3 + data/etc/system.conf | 67 ++++ meson.build | 7 + src/bin/e_includes.h | 1 + src/bin/e_main.c | 9 + src/bin/e_system.c | 320 +++++++++++++++++++ src/bin/e_system.h | 15 + src/bin/meson.build | 3 + src/bin/system/e_system.h | 121 ++++++++ src/bin/system/e_system_backlight.c | 321 ++++++++++++++++++++ src/bin/system/e_system_cpufreq.c | 163 ++++++++++ src/bin/system/e_system_inout.c | 171 +++++++++++ src/bin/system/e_system_l2ping.c | 229 ++++++++++++++ src/bin/system/e_system_main.c | 303 ++++++++++++++++++ src/bin/system/e_system_power.c | 134 ++++++++ src/bin/system/e_system_rfkill.c | 176 +++++++++++ src/bin/system/e_system_storage.c | 455 ++++++++++++++++++++++++++++ src/bin/system/e_system_test.c | 146 +++++++++ src/bin/system/meson.build | 29 ++ 19 files changed, 2673 insertions(+) create mode 100644 data/etc/system.conf create mode 100644 src/bin/e_system.c create mode 100644 src/bin/e_system.h create mode 100644 src/bin/system/e_system.h create mode 100644 src/bin/system/e_system_backlight.c create mode 100644 src/bin/system/e_system_cpufreq.c create mode 100644 src/bin/system/e_system_inout.c create mode 100644 src/bin/system/e_system_l2ping.c create mode 100644 src/bin/system/e_system_main.c create mode 100644 src/bin/system/e_system_power.c create mode 100644 src/bin/system/e_system_rfkill.c create mode 100644 src/bin/system/e_system_storage.c create mode 100644 src/bin/system/e_system_test.c create mode 100644 src/bin/system/meson.build diff --git a/data/etc/meson.build b/data/etc/meson.build index 5c0cc7f32..46ba187a3 100644 --- a/data/etc/meson.build +++ b/data/etc/meson.build @@ -86,3 +86,6 @@ if get_option('install-enlightenment-menu') ) endif +install_data('system.conf', + install_dir: join_paths(dir_sysconf, 'enlightenment') + ) diff --git a/data/etc/system.conf b/data/etc/system.conf new file mode 100644 index 000000000..6f1355537 --- /dev/null +++ b/data/etc/system.conf @@ -0,0 +1,67 @@ +# Enlightenment System access control file +# +# This should be installed as /etc/enlightenment/system.conf if you wish to +# limit access to enlightenment_system setuid tool. The tool will load this +# file, if it exists, and abort any kind of execution if the file would not +# permit the calling user to use it. If this file does not exist, then any +# user or group will be permitted to run this tool and access its features. +# This file will be installed + +# This file is read in order from top to bottom - the first rule to MATCH +# will be used for a user or a group, and nothing after that is read. + +# Any user or group NOT matched by an allow or a deny will be ALLOWED to +# perform the action by default (system administrators should be aware of +# this and implement whatever policies they see fit). Generally speaking +# a user of a workstation, desktop or laptop is intended to have such abilities +# to perform these actions, thus the default of allow. For multi-user systems +# the system administrator is considered capable enough to restrict what they +# see they need to. + +# A WARNING to admins: do NOT allow access for users to this system remotely +# UNLESS you fully trust them or you have locked down permissions to halt/reboot +# suspend etc. here first. You have been warned. + +# FORMAT: +# +# user: username allow: halt reboot suspend hibernate +# group: groupname deny: * +# group: * deny: * +# user: * allow: suspend +# user: billy allow: halt reboot +# group: staff deny: halt suspend hibernate +# ... etc. ... +# +# user and group name can use glob matches (* == all for example) like the +# shell. as can action names allowed or denied. + +# root is allowed to do anything - but it needs to be here explicitly anyway +user: root allow: * +# members of operator, staff and admin groups should be able to do all +group: operator allow: * +group: staff allow: * +group: admin allow: * +group: sys allow: * +group: wheel allow: * +group: adm allow: * +# common "user" groups for "console users" on desktops/laptops +group: dialout allow: * +group: disk allow: * +group: adm allow: * +group: cdrom allow: * +group: floppy allow: * +group: audio allow: * +group: dip allow: * +group: plugdev allow: * +group: netdev allow: * +group: bluetooth allow: * +group: video allow: * +group: voice allow: * +group: fax allow: * +group: tty allow: * +group: colord allow: * +group: input allow: * +group: sudo allow: * + +# deny everyone else by default +user: * deny: * diff --git a/meson.build b/meson.build index b3ae22b1f..a1fa4ae15 100644 --- a/meson.build +++ b/meson.build @@ -147,6 +147,13 @@ config_h.set_quoted('PACKAGE_NAME' , proj) config_h.set_quoted('BINDIR' , dir_bin) config_h.set_quoted('DATADIR' , dir_data) +if cc.has_header('sys/prctl.h') == true + config_h.set('HAVE_PRCTL' , '1') +endif +if cc.has_header('sys/procctl.h') == true + config_h.set('HAVE_PROCCTL' , '1') +endif + if cc.has_function('setenv') == true config_h.set('HAVE_SETENV' , '1') endif diff --git a/src/bin/e_includes.h b/src/bin/e_includes.h index c2b7c2e8d..6ca9c9836 100644 --- a/src/bin/e_includes.h +++ b/src/bin/e_includes.h @@ -109,6 +109,7 @@ #include "e_widget_color_well.h" #include "e_color_dialog.h" #include "e_sys.h" +#include "e_system.h" #include "e_obj_dialog.h" #include "e_filereg.h" #include "e_widget_aspect.h" diff --git a/src/bin/e_main.c b/src/bin/e_main.c index 972f689dd..3738cb404 100644 --- a/src/bin/e_main.c +++ b/src/bin/e_main.c @@ -790,6 +790,15 @@ main(int argc, char **argv) e_desk_init(); e_exehist_init(); + TS("E_System Init"); + if (!e_system_init()) + { + e_error_message_show(_("Enlightenment cannot initialize the Privelege System access system.\n")); + _e_main_shutdown(-1); + } + TS("E_System Init Done"); + _e_main_shutdown_push(e_system_shutdown); + TS("E_Powersave Init"); if (!e_powersave_init()) { diff --git a/src/bin/e_system.c b/src/bin/e_system.c new file mode 100644 index 000000000..43199d6f3 --- /dev/null +++ b/src/bin/e_system.c @@ -0,0 +1,320 @@ +#include "e.h" + +typedef struct +{ + char cmd[24]; + int size; +} Message_Head; + +typedef struct +{ + void (*func) (void *data, const char *params); + void *data; + Eina_Bool delete_me : 1; +} Handler; + +static Ecore_Exe *_system_exe = NULL; +static Ecore_Event_Handler *_handler_del = NULL; +static Ecore_Event_Handler *_handler_data = NULL; +static Eina_Binbuf *_msg_buf = NULL; +static Eina_Hash *_handlers = NULL; +static int _handlers_busy = 0; +static Ecore_Timer *_error_dialog_timer = NULL; +static double _last_spawn = 0.0; +static int _respawn_count = 0; + +static Eina_Bool +_cb_dialog_timer(void *data EINA_UNUSED) +{ + _error_dialog_timer = NULL; + e_util_dialog_show(_("Error in Enlightenment System Service"), + _("Enlightenment cannot successfully start
" + "the enlightenment_system service.")); + return EINA_FALSE; +} + +static void +_system_spawn_error(void) +{ + if (_error_dialog_timer) ecore_timer_del(_error_dialog_timer); + _error_dialog_timer = ecore_timer_add(5.0, _cb_dialog_timer, NULL); +} + +static void +_system_spawn(void) +{ + char buf[PATH_MAX]; + double t = ecore_time_get(); + + if ((t - _last_spawn) < 10.0) _respawn_count++; + else _respawn_count = 0; + if (_respawn_count > 5) return; + snprintf(buf, sizeof(buf), + "%s/enlightenment/utils/enlightenment_system", e_prefix_lib_get()); + _system_exe = ecore_exe_pipe_run + (buf, ECORE_EXE_NOT_LEADER | ECORE_EXE_TERM_WITH_PARENT | + ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_WRITE, NULL); + if (!_system_exe) _system_spawn_error(); +} + +static Eina_Bool +_system_message_read(void) +{ + Message_Head *head; + const char *data = (const char *)eina_binbuf_string_get(_msg_buf); + size_t len = eina_binbuf_length_get(_msg_buf); + Eina_Binbuf *buf2; + + if (!data) return EINA_FALSE; + if (len < sizeof(Message_Head)) return EINA_FALSE; + head = (Message_Head *)data; + if (len < (sizeof(Message_Head) + head->size)) return EINA_FALSE; + if (_handlers) + { + Eina_List *list, *l, *ll, *plist; + Handler *h; + int del_count = 0; + + _handlers_busy++; + list = plist = eina_hash_find(_handlers, head->cmd); + EINA_LIST_FOREACH(list, l, h) + { + if (!h->delete_me) + { + if (head->size == 0) h->func(h->data, NULL); + else + { + if ((data + sizeof(Message_Head))[head->size - 1] == 0) + h->func(h->data, + (const char *)(data + sizeof(Message_Head))); + } + } + } + _handlers_busy--; + if (_handlers_busy == 0) + { + EINA_LIST_FOREACH_SAFE(list, l, ll, h) + { + if (h->delete_me) + { + list = eina_list_remove_list(list, l); + del_count++; + free(h); + } + } + } + if (del_count > 0) + { + eina_hash_del(_handlers, head->cmd, plist); + eina_hash_add(_handlers, head->cmd, list); + } + } + buf2 = eina_binbuf_new(); + if (buf2) + { + eina_binbuf_append_length + (buf2, + (const unsigned char *)data + sizeof(Message_Head) + head->size, + len - (sizeof(Message_Head) + head->size)); + eina_binbuf_free(_msg_buf); + _msg_buf = buf2; + } + return EINA_TRUE; +} + +static Eina_Bool +_cb_exe_del(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event) +{ + Ecore_Exe_Event_Del *ev = event; + + if (ev->exe != _system_exe) return ECORE_CALLBACK_PASS_ON; + if ((ev->exit_code == 3) || (ev->exit_code == 5) || + (ev->exit_code == 7) || (ev->exit_code == 9) || + (ev->exit_code == 11)) + { + // didn't run because it literally refused.... + _system_spawn_error(); + } + else + { + // it died for some other reason - restart it - maybe crashed? + if (_msg_buf) eina_binbuf_free(_msg_buf); + _msg_buf = eina_binbuf_new(); + _system_spawn(); + } + return ECORE_CALLBACK_DONE; +} + +static Eina_Bool +_cb_exe_data(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event) +{ + Ecore_Exe_Event_Data *ev = event; + + if (ev->exe != _system_exe) return ECORE_CALLBACK_PASS_ON; + // this is dumb, but it will work as this is avery low bandwidth + // i/o channel to/from the child exe stdin/out + if (_msg_buf) + { + eina_binbuf_append_length(_msg_buf, ev->data, ev->size); + while (_msg_buf && _system_message_read()); + } + return ECORE_CALLBACK_DONE; +} + +static Eina_Bool +_handler_list_free(const Eina_Hash *hash EINA_UNUSED, const void *key EINA_UNUSED, void *data, void *fdata EINA_UNUSED) +{ + Eina_List *list = data; + Handler *h; + + EINA_LIST_FREE(list, h) free(h); + return EINA_TRUE; +} + +/* externally accessible functions */ +EINTERN int +e_system_init(void) +{ + // XXX: + // + // if exe_data - parse/get data + // ... per message - call registered cb's for that msg + _handler_del = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _cb_exe_del, NULL); + _handler_data = ecore_event_handler_add(ECORE_EXE_EVENT_DATA, _cb_exe_data, NULL); + _msg_buf = eina_binbuf_new(); + _handlers = eina_hash_string_superfast_new(NULL); + _system_spawn(); + return 1; +} + +EINTERN int +e_system_shutdown(void) +{ + if (_msg_buf) eina_binbuf_free(_msg_buf); + if (_handler_del) ecore_event_handler_del(_handler_del); + if (_handler_data) ecore_event_handler_del(_handler_data); + if (_system_exe) + { + ecore_exe_interrupt(_system_exe); + ecore_exe_quit(_system_exe); + ecore_exe_terminate(_system_exe); + ecore_exe_kill(_system_exe); + ecore_exe_free(_system_exe); + } + if (_handlers) + { + eina_hash_foreach(_handlers, _handler_list_free, NULL); + eina_hash_free(_handlers); + } + if (_error_dialog_timer) ecore_timer_del(_error_dialog_timer); + _msg_buf = NULL; + _handler_del = NULL; + _handler_data = NULL; + _system_exe = NULL; + _handlers = NULL; + _error_dialog_timer = NULL; + return 1; +} + +E_API void +e_system_send(const char *cmd, const char *fmt, ...) +{ + char *buf = NULL, stack_buf[4096]; + Message_Head head; + size_t len = strlen(cmd); + int printed = 0; + va_list ap; + + if (!_system_exe) + { + ERR("Trying to send system command to non-existent process"); + return; + } + if (len > 23) + { + ERR("Trying to send command of length %i (max 23)", (int)len); + return; + } + if (fmt) + { + buf = stack_buf; + va_start(ap, fmt); + printed = vsnprintf(stack_buf, sizeof(stack_buf), fmt, ap); + va_end(ap); + if ((size_t)printed >= (sizeof(stack_buf) - 1)) + { + buf = malloc(printed + 1); + if (!buf) goto end; + va_start(ap, fmt); + printed = vsnprintf(buf, printed + 1, fmt, ap); + va_end(ap); + } + } + + memset(head.cmd, 0, sizeof(head.cmd)); + memcpy(head.cmd, cmd, len); + if (printed > 0) head.size = printed + 1; + else head.size = 0; + ecore_exe_send(_system_exe, &head, sizeof(head)); + if ((buf) && (head.size > 0)) + { + ecore_exe_send(_system_exe, buf, head.size); + } +end: + if (buf != stack_buf) free(buf); +} + +E_API void +e_system_handler_add(const char *cmd, void (*func) (void *data, const char *params), void *data) +{ + Eina_List *list; + Handler *h; + + if (!_handlers) + { + ERR("Trying to add system handler to NULL handler hash"); + return; + } + h = calloc(1, sizeof(Handler)); + if (!h) return; + h->func = func; + h->data = data; + list = eina_hash_find(_handlers, cmd); + eina_hash_del(_handlers, cmd, list); + list = eina_list_append(list, h); + eina_hash_add(_handlers, cmd, list); +} + +E_API void +e_system_handler_del(const char *cmd, void (*func) (void *data, const char *params), void *data) +{ + Eina_List *list, *l; + Handler *h; + + if (!_handlers) + { + ERR("Trying to del system handler to NULL handler hash"); + return; + } + list = eina_hash_find(_handlers, cmd); + if (list) + { + eina_hash_del(_handlers, cmd, list); + EINA_LIST_FOREACH(list, l, h) + { + if ((h->func == func) && (h->data == data)) + { + if (_handlers_busy > 0) + h->delete_me = EINA_TRUE; + else + { + list = eina_list_remove_list(list, l); + free(h); + } + break; + } + } + eina_hash_add(_handlers, cmd, list); + } +} + diff --git a/src/bin/e_system.h b/src/bin/e_system.h new file mode 100644 index 000000000..a846e9340 --- /dev/null +++ b/src/bin/e_system.h @@ -0,0 +1,15 @@ +#ifdef E_TYPEDEFS + +#else +#ifndef E_SYSTEM_H +#define E_SYSTEM_H + +EINTERN int e_system_init(void); +EINTERN int e_system_shutdown(void); + +E_API void e_system_send(const char *cmd, const char *fmt, ...); +E_API void e_system_handler_add(const char *cmd, void (*func) (void *data, const char *params), void *data); +E_API void e_system_handler_del(const char *cmd, void (*func) (void *data, const char *params), void *data); + +#endif +#endif diff --git a/src/bin/meson.build b/src/bin/meson.build index 4a0878eb9..fc0dc3480 100644 --- a/src/bin/meson.build +++ b/src/bin/meson.build @@ -177,6 +177,7 @@ src = [ 'e_spectrum.c', 'e_startup.c', 'e_sys.c', + 'e_system.c', 'e_test.c', 'e_theme_about.c', 'e_theme.c', @@ -355,6 +356,7 @@ hdr = [ 'e_spectrum.h', 'e_startup.h', 'e_sys.h', + 'e_system.h', 'e_test.h', 'e_theme_about.h', 'e_theme.h', @@ -582,3 +584,4 @@ if config_h.has('HAVE_WAYLAND') == true install: true) endif subdir('e_fm') +subdir('system') diff --git a/src/bin/system/e_system.h b/src/bin/system/e_system.h new file mode 100644 index 000000000..39a267adf --- /dev/null +++ b/src/bin/system/e_system.h @@ -0,0 +1,121 @@ +#ifndef E_SYSTEM_H +# define E_SYSTEM_H 1 +# include "config.h" + +# ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 +# endif + +# ifdef STDC_HEADERS +# include +# include +# else +# ifdef HAVE_STDLIB_H +# include +# endif +# endif +# ifdef HAVE_ALLOCA_H +# include +# elif !defined alloca +# ifdef __GNUC__ +# define alloca __builtin_alloca +# elif defined _AIX +# define alloca __alloca +# elif defined _MSC_VER +# include +# define alloca _alloca +# elif !defined HAVE_ALLOCA +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +# endif +# endif + +# ifdef __linux__ +# include +# endif + +# ifdef HAVE_ENVIRON +# define _GNU_SOURCE 1 +# endif +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +#if defined (__FreeBSD__) || defined (__OpenBSD__) +# include +#endif + +#ifdef HAVE_PRCTL +# include +#elif defined(HAVE_PROCCTL) +# include +#endif + +# ifndef _POSIX_HOST_NAME_MAX +# define _POSIX_HOST_NAME_MAX 255 +# endif + +# include +# include +# include +# include +# ifdef HAVE_EEZE +# include +# endif + +#define ERR(args...) do { fprintf(stderr, "E_SYSTEM_ERR: "); fprintf(stderr, ##args); } while (0) + +extern uid_t uid; +extern gid_t gid; + +void e_system_inout_init(void); +void e_system_inout_shutdown(void); +void e_system_inout_command_register(const char *cmd, void (*func) (void *data, const char *aprams), void *data); +void e_system_inout_command_send(const char *cmd, const char *fmt, ...); + +void e_system_backlight_init(void); +void e_system_backlight_shutdown(void); + +void e_system_storage_init(void); +void e_system_storage_shutdown(void); + +void e_system_power_init(void); +void e_system_power_shutdown(void); + +void e_system_rfkill_init(void); +void e_system_rfkill_shutdown(void); + +void e_system_l2ping_init(void); +void e_system_l2ping_shutdown(void); + +void e_system_cpufreq_init(void); +void e_system_cpufreq_shutdown(void); + +#endif + diff --git a/src/bin/system/e_system_backlight.c b/src/bin/system/e_system_backlight.c new file mode 100644 index 000000000..fca3a4414 --- /dev/null +++ b/src/bin/system/e_system_backlight.c @@ -0,0 +1,321 @@ +#include "e_system.h" + +typedef struct +{ + char *dev; + int val, max, val_set, val_get_count; + Eina_Bool prefer : 1; + Eina_Bool set : 1; +} Light; + +static Eina_Lock _devices_lock; +static Eina_List *_devices = NULL; +static Eina_Semaphore _worker_sem; + +static void +_light_set(Light *lig, int val) +{ // val == 0->1000 + int nval = ((lig->max * val) + 500) / 1000; + if (nval < 0) nval = 0; + else if (nval > lig->max) nval = lig->max; + lig->val = nval; +#ifdef HAVE_EEZE + char buf[PATH_MAX]; + snprintf(buf, sizeof(buf), "%s/brightness", lig->dev); + int fd = open(buf, O_WRONLY); + if (fd >= 0) + { + char buf2[32]; + snprintf(buf2, sizeof(buf2), "%i", lig->val); + if (write(fd, buf2, strlen(buf2)) <= 0) + ERR("Write failed of [%s] to [%s]\n", buf2, buf); + close(fd); + } +#elif defined(__FreeBSD_kernel__) + sysctlbyname(lig->dev, NULL, NULL, &(lig->val), sizeof(lig->val)); +#endif +} + +static void +_light_get(Light *lig) +{ +#ifdef HAVE_EEZE + const char *s; + s = eeze_udev_syspath_get_sysattr(lig->dev, "brightness"); + if (s) + { + lig->val = atoi(s); + eina_stringshare_del(s); + } +#elif defined(__FreeBSD_kernel__) + int plen = sizeof(lig->val); + sysctlbyname(lig->dev, &(lig->val), &plen, NULL, 0); +#endif +} + +static void +_cb_worker(void *data EINA_UNUSED, Ecore_Thread *th) +{ + for (;;) + { + Eina_List *l; + Light *lig; + + eina_semaphore_lock(&_worker_sem); + + eina_lock_take(&_devices_lock); + EINA_LIST_FOREACH(_devices, l, lig) + { + if (lig->val_get_count > 0) + { + _light_get(lig); + while (lig->val_get_count > 0) + { + Light *lig2 = calloc(1, sizeof(Light)); + lig->val_get_count--; + if (lig2) + { + lig2->dev = strdup(lig->dev); + lig2->max = lig->max; + if (lig2->dev) + { + lig2->val = lig->val; + ecore_thread_feedback(th, lig2); + } + else free(lig2); + } + } + } + if (lig->set) + { + lig->set = EINA_FALSE; + lig->val = lig->val_set; + _light_set(lig, lig->val); + } + } + eina_lock_release(&_devices_lock); + } +} + +static void +_cb_worker_message(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED, void *msg_data) +{ + Light *lig = msg_data; + int val = 0; + + if (lig->max > 0) + { + val = ((1000 * lig->val) + 500) / lig->max; + if (val < 0) val = 0; + else if (val > 1000) val = 1000; + } + e_system_inout_command_send("bklight-val", "%s %i", lig->dev, val); + free(lig->dev); + free(lig); +} + +static void +_cb_worker_end(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED) +{ +} + +static void +_cb_worker_cancel(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED) +{ +} + +static void +_light_add(const char *dev) +{ + Light *lig = calloc(1, sizeof(Light)); + + if (!lig) abort(); + lig->dev = strdup(dev); + if (!lig->dev) abort(); + lig->val = -1; // uknown +#ifdef HAVE_EEZE + const char *s; + + s = eeze_udev_syspath_get_sysattr(lig->dev, "brightness"); + if (!s) + { + free(lig->dev); + free(lig); + return; + } + lig->val = atoi(s); + eina_stringshare_del(s); + + s = eeze_udev_syspath_get_sysattr(lig->dev, "max_brightness"); + if (s) + { + lig->max = atoi(s); + eina_stringshare_del(s); + } + if (lig->max <= 0) lig->max = 255; + lig->prefer = eeze_udev_syspath_check_sysattr(lig->dev, "type", "firmware"); +#elif defined(__FreeBSD_kernel__) + int plen = sizeof(lig->val); + sysctlbyname(lig->dev, &(lig->val), &plen, NULL, 0); + lig->max = 100; +#endif + _devices = eina_list_append(_devices, lig); +} + +#ifdef HAVE_EEZE +static Eina_Bool +_light_device_include(const char *dev) +{ // filter out known undesireable devices + if (strstr(dev, "::capslock")) return EINA_FALSE; + if (strstr(dev, "::numlock")) return EINA_FALSE; + if (strstr(dev, "::scrolllock")) return EINA_FALSE; + if (strstr(dev, "::compose")) return EINA_FALSE; + if (strstr(dev, "::kana")) return EINA_FALSE; + return EINA_TRUE; +} +#endif + +static void +_light_refresh_devices(void) +{ + Light *lig; + + EINA_LIST_FREE(_devices, lig) + { + free(lig->dev); + free(lig); + } +#ifdef HAVE_EEZE + Eina_List *devs; + const char *s; + + devs = eeze_udev_find_by_filter("backlight", NULL, NULL); + EINA_LIST_FREE(devs, s) + { + if (_light_device_include(s)) _light_add(s); + eina_stringshare_del(s); + } + devs = eeze_udev_find_by_filter("leds", NULL, NULL); + EINA_LIST_FREE(devs, s) + { + if (_light_device_include(s)) _light_add(s); + eina_stringshare_del(s); + } +#elif defined(__FreeBSD_kernel__) + // XXX; shouldn't we scan for devices? + _light_add("hw.acpi.video.lcd0.brightness"); +#endif +} + +static Light * +_light_find(const char *dev) +{ + Eina_List *l; + Light *lig; + + if (!dev) return NULL; + EINA_LIST_FOREACH(_devices, l, lig) + { + if (!strcmp(lig->dev, dev)) return lig; + } + return NULL; +} + +static void +_cb_bklight_list(void *data EINA_UNUSED, const char *params EINA_UNUSED) +{ // reply = "dev1 -|p dev2 -|p ..." + Eina_List *l; + Light *lig; + char *rep = NULL, *p = NULL; + size_t repsize = 0; + + eina_lock_take(&_devices_lock); + EINA_LIST_FOREACH(_devices, l, lig) + { + repsize += strlen(lig->dev) + 2 + 1; + } + if (repsize > 0) + { + rep = malloc(repsize); + if (!rep) abort(); + p = rep; + EINA_LIST_FOREACH(_devices, l, lig) + { + size_t len = strlen(lig->dev); + memcpy(p, lig->dev, len + 1); + p[len] = ' '; len++; + if (lig->prefer) p[len] = 'p'; + else p[len] = '-'; + len++; + if (l->next) p[len] = ' '; + else p[len] = '\0'; + p += len + 1; + } + } + eina_lock_release(&_devices_lock); + e_system_inout_command_send("bklight-list", "%s", rep); + free(rep); +} + +static void +_cb_bklight_refresh(void *data EINA_UNUSED, const char *params EINA_UNUSED) +{ + eina_lock_take(&_devices_lock); + _light_refresh_devices(); + eina_lock_release(&_devices_lock); +} + +static void +_cb_bklight_get(void *data EINA_UNUSED, const char *params) +{ + Light *lig; + + eina_lock_take(&_devices_lock); + lig = _light_find(params); + if (!lig) goto done; + lig->val_get_count++; + eina_semaphore_release(&_worker_sem, 1); + _light_get(lig); +done: + eina_lock_release(&_devices_lock); +} + +static void +_cb_bklight_set(void *data EINA_UNUSED, const char *params) +{ + Light *lig; + char dev[1024] = ""; + int val = 0; + + if (!params) return; + if (sscanf(params, "%1023s %i", dev, &val) != 2) return; + eina_lock_take(&_devices_lock); + lig = _light_find(dev); + if (!lig) goto done; + lig->val_set = val; + lig->set = EINA_TRUE; + eina_semaphore_release(&_worker_sem, 1); +done: + eina_lock_release(&_devices_lock); +} + +void +e_system_backlight_init(void) +{ + _light_refresh_devices(); + eina_lock_new(&_devices_lock); + eina_semaphore_new(&_worker_sem, 0); + ecore_thread_feedback_run(_cb_worker, _cb_worker_message, + _cb_worker_end, _cb_worker_cancel, + NULL, EINA_TRUE); + e_system_inout_command_register("bklight-list", _cb_bklight_list, NULL); + e_system_inout_command_register("bklight-refresh", _cb_bklight_refresh, NULL); + e_system_inout_command_register("bklight-get", _cb_bklight_get, NULL); + e_system_inout_command_register("bklight-set", _cb_bklight_set, NULL); +} + +void +e_system_backlight_shutdown(void) +{ + // only shutdown things we really have to - no need to free mem etc. +} diff --git a/src/bin/system/e_system_cpufreq.c b/src/bin/system/e_system_cpufreq.c new file mode 100644 index 000000000..31d2ed34a --- /dev/null +++ b/src/bin/system/e_system_cpufreq.c @@ -0,0 +1,163 @@ +#include "e_system.h" + +#if defined __OpenBSD__ +// no local funcs +#elif defined __FreeBSD__ +// no local funcs +#else +static int +sys_cpu_setall(const char *control, const char *value) +{ + int num = 0; + char buf[4096]; + FILE *f; + + for (;;) + { + snprintf(buf, sizeof(buf), + "/sys/devices/system/cpu/cpu%i/cpufreq/%s", num, control); + f = fopen(buf, "w"); + if (!f) return num; + fprintf(f, "%s", value); + fclose(f); + num++; + } + return 0; +} + +static int +sys_cpufreq_set(const char *control, const char *value) +{ + char buf[4096]; + FILE *f; + + snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpufreq/%s", control); + f = fopen(buf, "w"); + if (!f) + { + if (sys_cpu_setall(control, value) > 0) return 1; + else return 0; + } + fprintf(f, "%s", value); + fclose(f); + return 1; +} + +static int +sys_cpu_pstate(int min, int max, int turbo) +{ + FILE *f; + + f = fopen("/sys/devices/system/cpu/intel_pstate/min_perf_pct", "w"); + if (!f) return 0; + fprintf(f, "%i", min); + fclose(f); + + f = fopen("/sys/devices/system/cpu/intel_pstate/max_perf_pct", "w"); + if (!f) return 0; + fprintf(f, "%i", max); + fclose(f); + + f = fopen("/sys/devices/system/cpu/intel_pstate/no_turbo", "w"); + if (!f) return 0; + fprintf(f, "%i", turbo ? 0 : 1); + fclose(f); + + return 1; +} +#endif + +static void +_cb_cpufreq_freq(void *data EINA_UNUSED, const char *params) +{ + // FREQ + int f = atoi(params); + if (f > 0) + { +#if defined __OpenBSD__ + int mib[] = {CTL_HW, HW_SETPERF}; + size_t len = sizeof(f); + + if (sysctl(mib, 2, NULL, 0, &f, len) == 0) + { + e_system_inout_command_send("cpufreq-freq", "ok"); + return; + } +#elif defined __FreeBSD__ + if (sysctlbyname("dev.cpu.0.freq", NULL, NULL, &f, sizeof(f)) == 0) + { + e_system_inout_command_send("cpufreq-freq", "ok"); + return; + } +#else + if (sys_cpu_setall("scaling_setspeed", params) > 0) + { + e_system_inout_command_send("cpufreq-freq", "ok"); + return; + } +#endif + } + e_system_inout_command_send("cpufreq-freq", "err"); +} + +static void +_cb_cpufreq_governor(void *data EINA_UNUSED, const char *params) +{ + // NAME +#if defined __OpenBSD__ + e_system_inout_command_send("cpufreq-governor", "err"); +#elif defined __FreeBSD__ + e_system_inout_command_send("cpufreq-governor", "err"); +#else + if (sys_cpu_setall("scaling_governor", params) <= 0) + { + ERR("Unable to open governor interface for writing\n"); + e_system_inout_command_send("cpufreq-governor", "err"); + } + if (!strcmp(params, "ondemand")) + sys_cpufreq_set("ondemand/ignore_nice_load", "0"); + else if (!strcmp(params, "conservative")) + sys_cpufreq_set("conservative/ignore_nice_load", "0"); + e_system_inout_command_send("cpufreq-governor", "ok"); +#endif +} + +static void +_cb_cpufreq_pstate(void *data EINA_UNUSED, const char *params) +{ + // MIN_PERC MAX_PERC TURBO +#if defined __OpenBSD__ + e_system_inout_command_send("cpufreq-pstate", "err"); +#elif defined __FreeBSD__ + e_system_inout_command_send("cpufreq-pstate", "err"); +#else + int min = 0, max = 100, turbo = 1; + + if (sscanf(params, "%i %i %i", &min, &max, &turbo) == 3) + { + if (min < 0) min = 0; + else if (min > 100) min = 100; + if (max < 0) max = 0; + else if (max > 100) max = 100; + if (turbo < 0) turbo = 0; + else if (turbo > 1) turbo = 1; + if (sys_cpu_pstate(min, max, turbo) > 0) + e_system_inout_command_send("cpufreq-pstate", "ok"); + } + e_system_inout_command_send("cpufreq-pstate", "err"); +#endif +} + +void +e_system_cpufreq_init(void) +{ + e_system_inout_command_register("cpufreq-freq", _cb_cpufreq_freq, NULL); + e_system_inout_command_register("cpufreq-governor", _cb_cpufreq_governor, NULL); + e_system_inout_command_register("cpufreq-pstate", _cb_cpufreq_pstate, NULL); +} + +void +e_system_cpufreq_shutdown(void) +{ + // only shutdown things we really have to - no need to free mem etc. +} diff --git a/src/bin/system/e_system_inout.c b/src/bin/system/e_system_inout.c new file mode 100644 index 000000000..5d1b12a45 --- /dev/null +++ b/src/bin/system/e_system_inout.c @@ -0,0 +1,171 @@ +#include "e_system.h" + +typedef struct +{ + void (*func) (void *data, const char *params); + void *data; +} Handler; + +static Eina_Hash *_cmd_handlers = NULL; +static Ecore_Fd_Handler *_fdh_in = NULL; + +//// note: commands are of the form: +// [ 24 bytes] command string including nul byte terminator and 0 padding +// [ 4 bytes] payload size (max size 2gb) +// [ N bytes] payload data (0 or more)... + +typedef struct +{ + char cmd[24]; + int size; +} Message_Head; + +static Eina_Bool +_cb_stdio_in_read(void *data EINA_UNUSED, Ecore_Fd_Handler *fd_handler EINA_UNUSED) +{ + Handler *h; + Message_Head head; + ssize_t ret; + + errno = 0; + ret = read(0, &head, sizeof(head)); + if (ret < 1) + { + int e = errno; + if ((e == EIO) || (e == EBADF) || (e == EPIPE) || + (e == EINVAL) || (e == ENOSPC) || + (!((e == EAGAIN) || (e == EINTR)))) + { + ecore_main_loop_quit(); + goto done; + } + goto done; + } + else + { + char *buf; + + if (head.size < 0) + { + ERR("Invalid message payload size (less than 0)\n"); + abort(); + } + buf = NULL; + if (head.size > 0) + { + buf = malloc(head.size); + if (!buf) + { + ERR("Out of memory for message of size %i bytes\n", head.size); + abort(); + } + ret = read(0, buf, head.size); + if (ret != head.size) + { + ERR("Cannot read full message payload of %i bytes\n", head.size); + abort(); + } + } + h = eina_hash_find(_cmd_handlers, head.cmd); + if (h) + { + if ((!buf) || ((buf) && (buf[head.size - 1] == '\0'))) + h->func(h->data, buf); + } + free(buf); + } +done: + return EINA_TRUE; +} + +void +e_system_inout_init(void) +{ + _cmd_handlers = eina_hash_string_superfast_new(free); + _fdh_in = ecore_main_fd_handler_add(0, ECORE_FD_READ, + _cb_stdio_in_read, NULL, + NULL, NULL); +} + +void +e_system_inout_shutdown(void) +{ + ecore_main_fd_handler_del(_fdh_in); + _fdh_in = NULL; + eina_hash_free(_cmd_handlers); + _cmd_handlers = NULL; +} + +void +e_system_inout_command_register(const char *cmd, void (*func) (void *data, const char *params), void *data) +{ + Handler *h = malloc(sizeof(Handler)); + size_t len = strlen(cmd); + if (!h) + { + ERR("Out of memory registering command handlers\n"); + abort(); + } + if (len > 23) + { + ERR("Trying to register command of length %i (max 23)\n", (int)len); + abort(); + } + h->func = func; + h->data = data; + eina_hash_add(_cmd_handlers, cmd, h); +} + +void +e_system_inout_command_send(const char *cmd, const char *fmt, ...) +{ + char *buf = NULL, stack_buf[4096]; + Message_Head head; + size_t len = strlen(cmd); + int printed = 0; + ssize_t ret; + va_list ap; + + if (len > 23) + { + ERR("Trying to send command of length %i (max 23)\n", (int)len); + abort(); + } + if (fmt) + { + buf = stack_buf; + va_start(ap, fmt); + printed = vsnprintf(stack_buf, sizeof(stack_buf), fmt, ap); + va_end(ap); + if ((size_t)printed >= (sizeof(stack_buf) - 1)) + { + buf = malloc(printed + 1); + if (!buf) goto end; + va_start(ap, fmt); + printed = vsnprintf(buf, printed + 1, fmt, ap); + va_end(ap); + } + } + + memset(head.cmd, 0, sizeof(head.cmd)); + memcpy(head.cmd, cmd, len); + if (printed > 0) head.size = printed + 1; + else head.size = 0; + ret = write(1, &head, sizeof(head)); + if (ret != sizeof(head)) + { + ERR("Write of command failed at %lli\n", (long long)ret); + abort(); + } + if ((buf) && (head.size > 0)) + { + ret = write(1, buf, head.size); + if (ret != (ssize_t)head.size) + { + ERR("Write of command buffer failed at %lli/%llu\n", (long long)ret, (unsigned long long)head.size); + abort(); + } + } +end: + if (buf != stack_buf) free(buf); +} diff --git a/src/bin/system/e_system_l2ping.c b/src/bin/system/e_system_l2ping.c new file mode 100644 index 000000000..0900e14d4 --- /dev/null +++ b/src/bin/system/e_system_l2ping.c @@ -0,0 +1,229 @@ +#include "e_system.h" + +#ifdef HAVE_BLUETOOTH +# include +# include +# include +# include +#endif + +#define MAX_SZ 500 + +typedef struct +{ + char *dev; + int timeout, result; +} Action; + +static void +_l2ping_free(Action *a) +{ + free(a->dev); + free(a); +} + +#ifdef HAVE_BLUETOOTH +static void +_l2ping_l2addr_init(struct sockaddr_l2 *ad) +{ + memset(ad, 0, sizeof(*ad)); + ad->l2_family = AF_BLUETOOTH; +} + +# define SETUP_FDSET(rfds, wfds, exfds) \ + FD_ZERO(&rfds); \ + FD_ZERO(&wfds); \ + FD_ZERO(&exfds); +# define SETUP_TIMEOUT(tv, a) \ + tv.tv_sec = a->timeout / 1000; \ + tv.tv_usec = (a->timeout % 1000) * 1000; +#endif + +static void +_cb_l2ping(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED) +{ +#ifdef HAVE_BLUETOOTH + Action *a = data; + char buf1[L2CAP_CMD_HDR_SIZE + MAX_SZ], buf2[L2CAP_CMD_HDR_SIZE + MAX_SZ]; + bdaddr_t ba; + l2cap_cmd_hdr *cmd; + struct sockaddr_l2 ad; + double start; + int fd, err, size, i; + fd_set rfds, wfds, exfds; + struct timeval tv; + + fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP); + if (fd < 0) + { + ERR("l2ping: Can't create socket\n"); + return; + } + + // bind to local address + _l2ping_l2addr_init(&ad); + bacpy(&ba, BDADDR_ANY); + bacpy(&ad.l2_bdaddr, &ba); + if (bind(fd, (struct sockaddr *)&ad, sizeof(ad)) < 0) + { + ERR("l2ping: Can't bind socket\n"); + goto err; + } + + // connect to remote device + _l2ping_l2addr_init(&ad); + str2ba(a->dev, &ad.l2_bdaddr); + if (connect(fd, (struct sockaddr *)&ad, sizeof(ad)) < 0) + { + ERR("l2ping: Can't connect socket to [%s]\n", a->dev); + goto err; + } + + SETUP_FDSET(rfds, wfds, exfds); + FD_SET(fd, &wfds); + SETUP_TIMEOUT(tv, a); + start = ecore_time_get(); + err = select(fd + 1, &rfds, &wfds, &exfds, &tv); + if (err == 0) + { + ERR("l2ping: Connect timeout [%s]\n", a->dev); + goto err; + } + + // adjust timeout by how long we waited to connect + a->timeout -= (ecore_time_get() - start) * 1000; + if (a->timeout < 1) a->timeout = 1; + + size = 44; // use std 44 byte ping size, but no more than MAX_SZ + cmd = (l2cap_cmd_hdr *)buf1; + cmd->ident = 200; + cmd->len = htobs(size); + cmd->code = L2CAP_ECHO_REQ; + // init buffer with some content + // ABCDEF....WXYZABCEF... up to "size" chars + for (i = 0; i < size; i++) buf1[L2CAP_CMD_HDR_SIZE + i] = 'A' + (i % 26); + + // get our time just before a send + start = ecore_time_get(); + // send the ping + if (send(fd, buf1, L2CAP_CMD_HDR_SIZE + size, 0) <= 0) + { + ERR("l2ping: Send to [%s] failed\n", a->dev); + goto err; + } + + // wait for the reply to this ping + for (;;) + { + SETUP_FDSET(rfds, wfds, exfds); + FD_SET(fd, &rfds); + SETUP_TIMEOUT(tv, a); + err = select(fd + 1, &rfds, &wfds, &exfds, &tv); + if (err == 0) + { + ERR("l2ping: Select timeout [%s]\n", a->dev); + goto err; + } + else if (err < 0) + { + ERR("l2ping: Select for [%s] failed\n", a->dev); + goto err; + } + + err = recv(fd, buf2, L2CAP_CMD_HDR_SIZE + size, 0); + if (err == 0) + { + ERR("l2ping: Disconnect %s\n", a->dev); + goto err; + } + else if (err < 0) + { + ERR("l2ping: Recv [%s] failed\n", a->dev); + goto err; + } + + cmd = (l2cap_cmd_hdr *)buf2; + cmd->len = btohs(cmd->len); + // we only want the 200 ident response packets + if (cmd->ident != 200) continue; + if (cmd->code == L2CAP_COMMAND_REJ) + { + ERR("l2ping: [%s] doesn't do echo\n", a->dev); + goto err; + } + if (cmd->len != size) + { + ERR("l2ping: Size %i echo for [%s] does not match %i\n", + (int)cmd->len, a->dev, (int)size); + goto err; + } + if (memcmp(buf1 + L2CAP_CMD_HDR_SIZE, buf2 + L2CAP_CMD_HDR_SIZE, + size) != 0) + { + ERR("l2ping: Echo response from [%s] does not match sent data\n", + a->dev); + goto err; + } + break; + } + // time it took to send and get our response + a->result = (ecore_time_get() - start) * 1000.0; +err: + close(fd); +#else + ERR("l2ping: Bluetooth support not compiled in\n"); +#endif +} + +static void +_cb_l2ping_end(void *data, Ecore_Thread *th EINA_UNUSED) +{ + Action *a = data; + e_system_inout_command_send("l2ping-ping", "%s %i", a->dev, a->result); + _l2ping_free(a); +} + +static void +_cb_l2ping_cancel(void *data, Ecore_Thread *th EINA_UNUSED) +{ + Action *a = data; + e_system_inout_command_send("l2ping-ping", "%s %i", a->dev, -1); + _l2ping_free(a); +} + +static void +_cb_l2ping_ping(void *data EINA_UNUSED, const char *params) +{ + // ADDR TIMEOUT_MS + int timeout = 1; + char dev[1024]; + Action *a; + + if (sscanf(params, "%s %i", dev, &timeout) != 2) return; + if (timeout < 1) timeout = 1; + else if (timeout > (1000 * 600)) timeout = 1000 * 600; + a = calloc(1, sizeof(Action)); + if (!a) return; + a->dev = strdup(dev); + if (!a->dev) goto err; + a->timeout = timeout; + a->result = -1; + if (ecore_thread_feedback_run(_cb_l2ping, NULL, + _cb_l2ping_end, _cb_l2ping_cancel, + a, EINA_TRUE)) + return; +err: + _l2ping_free(a); +} + +void +e_system_l2ping_init(void) +{ + e_system_inout_command_register("l2ping-ping", _cb_l2ping_ping, NULL); +} + +void +e_system_l2ping_shutdown(void) +{ + // only shutdown things we really have to - no need to free mem etc. +} diff --git a/src/bin/system/e_system_main.c b/src/bin/system/e_system_main.c new file mode 100644 index 000000000..f355b57d4 --- /dev/null +++ b/src/bin/system/e_system_main.c @@ -0,0 +1,303 @@ +#include "e_system.h" + +uid_t uid = -1; // uid of person running me +gid_t gid = -1; // gid of person running me + +static int +_conf_allow_deny(const char *cmd, const char *glob) +{ + if (!strcmp(cmd, "allow:")) + { + if (!strcmp(glob, "*")) return 1; // allow + } + else if (!strcmp(cmd, "deny:")) + { + if (!strcmp(glob, "*")) return -1; // deny + } + return 0; // unknown +} + +static void +_etc_enlightenment_system_conf(void) +{ +#define MAXGROUPS 1024 + int gn, i; + gid_t gl[MAXGROUPS]; + char type[32], usergroup[256], cmd[32], glob[256]; + Eina_Bool in_usergroup; + FILE *f = fopen("/etc/enlightenment/system.conf", "r"); + if (!f) return; + + gn = getgroups(MAXGROUPS, gl); + if (gn < 0) + { + ERR("User %i member of too many groups\n", uid); + exit(9); + } + if (fscanf(f, "%31s %255s %31s %255s\n", type, usergroup, cmd, glob) == 4) + { + if (!strcmp(type, "user:")) + { + struct passwd *pw = getpwuid(uid); + + in_usergroup = EINA_FALSE; + if (pw) + { + if (!fnmatch(usergroup, pw->pw_name, 0)) + in_usergroup = EINA_TRUE; + } + if (in_usergroup) + { + int ok = _conf_allow_deny(cmd, glob); + if (ok == 1) goto allow; + else if (ok == -1) goto deny; + } + } + else if (!strcmp(type, "group:")) + { + struct group *gp; + + in_usergroup = EINA_FALSE; + gp = getgrgid(gid); + if (gp) + { + for (i = 0; i < gn; i++) + { + gp = getgrgid(gl[i]); + if (!fnmatch(usergroup, gp->gr_name, 0)) + { + in_usergroup = EINA_TRUE; + break; + } + } + } + if (in_usergroup) + { + int ok = _conf_allow_deny(cmd, glob); + if (ok == 1) goto allow; + else if (ok == -1) goto deny; + } + } + } +allow: + fclose(f); + return; +deny: + fclose(f); + ERR("Permission denied to use this tool\n"); + exit(11); +} + +static void +setuid_setup(void) +{ + uid = getuid(); + gid = getgid(); + + if (setuid(0) != 0) + { + ERR("Unable to assume root user privileges\n"); + exit(5); + } + if (setgid(0) != 0) + { + ERR("Unable to assume root group privileges\n"); + exit(7); + } + + // die with parent - special as this is setuid +#ifdef HAVE_PRCTL + prctl(PR_SET_PDEATHSIG, SIGTERM); +#elif defined(HAVE_PROCCTL) + int sig = SIGTERM; + procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &sig); +#endif + +#ifdef HAVE_UNSETENV +# define NOENV(x) unsetenv(x) + // pass 1 - just nuke known dangerous env vars brutally if possible via + // unsetenv(). if you don't have unsetenv... there's pass 2 and 3 + NOENV("IFS"); + NOENV("CDPATH"); + NOENV("LOCALDOMAIN"); + NOENV("RES_OPTIONS"); + NOENV("HOSTALIASES"); + NOENV("NLSPATH"); + NOENV("PATH_LOCALE"); + NOENV("COLORTERM"); + NOENV("LANG"); + NOENV("LANGUAGE"); + NOENV("LINGUAS"); + NOENV("TERM"); + NOENV("LD_PRELOAD"); + NOENV("LD_LIBRARY_PATH"); + NOENV("SHLIB_PATH"); + NOENV("LIBPATH"); + NOENV("AUTHSTATE"); + NOENV("DYLD_*"); + NOENV("KRB_CONF*"); + NOENV("KRBCONFDIR"); + NOENV("KRBTKFILE"); + NOENV("KRB5_CONFIG*"); + NOENV("KRB5_KTNAME"); + NOENV("VAR_ACE"); + NOENV("USR_ACE"); + NOENV("DLC_ACE"); + NOENV("TERMINFO"); + NOENV("TERMINFO_DIRS"); + NOENV("TERMPATH"); + NOENV("TERMCAP"); + NOENV("ENV"); + NOENV("BASH_ENV"); + NOENV("PS4"); + NOENV("GLOBIGNORE"); + NOENV("SHELLOPTS"); + NOENV("JAVA_TOOL_OPTIONS"); + NOENV("PERLIO_DEBUG"); + NOENV("PERLLIB"); + NOENV("PERL5LIB"); + NOENV("PERL5OPT"); + NOENV("PERL5DB"); + NOENV("FPATH"); + NOENV("NULLCMD"); + NOENV("READNULLCMD"); + NOENV("ZDOTDIR"); + NOENV("TMPPREFIX"); + NOENV("PYTHONPATH"); + NOENV("PYTHONHOME"); + NOENV("PYTHONINSPECT"); + NOENV("RUBYLIB"); + NOENV("RUBYOPT"); +# ifdef HAVE_ENVIRON + if (environ) + { + Eina_Bool again; + // go over environment array again and again... safely + do + { + again = EINA_FALSE; + // walk through and find first entry that we don't like */ + for (i = 0; environ[i]; i++) + { + // if it begins with any of these, it's possibly nasty */ + if ((!strncmp(environ[i], "LD_", 3)) || + (!strncmp(environ[i], "_RLD_", 5)) || + (!strncmp(environ[i], "LC_", 3)) || + (!strncmp(environ[i], "LDR_", 3))) + { + // unset it + char *tmp, *p; + + tmp = strdup(environ[i]); + if (!tmp) abort(); + p = strchr(tmp, '='); + if (!p) abort(); + *p = 0; + NOENV(tmp); + free(tmp); + // and mark our do to try again from the start in case + // unsetenv changes environ ptr + again = EINA_TRUE; + break; + } + } + } + while (again); + } +# endif +#endif + // pass 2 - clear entire environment so it doesn't exist at all. if you + // can't do this... you're possibly in trouble... but the worst is still + // fixed in pass 3 +#ifdef HAVE_CLEARENV + clearenv(); +#else +# ifdef HAVE_ENVIRON + environ = NULL; +# endif +#endif + // pass 3 - set path and ifs to minimal defaults + putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin"); + putenv("IFS= \t\n"); + _etc_enlightenment_system_conf(); +} + +static void +_cb_die(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED) +{ + char buf[256]; + int f; + + snprintf(buf, sizeof(buf), "/var/run/enlightenment_sys-%u.lck", uid); + f = open(buf, O_RDONLY); + if (f < 0) exit(0); + exit(0); +} + +static void +singleton_setup(void) +{ // only one per uid - kill existing and replace... + char buf[256]; + int f; + mode_t um; + + snprintf(buf, sizeof(buf), "/var/run/enlightenment_sys-%u.lck", uid); + f = open(buf, O_WRONLY | O_NONBLOCK); + if (f >= 0) + { + if (write(f, buf, 1) == 1) + ERR("Replacing previous enlightenment_system\n"); + close(f); + } + unlink(buf); + um = umask(0); + mkfifo(buf, S_IRUSR | S_IWUSR); + umask(um); + + ecore_thread_feedback_run(_cb_die, NULL, NULL, NULL, NULL, EINA_TRUE); +} + +int +main(int argc EINA_UNUSED, const char **argv EINA_UNUSED) +{ + setuid_setup(); + + ecore_app_no_system_modules(); + + eina_init(); + ecore_init(); + ecore_file_init(); +#ifdef HAVE_EEZE + eeze_init(); +#endif + eet_init(); + + singleton_setup(); + + e_system_inout_init(); + e_system_backlight_init(); + e_system_storage_init(); + e_system_power_init(); + e_system_rfkill_init(); + e_system_l2ping_init(); + e_system_cpufreq_init(); + + ecore_main_loop_begin(); + + e_system_cpufreq_shutdown(); + e_system_l2ping_shutdown(); + e_system_rfkill_shutdown(); + e_system_power_shutdown(); + e_system_storage_shutdown(); + e_system_backlight_shutdown(); + e_system_inout_shutdown(); + + eet_shutdown(); +#ifdef HAVE_EEZE + eeze_shutdown(); +#endif + ecore_file_shutdown(); + ecore_shutdown(); + eina_shutdown(); + return 0; +} diff --git a/src/bin/system/e_system_power.c b/src/bin/system/e_system_power.c new file mode 100644 index 000000000..6643a3133 --- /dev/null +++ b/src/bin/system/e_system_power.c @@ -0,0 +1,134 @@ +#include "e_system.h" + +char *_cmd_halt = NULL; +char *_cmd_reboot = NULL; +char *_cmd_suspend = NULL; +char *_cmd_hibernate = NULL; + +static void +_cb_power_halt(void *data EINA_UNUSED, const char *params EINA_UNUSED) +{ + if (_cmd_halt) ecore_exe_run(_cmd_halt, NULL); +} + +static void +_cb_power_reboot(void *data EINA_UNUSED, const char *params EINA_UNUSED) +{ + if (_cmd_reboot) ecore_exe_run(_cmd_reboot, NULL); +} + +static void +_cb_power_suspend(void *data EINA_UNUSED, const char *params EINA_UNUSED) +{ + if (_cmd_suspend) ecore_exe_run(_cmd_suspend, NULL); +} + +static void +_cb_power_hibernate(void *data EINA_UNUSED, const char *params EINA_UNUSED) +{ + if (_cmd_hibernate) ecore_exe_run(_cmd_hibernate, NULL); +} + +static void +_power_halt_init(void) +{ +#if defined (__FreeBSD__) || defined (__OpenBSD__) + _cmd_halt = strdup("shutdown -p now"); +#else + if (ecore_file_app_installed("systemctl")) + _cmd_halt = strdup("systemctl poweroff"); + else + _cmd_halt = strdup("shutdown -h now"); +#endif + // linux systemd: PATH/systemctl poweroff + // bsd: /sbin/shutdown -p no + // * : /sbin/shutdown -h now +} + +static void +_power_reboot_init(void) +{ +#if defined (__FreeBSD__) || defined (__OpenBSD__) + _cmd_reboot = strdup("shutdown -r now"); +#else + if (ecore_file_app_installed("systemctl")) + _cmd_reboot = strdup("systemctl reboot"); + else + _cmd_reboot = strdup("shutdown -r now"); +#endif + // linux systemd: PATH/systemctl reboot + // *: /sbin/shutdown -r now +} + +static void +_power_suspend_init(void) +{ +#if defined (__FreeBSD__) || defined (__OpenBSD__) + if (ecore_file_app_installed("zzz")) + _cmd_suspend = strdup("zzz"); +#else + if (ecore_file_app_installed("systemctl")) + _cmd_suspend = strdup("systemctl suspend"); + else if (ecore_file_app_installed("sleep.sh")) + _cmd_suspend = strdup("sleep.sh"); + else if (ecore_file_can_exec("/etc/acpi/sleep.sh")) + _cmd_suspend = strdup("/etc/acpi/sleep.sh force"); + else if (ecore_file_app_installed("pm-suspend")) + _cmd_suspend = strdup("pm-suspend"); + else if (ecore_file_can_exec("/etc/acpi/pm-suspend")) + _cmd_suspend = strdup("/etc/acpi/pm-suspend"); +#endif + // linux systemd: PATH/systemctl suspend + // bsd: /usr/sbin/zzz + // *: + // PATH/sleep.sh + // /etc/acpi/sleep.sh force + // PATH/pm-suspend + // /etc/acpi/pm-suspend +} + +static void +_power_hibernate_init(void) +{ +#if defined (__FreeBSD__) || defined (__OpenBSD__) + if (ecore_file_app_installed("acpiconf")) + _cmd_hibernate = strdup("acpiconf -s4"); +#else + if (ecore_file_app_installed("systemctl")) + _cmd_hibernate = strdup("systemctl hibernate"); + else if (ecore_file_app_installed("hibernate.sh")) + _cmd_hibernate = strdup("hibernate.sh"); + else if (ecore_file_can_exec("/etc/acpi/hibernate.sh")) + _cmd_hibernate = strdup("/etc/acpi/hibernate.sh force"); + else if (ecore_file_app_installed("pm-hibernate")) + _cmd_hibernate = strdup("pm-hibernate"); + else if (ecore_file_can_exec("/etc/acpi/pm-hibernate")) + _cmd_hibernate = strdup("/etc/acpi/pm-hibernate"); +#endif + // linux systemd: PATH/systemctl hibernate + // bsd: acpiconf -s4 + // if exist: + // PATH/hibernate.sh + // /etc/acpi/hibernate.sh force + // PATH/pm-hibernate + // /etc/acpi/pm-hibernate +} + +void +e_system_power_init(void) +{ + _power_halt_init(); + _power_reboot_init(); + _power_suspend_init(); + _power_hibernate_init(); + e_system_inout_command_register("power-halt", _cb_power_halt, NULL); + e_system_inout_command_register("power-reboot", _cb_power_reboot, NULL); + e_system_inout_command_register("power-suspend", _cb_power_suspend, NULL); + e_system_inout_command_register("power-hibernate", _cb_power_hibernate, NULL); +} + +void +e_system_power_shutdown(void) +{ + // only shutdown things we really have to - no need to free mem etc. +} diff --git a/src/bin/system/e_system_rfkill.c b/src/bin/system/e_system_rfkill.c new file mode 100644 index 000000000..645f3780f --- /dev/null +++ b/src/bin/system/e_system_rfkill.c @@ -0,0 +1,176 @@ +#include "e_system.h" + +typedef struct +{ + char *dev; + Ecore_Event_Handler *handle_del, *handle_data; + Ecore_Exe *exe; + int id; + Eina_Bool block : 1; + Eina_Bool doit : 1; +} Action; + +static Eina_Bool +_rfkill_dev_verify(const char *dev) +{ + const char *s; + + for (s = dev; *s; s++) + { + if (!(((*s >= 'a') && (*s <= 'z')) || + ((*s >= 'A') && (*s <= 'Z')) || + ((*s >= '0') && (*s <= '9')) || + (*s == '-') || (*s == '+') || (*s == ',') || (*s == '.') || + (*s == '/') || (*s == ':') || (*s == '=') || (*s == '@'))) + return EINA_FALSE; + } + return EINA_TRUE; +} + +static void +_rfkill_action_free(Action *a) +{ + // a->exe can stay - exe's are auto-freed by ecore on exit +// if (a->exe) ecore_exe_free(a->exe); + if (a->handle_del) ecore_event_handler_del(a->handle_del); + if (a->handle_data) ecore_event_handler_del(a->handle_data); + free(a->dev); + free(a); +} + +static Eina_Bool +_cb_rfkill_exe_del(void *data, int ev_type EINA_UNUSED, void *event) +{ + Action *a = data; + Ecore_Exe_Event_Del *ev = event; + if (ev->exe != a->exe) return EINA_TRUE; + if (a->id >= 0) + { + if (a->doit) + { + if (a->block) + e_system_inout_command_send("rfkill-block", "%i %s", + ev->exit_code, a->dev); + else + e_system_inout_command_send("rfkill-unblock", "%i %s", + ev->exit_code, a->dev); + _rfkill_action_free(a); + } + else + { + char cmd[1024]; + + // a->exe can stay - exe's are auto-freed by ecore on exit + // ecore_exe_free(a->exe); + a->exe = NULL; + if (snprintf(cmd, sizeof(cmd), "rfkill %s %i", + a->block ? "block" : "unblock", a->id) < + (int)(sizeof(cmd) - 1)) + { + a->doit = EINA_TRUE; + a->exe = ecore_exe_pipe_run(cmd, + ECORE_EXE_NOT_LEADER | + ECORE_EXE_TERM_WITH_PARENT | + ECORE_EXE_PIPE_READ | + ECORE_EXE_PIPE_WRITE | + ECORE_EXE_PIPE_READ_LINE_BUFFERED, NULL); + if (!a->exe) _rfkill_action_free(a); + } + else _rfkill_action_free(a); + } + } + else + { + if (a->block) + e_system_inout_command_send("rfkill-block", "%i %s", + 123, a->dev); + else + e_system_inout_command_send("rfkill-unblock", "%i %s", + 123, a->dev); + _rfkill_action_free(a); + } + return EINA_TRUE; +} + +static Eina_Bool +_cb_rfkill_exe_data(void *data, int ev_type EINA_UNUSED, void *event) +{ + Action *a = data; + Ecore_Exe_Event_Data *ev = event; + int i; + + if (ev->exe != a->exe) return EINA_TRUE; + if (a->id >= 0) return EINA_TRUE; + for (i = 0; ev->lines[i].line; i++) + { + int id; + char dev[1024]; + + id = -1; + if (sscanf(ev->lines[i].line, "%i %*s %1023s %*s %*s", &id, dev) == 2) + { + if (!strcmp(a->dev, dev)) + { + a->id = id; + // wait for exit to spawn rfkill again... + break; + } + } + } + return EINA_TRUE; +} + +static void +_rfkill_do(Eina_Bool block, const char *dev) +{ + Action *a = calloc(1, sizeof(Action)); + if (!a) return; + a->dev = strdup(dev); + if (!a->dev) goto err; + a->id = -1; + a->block = block; + a->handle_del = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, + _cb_rfkill_exe_del, a); + a->handle_data = ecore_event_handler_add(ECORE_EXE_EVENT_DATA, + _cb_rfkill_exe_data, a); + if ((!a->handle_del) || (!a->handle_data)) goto err; + // stage 1 - run to get list of devices and id's to know what id to use + a->exe = ecore_exe_pipe_run("rfkill", + ECORE_EXE_NOT_LEADER | + ECORE_EXE_TERM_WITH_PARENT | + ECORE_EXE_PIPE_READ | + ECORE_EXE_PIPE_WRITE | + ECORE_EXE_PIPE_READ_LINE_BUFFERED, NULL); + if (!a->exe) goto err; + return; +err: + ERR("Can't run rfkill\n"); + _rfkill_action_free(a); +} + +static void +_cb_rfkill_block(void *data EINA_UNUSED, const char *params) +{ + if (!_rfkill_dev_verify(params)) return; + _rfkill_do(EINA_TRUE, params); +} + +static void +_cb_rfkill_unblock(void *data EINA_UNUSED, const char *params) +{ + if (!_rfkill_dev_verify(params)) return; + _rfkill_do(EINA_FALSE, params); +} + +void +e_system_rfkill_init(void) +{ + e_system_inout_command_register("rfkill-block", _cb_rfkill_block, NULL); + e_system_inout_command_register("rfkill-unblock", _cb_rfkill_unblock, NULL); +} + +void +e_system_rfkill_shutdown(void) +{ + // only shutdown things we really have to - no need to free mem etc. +} diff --git a/src/bin/system/e_system_storage.c b/src/bin/system/e_system_storage.c new file mode 100644 index 000000000..b58cc419a --- /dev/null +++ b/src/bin/system/e_system_storage.c @@ -0,0 +1,455 @@ +#include "e_system.h" + +typedef struct +{ + char *dev, *mountpoint, *cmd, *cmd2, *cmd1; + Ecore_Exe *exe; + Ecore_Event_Handler *handler; + Eina_Bool eject : 1; + Eina_Bool mount : 1; + Eina_Bool umount : 1; + Eina_Bool in_progress : 1; +} Action; + +static Eina_List *_pending_actions = NULL; + +static Eina_Bool _store_action_do(Action *a); + +static Eina_Bool +_store_device_verify(const char *dev) +{ + const char *s; + struct stat st; + + // not even /dev/something? no way. + if (!(!strncmp(dev, "/dev/", 5))) return EINA_FALSE; + // if it contains /.. - even tho it could be file/..name not file/../name + // it still looks suspicious enough so - no + if (strstr(dev, "/..")) return EINA_FALSE; + // any chars that cold be used in any evil ways - no way. device names + // should not have these... + for (s = dev; *s; s++) + { + if ((*s <= '*') || (*s == '`') || (*s == ';') || (*s == '<') || + (*s == '>') || (*s == '?') || (*s >= '{') || + ((*s >= '[') && (*s <= '^'))) + return EINA_FALSE; + } + // must exist as a file - if not - nope + if (stat(dev, &st) != 0) return EINA_FALSE; + return EINA_TRUE; +} + +static Eina_Bool +_store_uuid_verify(const char *dev) +{ + const char *s; + + if (!(!strncmp(dev, "UUID=", 5))) return EINA_FALSE; + for (s = dev + 5; *s; s++) + { + if ((!isalnum(*s)) && (*s != '-')) return EINA_FALSE; + } + return EINA_TRUE; +} + +static Eina_Bool +_mkdir(const char *path, uid_t u, gid_t g) +{ + mode_t um; + int ret, e; + + um = umask(0); + errno = 0; + ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + e = errno; + umask(um); + if (ret != 0) + { + if (e != EEXIST) + { + ERR("Can't create mount dir [%s]\n", path); + return EINA_FALSE; + } + else + { + struct stat st; + + if (stat(path, &st) == 0) + { + if (!S_ISDIR(st.st_mode)) + { + ERR("Path is not a dir [%s]\n", path); + return EINA_FALSE; + } + } + } + } + if (chown(path, u, g) != 0) + { + ERR("Can't own [%s] to uid.gid %i.%i\n", path, uid, gid); + return EINA_FALSE; + } + return EINA_TRUE; +} + +static Eina_Bool +_store_mount_verify(const char *mnt) +{ + char *tmnt, *p; + const char *s; + struct stat st; + + if (!(!strncmp(mnt, "/media/", 7))) return EINA_FALSE; + for (s = mnt; *s; s++) + { + if (*s == '\\') return EINA_FALSE; + } + if (strstr(mnt, "/..")) return EINA_FALSE; + if (stat(mnt, &st) == 0) + { + if (!S_ISDIR(st.st_mode)) return EINA_FALSE; + if (st.st_uid != uid) return EINA_FALSE; + } + tmnt = strdup(mnt); + if (tmnt) + { + // /media <- owned by root + p = strchr(tmnt + 1, '/'); + if (!p) goto malformed; + *p = '\0'; + if (!_mkdir(tmnt, 0, 0)) goto err; + *p = '/'; + + // /media/username <- owned by uid.gid + p = strchr(p + 1, '/'); + if (!p) goto malformed; + *p = '\0'; + if (!_mkdir(tmnt, uid, gid)) goto err; + *p = '/'; + + // /media/username/dirname <- owned by root + if (!_mkdir(tmnt, uid, gid)) goto err; + free(tmnt); + } + return EINA_TRUE; +malformed: + ERR("Malformed mount point [%s]\n", mnt); +err: + free(tmnt); + return EINA_FALSE; +} + +static Eina_Bool +_store_umount_verify(const char *mnt) +{ + char *tmnt, *p; + const char *s; + struct stat st; + + if (!(!strncmp(mnt, "/media/", 7))) return EINA_FALSE; + for (s = mnt; *s; s++) + { + if (*s == '\\') return EINA_FALSE; + } + if (stat(mnt, &st) != 0) return EINA_FALSE; + if (!S_ISDIR(st.st_mode)) return EINA_FALSE; + tmnt = strdup(mnt); + if (!tmnt) return EINA_FALSE; + p = strchr(tmnt + 8, '/'); + if (!p) goto err; + *p = '\0'; + if (stat(tmnt, &st) != 0) goto err; + if (st.st_uid != uid) goto err; + free(tmnt); + return EINA_TRUE; +err: + free(tmnt); + return EINA_FALSE; +} + +static void +_store_action_free(Action *a) +{ + free(a->dev); + free(a->mountpoint); + free(a->cmd); + free(a->cmd2); + free(a->cmd1); + if (a->handler) ecore_event_handler_del(a->handler); + if (a->exe) ecore_exe_free(a->exe); + free(a); +} + +static void +_store_pending_action_next(void) +{ + while (_pending_actions) + { + Action *a = _pending_actions->data; + if (_store_action_do(a)) break; + else _pending_actions = eina_list_remove(_pending_actions, a); + } +} + +static Eina_Bool +_cb_store_eject_exe_del(void *data, int ev_type EINA_UNUSED, void *event) +{ + Ecore_Exe_Event_Del *ev = event; + Action *a = data; + + if (ev->exe == a->exe) + { + a->exe = NULL; + if (a->eject) + e_system_inout_command_send("store-eject", "%i %s", + ev->exit_code, a->dev); + else if ((a->mount) && + ((ev->exit_code == 0) || + (!((a->cmd) || (a->cmd1) || (a->cmd2))))) + e_system_inout_command_send("store-mount", "%i %s %s", + ev->exit_code, a->dev, a->mountpoint); + else if (a->umount) + { + if (ev->exit_code == 0) + { + rmdir(a->mountpoint); + } + e_system_inout_command_send("store-umount", "%i %s", + ev->exit_code, a->mountpoint); + } + if (((a->cmd) || (a->cmd1) || (a->cmd2)) && (ev->exit_code != 0)) + { + if (!_store_action_do(a)) + { + _pending_actions = eina_list_remove(_pending_actions, a); + _store_action_free(a); + _store_pending_action_next(); + } + } + else + { + _pending_actions = eina_list_remove(_pending_actions, a); + _store_action_free(a); + _store_pending_action_next(); + } + } + return EINA_TRUE; +} + +static Eina_Bool +_store_action_do(Action *a) +{ + a->handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, + _cb_store_eject_exe_del, a); + if (!a->handler) _store_action_free(a); + else + { + if (a->cmd2) + { + a->exe = ecore_exe_run(a->cmd2, NULL); + free(a->cmd2); + a->cmd2 = NULL; + } + else if (a->cmd1) + { + a->exe = ecore_exe_run(a->cmd1, NULL); + free(a->cmd1); + a->cmd1 = NULL; + } + else + { + a->exe = ecore_exe_run(a->cmd, NULL); + free(a->cmd); + a->cmd = NULL; + } + a->in_progress = EINA_TRUE; + if (!a->exe) _store_action_free(a); + else return EINA_TRUE; + } + return EINA_FALSE; +} + +static void +_store_action_queue(Action *a) +{ + if (_pending_actions) + _pending_actions = eina_list_append(_pending_actions, a); + else if (_store_action_do(a)) + _pending_actions = eina_list_append(_pending_actions, a); +} + +static void +_cb_store_eject(void *data EINA_UNUSED, const char *params) +{ + // params: + // /dev/xxx + char cmd0[512]; + char cmd[4096 + 512 + 128]; + + if (!_store_device_verify(params)) + { + ERR("Invalid device [%s]\n", params); + return; + } +#if defined (__FreeBSD__) || defined (__OpenBSD__) + // on bsd cdcontrol is the shnizzle for this + if (ecore_file_app_installed("cdcontrol")) + snprintf(cmd0, sizeof(cmd0), "cdcontrol eject"); + // otherwise regular old eject will do + else +#endif + snprintf(cmd0, sizeof(cmd0), "eject"); + if (snprintf(cmd, sizeof(cmd), "%s %s", cmd0, params) < (int)(sizeof(cmd) - 1)) + { + Action *a = calloc(1, sizeof(Action)); + if (a) + { + a->eject = EINA_TRUE; + a->dev = strdup(params); + a->cmd = strdup(cmd); + if ((!a->dev) || (!a->cmd)) _store_action_free(a); + else _store_action_queue(a); + } + } +} + +static void +_cb_store_mount(void *data EINA_UNUSED, const char *params) +{ + // params: + // /dev/sdc1 - /media/user/dir + // /dev/disk/by-uuid/d9c53a62-7fc2-4cc3-9616-4e41e065da4c - /media/user/dir + // /dev/sdb1 x /media/user/dir + // ... + // DEV ARG MNT + // ARG is - OR one or more of the below chars (-, xs, rx, ...): + // x = noexec + // r = ro + // s = sync + // d = dirsync + // l = lazytime + // a = noatime + // A = relatime + // D = diratime + char dev[1024], arg[128], mnt[4096], opt2[512], opt1[512], opt0[256], *p; + char cmd2[(4096 * 2) + 1024 + 500]; + char cmd1[(4096 * 2) + 1024 + 500]; + char cmd0[(4096 * 2) + 1024 + 500]; + + if (sscanf(params, "%1023s %127s %4095s", dev, arg, mnt) == 3) + { + char *mnt2; + + if ((!_store_device_verify(dev)) && (!_store_uuid_verify(dev))) + { + ERR("Invalid device [%s]\n", dev); + return; + } + if (!_store_mount_verify(mnt)) + { + ERR("Invalid mount [%s]\n", mnt); + return; + } + mnt2 = ecore_file_escape_name(mnt); + if (mnt2) + { + Eina_Bool o_noexec = EINA_FALSE; + Eina_Bool o_ro = EINA_FALSE; + Eina_Bool o_sync = EINA_FALSE; + Eina_Bool o_dirsync = EINA_FALSE; + Eina_Bool o_lazytime = EINA_FALSE; + Eina_Bool o_noatime = EINA_FALSE; + Eina_Bool o_relatime = EINA_FALSE; + Eina_Bool o_diratime = EINA_FALSE; + + for (p = arg; *p; p++) + { + if ((*p) == 'x') o_noexec = EINA_TRUE; + else if ((*p) == 'r') o_ro = EINA_TRUE; + else if ((*p) == 's') o_sync = EINA_TRUE; + else if ((*p) == 'd') o_dirsync = EINA_TRUE; + else if ((*p) == 'l') o_lazytime = EINA_TRUE; + else if ((*p) == 'a') o_noatime = EINA_TRUE; + else if ((*p) == 'A') o_relatime = EINA_TRUE; + else if ((*p) == 'D') o_diratime = EINA_TRUE; + } + snprintf(opt0, sizeof(opt0), + "nosuid,nodev%s%s%s%s%s%s%s%s", + o_noexec ? ",noexec" : "", + o_ro ? ",ro" : "", + o_sync ? ",sync" : "", + o_dirsync ? ",dirsync" : "", + o_lazytime ? ",lazytime" : "", + o_noatime ? ",noatime" : "", + o_relatime ? ",relatime" : "", + o_diratime ? ",diratime" : ""); + snprintf(opt2, sizeof(opt2), "%s,iocharset=utf8,uid=%i", opt0, uid); + snprintf(opt1, sizeof(opt1), "%s,iocharset=utf8", opt0); + // opt2, opt1, opt0 + if ((snprintf(cmd2, sizeof(cmd2), "mount -o %s %s %s", + opt2, dev, mnt2) < (int)(sizeof(cmd2) - 1)) && + (snprintf(cmd1, sizeof(cmd1), "mount -o %s %s %s", + opt1, dev, mnt2) < (int)(sizeof(cmd1) - 1)) && + (snprintf(cmd0, sizeof(cmd0), "mount -o %s %s %s", + opt0, dev, mnt2) < (int)(sizeof(cmd0) - 1))) + { + Action *a = calloc(1, sizeof(Action)); + if (a) + { + a->mount = EINA_TRUE; + a->dev = strdup(dev); + a->mountpoint = strdup(mnt); + a->cmd = strdup(cmd0); + a->cmd2 = strdup(cmd2); + a->cmd1 = strdup(cmd1); + if ((!a->dev) || (!a->mountpoint) || + (!a->cmd) || (!a->cmd2) || (!a->cmd1)) + _store_action_free(a); + else _store_action_queue(a); + } + } + } + free(mnt2); + } +} + +static void +_cb_store_umount(void *data EINA_UNUSED, const char *params) +{ + // params: + // /media/user/xxx + char cmd[4096]; + + if (!_store_umount_verify(params)) + { + ERR("Invalid mount [%s]\n", params); + return; + } + if (snprintf(cmd, sizeof(cmd), "umount %s", params) < (int)(sizeof(cmd) - 1)) + { + Action *a = calloc(1, sizeof(Action)); + if (a) + { + a->umount = EINA_TRUE; + a->mountpoint = strdup(params); + a->cmd = strdup(cmd); + if ((!a->mountpoint) || (!a->cmd)) _store_action_free(a); + else _store_action_queue(a); + } + } +} + +void +e_system_storage_init(void) +{ + e_system_inout_command_register("store-eject", _cb_store_eject, NULL); + e_system_inout_command_register("store-mount", _cb_store_mount, NULL); + e_system_inout_command_register("store-umount", _cb_store_umount, NULL); +} + +void +e_system_storage_shutdown(void) +{ + // only shutdown things we really have to - no need to free mem etc. +} diff --git a/src/bin/system/e_system_test.c b/src/bin/system/e_system_test.c new file mode 100644 index 000000000..edfc85d01 --- /dev/null +++ b/src/bin/system/e_system_test.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include + +typedef struct +{ + char cmd[24]; + int size; +} Message_Head; + +static Ecore_Exe *exe; + +static Eina_Bool +_cb_exe_del(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event EINA_UNUSED) +{ + ecore_main_loop_quit(); + return EINA_TRUE; +} + +static Eina_Binbuf *buf = NULL; + +static Eina_Bool +_message_read(void) +{ + Message_Head *head; + const unsigned char *data = eina_binbuf_string_get(buf); + size_t len = eina_binbuf_length_get(buf); + Eina_Binbuf *buf2; + + if (!data) return EINA_FALSE; + if (len < sizeof(Message_Head)) return EINA_FALSE; + head = (Message_Head *)data; + if (len < (sizeof(Message_Head) + head->size)) return EINA_FALSE; + printf("CMD: [%s]", head->cmd); + if (head->size == 0) printf("\n\n"); + else printf(" [%s]\n\n", data + sizeof(Message_Head)); + buf2 = eina_binbuf_new(); + eina_binbuf_append_length(buf2, + data + sizeof(Message_Head) + head->size, + len - (sizeof(Message_Head) + head->size)); + eina_binbuf_free(buf); + buf = buf2; + return EINA_TRUE; +} + +static Eina_Bool +_cb_exe_data(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event) +{ + Ecore_Exe_Event_Data *ev = event; + + eina_binbuf_append_length(buf, ev->data, ev->size); + while (_message_read()); + return EINA_TRUE; +} + +static void +_cb_input(void *data EINA_UNUSED, Ecore_Thread *th) +{ + char b[4096], *msg = NULL; + + for (;;) + { + while (fgets(b, sizeof(b) - 1, stdin)) + { + b[sizeof(b) - 1] = 0; + size_t len = strlen(b); + for (len = len - 1; len > 0; len--) + { + if (b[len] == '\n') b[len] = '\0'; + else if (b[len] == '\r') b[len] = '\0'; + else break; + } + msg = strdup(b); + if (msg) ecore_thread_feedback(th, msg); + } + } +} + +static void +_cb_input_message(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED, void *msg_data) +{ + char *msg = msg_data; + char *spc, *params = NULL; + Message_Head head; + + head.size = 0; + spc = strchr(msg, ' '); + if (spc) + { + *spc = '\0'; + params = spc + 1; + head.size = strlen(params) + 1; + } + strcpy(head.cmd, msg); + ecore_exe_send(exe, &head, sizeof(Message_Head)); + if (head.size > 0) ecore_exe_send(exe, params, strlen(params) + 1); +} + +static void +_cb_input_end(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED) +{ +} + +static void +_cb_input_cancel(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED) +{ +} + +int +main(int argc, const char **argv) +{ + const char *backend; + + if (argc < 2) + { + printf("use: %s /usr/local/lib/enlightenment/utils/enlightenment_system\n" + " or the path to the enlightenment_system tool wherever it is\n", + argv[0]); + return -1; + } + backend = argv[1]; + + eina_init(); + ecore_init(); + + buf = eina_binbuf_new(); + ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _cb_exe_del, NULL); + ecore_event_handler_add(ECORE_EXE_EVENT_DATA, _cb_exe_data, NULL); + exe = ecore_exe_pipe_run(backend, + ECORE_EXE_NOT_LEADER | + ECORE_EXE_TERM_WITH_PARENT | + ECORE_EXE_PIPE_READ | + ECORE_EXE_PIPE_WRITE, NULL); + ecore_thread_feedback_run(_cb_input, _cb_input_message, + _cb_input_end, _cb_input_cancel, NULL, EINA_TRUE); + + ecore_main_loop_begin(); + + ecore_shutdown(); + eina_shutdown(); + return 0; +} + diff --git a/src/bin/system/meson.build b/src/bin/system/meson.build new file mode 100644 index 000000000..f681e1ace --- /dev/null +++ b/src/bin/system/meson.build @@ -0,0 +1,29 @@ +src = [ + 'e_system_main.c', + 'e_system_inout.c', + 'e_system_backlight.c', + 'e_system_storage.c', + 'e_system_power.c', + 'e_system_rfkill.c', + 'e_system_l2ping.c', + 'e_system_cpufreq.c', + 'e_system.h', +] +executable('enlightenment_system', src, + include_directories: include_directories('../../..'), + dependencies : [ dep_eina, dep_ecore, dep_ecore_file, + dep_m, dep_dl, + dep_eet, dep_eeze, + dep_bluez ], + c_args : suid_cflags, + link_args : suid_ldflags, + install_dir : dir_e_utils, + install : true + ) +suid_exes += join_paths(dir_e_utils, 'enlightenment_system') +executable('enlightenment_system_test', [ 'e_system_test.c' ], + include_directories: include_directories('../../..'), + dependencies : [ dep_eina, dep_ecore ], + install_dir : dir_e_utils, + install : true + )