e system - add new uber setuid tool to replace all the previous ones

so e has had a bit of a mess of setuid root tools to do things that
are essential to maintaing a functioning desktop/laptop/tablet/phone
like device like shutting it down or suspending or sqizzling the cpu
governor or messing with the backlight or... you get the idea.

this has been spread around enlightenment_sys and other special
purpose tools. this now unifies it into a single "always there"
enlightenment_system backend setuid root slave process whose job it is
to do all these things via a stdio protocol in an organized way. this
means latency to do something is lower, but at the expense of
consuming ram and a lurking process. unfortunately the lurking will be
needed soon when i add ddc support to make it even vaguely efficient,
so it's a cost i guess we have to pay now. we'll need this in future
as well for some stats collection and more.

still need to port existing code to use this instead of the existing stuff,
and then remove of the old stuff.
enlightenment-0.24
Carsten Haitzler 3 years ago
parent dda50e8d87
commit f57572b678
  1. 3
      data/etc/meson.build
  2. 67
      data/etc/system.conf
  3. 7
      meson.build
  4. 1
      src/bin/e_includes.h
  5. 9
      src/bin/e_main.c
  6. 320
      src/bin/e_system.c
  7. 15
      src/bin/e_system.h
  8. 3
      src/bin/meson.build
  9. 121
      src/bin/system/e_system.h
  10. 321
      src/bin/system/e_system_backlight.c
  11. 163
      src/bin/system/e_system_cpufreq.c
  12. 171
      src/bin/system/e_system_inout.c
  13. 229
      src/bin/system/e_system_l2ping.c
  14. 303
      src/bin/system/e_system_main.c
  15. 134
      src/bin/system/e_system_power.c
  16. 176
      src/bin/system/e_system_rfkill.c
  17. 455
      src/bin/system/e_system_storage.c
  18. 146
      src/bin/system/e_system_test.c
  19. 29
      src/bin/system/meson.build

@ -86,3 +86,6 @@ if get_option('install-enlightenment-menu')
)
endif
install_data('system.conf',
install_dir: join_paths(dir_sysconf, 'enlightenment')
)

@ -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: *

@ -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

@ -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"

@ -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())
{

@ -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<br>"
"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);
}
}

@ -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

@ -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')

@ -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 <stdlib.h>
# include <stddef.h>
# else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
# endif
# ifdef HAVE_ALLOCA_H
# include <alloca.h>
# elif !defined alloca
# ifdef __GNUC__
# define alloca __builtin_alloca
# elif defined _AIX
# define alloca __alloca
# elif defined _MSC_VER
# include <malloc.h>
# define alloca _alloca
# elif !defined HAVE_ALLOCA
# ifdef __cplusplus
extern "C"
# endif
void *alloca (size_t);
# endif
# endif
# ifdef __linux__
# include <features.h>
# endif
# ifdef HAVE_ENVIRON
# define _GNU_SOURCE 1
# endif
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/time.h>
# include <sys/param.h>
# include <sys/resource.h>
# include <utime.h>
# include <dlfcn.h>
# include <math.h>
# include <fcntl.h>
# include <fnmatch.h>
# include <limits.h>
# include <ctype.h>
# include <time.h>
# include <dirent.h>
# include <pwd.h>
# include <grp.h>
# include <glob.h>
# include <locale.h>
# include <errno.h>
# include <signal.h>
# include <inttypes.h>
# include <assert.h>
# include <fcntl.h>
#if defined (__FreeBSD__) || defined (__OpenBSD__)
# include <sys/sysctl.h>
#endif
#ifdef HAVE_PRCTL
# include <sys/prctl.h>
#elif defined(HAVE_PROCCTL)
# include <sys/procctl.h>
#endif
# ifndef _POSIX_HOST_NAME_MAX
# define _POSIX_HOST_NAME_MAX 255
# endif
# include <Eina.h>
# include <Ecore.h>
# include <Ecore_File.h>
# include <Eet.h>
# ifdef HAVE_EEZE
# include <Eeze.h>
# 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

@ -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.
}

@ -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.
}

@ -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);
}

@ -0,0 +1,229 @@
#include "e_system.h"
#ifdef HAVE_BLUETOOTH
# include <bluetooth/bluetooth.h>
# include <bluetooth/l2cap.h>
# include <sys/socket.h>
# include <sys/select.h>
#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;
}