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