diff --git a/configure.ac b/configure.ac index f58f7e5c..37654211 100644 --- a/configure.ac +++ b/configure.ac @@ -87,9 +87,9 @@ AC_PROG_INSTALL AM_GNU_GETTEXT_VERSION(0.15) AM_GNU_GETTEXT([external]) -AC_CHECK_HEADERS(ctype.h fcntl.h locale.h signal.h string.h stdio.h stdlib.h) -AC_CHECK_HEADERS(unistd.h sys/stat.h sys/select.h sys/socket.h sys/time.h) -AC_CHECK_HEADERS(sys/wait.h) +AC_CHECK_HEADERS(ctype.h fcntl.h grp.h locale.h pwd.h signal.h string.h) +AC_CHECK_HEADERS(stdio.h stdlib.h unistd.h sys/stat.h sys/select.h) +AC_CHECK_HEADERS(sys/socket.h sys/time.h sys/wait.h) # AC_HEADER_TIME # AC_TYPE_SIGNAL diff --git a/obt/paths.c b/obt/paths.c index d99659b8..f723ae2c 100644 --- a/obt/paths.c +++ b/obt/paths.c @@ -28,6 +28,15 @@ #ifdef HAVE_STRING_H # include #endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_GRP_H +# include +#endif +#ifdef HAVE_PWD_H +# include +#endif struct _ObtPaths { @@ -38,6 +47,11 @@ struct _ObtPaths GSList *config_dirs; GSList *data_dirs; GSList *autostart_dirs; + GSList *exec_dirs; + + uid_t uid; + gid_t *gid; + guint n_gid; }; static gint slist_path_cmp(const gchar *a, const gchar *b) @@ -76,6 +90,33 @@ static GSList* split_paths(const gchar *paths) return list; } +static void find_uid_gid(uid_t *u, gid_t **g, guint *n) +{ + struct passwd *pw; + const gchar *name; + struct group *gr; + + *u = getuid(); + pw = getpwuid(*u); + name = pw->pw_name; + + *g = g_new(gid_t, *n=1); + (*g)[0] = getgid(); + + while ((gr = getgrent())) { + if (gr->gr_gid != (*g)[0]) { /* skip the main group */ + gchar **c; + for (c = gr->gr_mem; *c; ++c) + if (strcmp(*c, name) == 0) { + *g = g_renew(gid_t, *g, ++(*n)); /* save the group */ + (*g)[*n-1] = gr->gr_gid; + break; + } + } + } + endgrent(); +} + ObtPaths* obt_paths_new(void) { ObtPaths *p; @@ -85,6 +126,8 @@ ObtPaths* obt_paths_new(void) p = g_slice_new0(ObtPaths); p->ref = 1; + find_uid_gid(&p->uid, &p->gid, &p->n_gid); + path = g_getenv("XDG_CONFIG_HOME"); if (path && path[0] != '\0') /* not unset or empty */ p->config_home = g_build_filename(path, NULL); @@ -147,6 +190,13 @@ ObtPaths* obt_paths_new(void) p->data_dirs = slist_path_add(p->data_dirs, g_strdup(p->data_home), (GSListFunc) g_slist_prepend); + + path = g_getenv("PATH"); + if (path && path[0] != '\0') /* not unset or empty */ + p->exec_dirs = split_paths(path); + else + p->exec_dirs = NULL; + return p; } @@ -262,3 +312,40 @@ GSList* obt_paths_autostart_dirs(ObtPaths *p) { return p->autostart_dirs; } + +static inline gboolean try_exec(const ObtPaths *const p, + const gchar *const path) +{ + struct stat st; + guint i; + + stat(path, &st); + + if (!S_ISREG(st.st_mode)) + return FALSE; + if (st.st_uid == p->uid) + return st.st_mode & S_IXUSR; + for (i = 0; i < p->n_gid; ++i) + if (st.st_gid == p->gid[i]) + return st.st_mode & S_IXGRP; + return st.st_mode & S_IXOTH; +} + +gboolean obt_paths_try_exec(ObtPaths *p, const gchar *path) +{ + if (path[0] == '/') { + return try_exec(p, path); + } + else { + GSList *it; + + for (it = p->exec_dirs; it; it = g_slist_next(it)) { + gchar *f = g_strdup_printf(it->data, G_DIR_SEPARATOR_S, path); + gboolean e = try_exec(p, f); + g_free(f); + if (e) return TRUE; + } + } + + return FALSE; +} diff --git a/obt/paths.h b/obt/paths.h index 6d6df5e7..7c43682d 100644 --- a/obt/paths.h +++ b/obt/paths.h @@ -40,6 +40,11 @@ gchar *obt_paths_expand_tilde(const gchar *f); gboolean obt_paths_mkdir(const gchar *path, gint mode); gboolean obt_paths_mkdir_path(const gchar *path, gint mode); +/*! Returns TRUE if the @path points to an executable file. + If the @path is not an absolute path, then it is searched for in $PATH. +*/ +gboolean obt_paths_try_exec(ObtPaths *p, const gchar *path); + G_END_DECLS #endif