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 + )