diff options
Diffstat (limited to 'src/lib/elput/elput_root.c')
-rw-r--r-- | src/lib/elput/elput_root.c | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/lib/elput/elput_root.c b/src/lib/elput/elput_root.c new file mode 100644 index 0000000000..f07d46d0b1 --- /dev/null +++ b/src/lib/elput/elput_root.c @@ -0,0 +1,146 @@ +#include "elput_private.h" +#include <grp.h> +#include <sys/types.h> +#include <pwd.h> + +# ifdef major +# define MAJOR(x) major(x) +# else +# define MAJOR(x) ((((x) >> 8) & 0xfff) | (((x) >> 32) & ~0xfff)) +# endif + +static Eina_Bool +_user_part_of_input(void) +{ + uid_t user = getuid(); + struct passwd *user_pw = getpwuid(user); + gid_t *gids = NULL; + int number_of_groups = 0; + struct group *input_group = getgrnam("input"); + + EINA_SAFETY_ON_NULL_RETURN_VAL(user_pw, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(input_group, EINA_FALSE); + + if (getgrouplist(user_pw->pw_name, getgid(), NULL, &number_of_groups) != -1) + { + ERR("Failed to enumerate groups of user"); + return EINA_FALSE; + } + number_of_groups ++; + gids = alloca((number_of_groups) * sizeof(gid_t)); + if (getgrouplist(user_pw->pw_name, getgid(), gids, &number_of_groups) == -1) + { + ERR("Failed to get groups of user"); + return EINA_FALSE; + } + + for (int i = 0; i < number_of_groups; ++i) + { + if (gids[i] == input_group->gr_gid) + return EINA_TRUE; + } + return EINA_FALSE; +} + +static Eina_Bool +_root_connect(Elput_Manager **manager EINA_UNUSED, const char *seat EINA_UNUSED, unsigned int tty EINA_UNUSED) +{ + Elput_Manager *em; + + em = calloc(1, sizeof(Elput_Manager)); + if (!em) return EINA_FALSE; + + em->interface = &_root_interface; + em->seat = eina_stringshare_add(seat); + + if (!_user_part_of_input()) + { + free(em); + return EINA_FALSE; + } + *manager = em; + return EINA_TRUE; +} + +static void +_root_disconnect(Elput_Manager *em EINA_UNUSED) +{ + //Nothing to do here, there is no data to free +} + +static int +_root_open(Elput_Manager *em EINA_UNUSED, const char *path, int flags) +{ + struct stat st; + int ret, fd = -1; + int fl; + + ret = stat(path, &st); + if (ret < 0) return -1; + + if (!S_ISCHR(st.st_mode)) return -1; + + fd = open(path, flags); + if (fd < 0) return fd; + + if (MAJOR(st.st_rdev) == 226) //DRM_MAJOR + em->drm_opens++; + + fl = fcntl(fd, F_GETFL); + if (fl < 0) goto err; + + if (flags & O_NONBLOCK) + fl |= O_NONBLOCK; + + ret = fcntl(fd, F_SETFL, fl); + if (ret < 0) goto err; + + return fd; +err: + close(fd); + return -1; +} + +static void +_root_open_async(Elput_Manager *em, const char *path, int flags) +{ + int fd = _root_open(em, path, flags); + int ret; + + while (1) + { + ret = write(em->input.pipe, &fd, sizeof(int)); + if (ret < 0) + { + if ((errno == EAGAIN) || (errno == EINTR)) + continue; + WRN("Failed to write to input pipe"); + } + break; + } + close(em->input.pipe); + em->input.pipe = -1; +} + +static void +_root_close(Elput_Manager *em EINA_UNUSED, int fd) +{ + close(fd); +} + +static Eina_Bool +_root_vt_set(Elput_Manager *em EINA_UNUSED, int vt EINA_UNUSED) +{ + //Nothing to do here + return EINA_TRUE; +} + +Elput_Interface _root_interface = +{ + _root_connect, + _root_disconnect, + _root_open, + _root_open_async, + _root_close, + _root_vt_set, +}; |