diff options
-rw-r--r-- | src/Makefile_Eina.am | 9 | ||||
-rw-r--r-- | src/lib/eina/Eina.h | 1 | ||||
-rw-r--r-- | src/lib/eina/eina_main.c | 3 | ||||
-rw-r--r-- | src/lib/eina/eina_vpath.c | 349 | ||||
-rw-r--r-- | src/lib/eina/eina_vpath.h | 74 | ||||
-rw-r--r-- | src/tests/eina/eina_suite.c | 1 | ||||
-rw-r--r-- | src/tests/eina/eina_suite.h | 1 | ||||
-rw-r--r-- | src/tests/eina/eina_test_vpath.c | 53 |
8 files changed, 488 insertions, 3 deletions
diff --git a/src/Makefile_Eina.am b/src/Makefile_Eina.am index 186bd1e8e1..33c7c660c2 100644 --- a/src/Makefile_Eina.am +++ b/src/Makefile_Eina.am @@ -107,7 +107,8 @@ lib/eina/eina_slice.h \ lib/eina/eina_inline_slice.x \ lib/eina/eina_inline_modinfo.x \ lib/eina/eina_freeq.h \ -lib/eina/eina_slstr.h +lib/eina/eina_slstr.h \ +lib/eina/eina_vpath.h lib_eina_libeina_la_SOURCES = \ @@ -183,7 +184,8 @@ lib/eina/eina_quaternion.c \ lib/eina/eina_bezier.c \ lib/eina/eina_safepointer.c \ lib/eina/eina_freeq.c \ -lib/eina/eina_slstr.c +lib/eina/eina_slstr.c \ +lib/eina/eina_vpath.c if HAVE_WIN32 @@ -356,7 +358,8 @@ tests/eina/eina_test_bezier.c \ tests/eina/eina_test_safepointer.c \ tests/eina/eina_test_slice.c \ tests/eina/eina_test_freeq.c \ -tests/eina/eina_test_slstr.c +tests/eina/eina_test_slstr.c \ +tests/eina/eina_test_vpath.c tests_eina_eina_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ -DTESTS_WD=\"`pwd`\" \ diff --git a/src/lib/eina/Eina.h b/src/lib/eina/Eina.h index 256de025bc..b403d308f8 100644 --- a/src/lib/eina/Eina.h +++ b/src/lib/eina/Eina.h @@ -274,6 +274,7 @@ extern "C" { #include <eina_slstr.h> #include <eina_debug.h> #include <eina_promise.h> +#include <eina_vpath.h> #undef EAPI #define EAPI diff --git a/src/lib/eina/eina_main.c b/src/lib/eina/eina_main.c index 1a152d0da7..275e4a0baa 100644 --- a/src/lib/eina/eina_main.c +++ b/src/lib/eina/eina_main.c @@ -68,6 +68,7 @@ #include "eina_evlog.h" #include "eina_freeq.h" #include "eina_slstr.h" +#include "eina_vpath.h" /*============================================================================* * Local * @@ -155,6 +156,7 @@ EAPI Eina_Inlist *_eina_tracking = NULL; S(safepointer); S(slstr); S(promise); + S(vpath); #undef S struct eina_desc_setup @@ -202,6 +204,7 @@ static const struct eina_desc_setup _eina_desc_setup[] = { S(safepointer), S(slstr), S(promise), + S(vpath), #undef S }; static const size_t _eina_desc_setup_len = sizeof(_eina_desc_setup) / diff --git a/src/lib/eina/eina_vpath.c b/src/lib/eina/eina_vpath.c new file mode 100644 index 0000000000..af57afae97 --- /dev/null +++ b/src/lib/eina/eina_vpath.c @@ -0,0 +1,349 @@ +#include <Eina.h> + +#include "eina_private.h" + +static Eina_Hash *vpath_data = NULL; + +#ifdef CRI +#undef CRI +#endif +#define CRI(...) EINA_LOG_DOM_CRIT(_eina_vpath_log_dom, __VA_ARGS__) + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_eina_vpath_log_dom, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_eina_vpath_log_dom, __VA_ARGS__) + +static int _eina_vpath_log_dom = -1; + +static inline void +_eina_vpath_data_add(const char *key, const char *value) +{ + eina_hash_add(vpath_data, key, eina_stringshare_add(value)); +} + +static inline Eina_Stringshare* +_eina_vpath_data_get(const char *key) +{ + return eina_hash_find(vpath_data, key); +} + + +static char* +_fallback_runtime_dir(const char *home) +{ + char buf[PATH_MAX]; + struct stat st; + +#if defined(HAVE_GETUID) && defined(HAVE_GETEUID) + if (setuid(geteuid()) != 0) + { + fprintf(stderr, + "FATAL: Cannot setuid - errno=%i\n", + errno); + abort(); + } +#endif + // fallback - make ~/.run + snprintf(buf, sizeof(buf), "%s/.run", home); + if (!!mkdir(buf, S_IRUSR | S_IWUSR | S_IXUSR)) + { + if (errno == EEXIST) + { + if (stat(buf, &st) == 0) + { + // some sanity checks - but not for security + if (!(st.st_mode & S_IFDIR)) + { + // fatal - exists but is not a dir + fprintf(stderr, + "FATAL: run dir '%s' exists but not a dir\n", + buf); + abort(); + } +#if defined(HAVE_GETUID) && defined(HAVE_GETEUID) + if (st.st_uid != geteuid()) + { + // fatal - run dir doesn't belong to user + fprintf(stderr, + "FATAL: run dir '%s' not owned by uid %i\n", + buf, (int)geteuid()); + abort(); + } +#endif + } + else + { + // fatal - we cant create our run dir in ~/ + fprintf(stderr, + "FATAL: Cannot verify run dir '%s' errno=%i\n", + buf, errno); + abort(); + } + } + else + { + // fatal - we cant create our run dir in ~/ + fprintf(stderr, + "FATAL: Cannot create run dir '%s' - errno=%i\n", + buf, errno); + abort(); + } + } +#if defined(HAVE_GETUID) && defined(HAVE_GETEUID) + if (setreuid(uid, geteuid()) != 0) + { + fprintf(stderr, + "FATAL: Cannot setreuid - errno=%i\n", + errno); + abort(); + } +#endif + + return strdup(buf); +} + +static char* +_fallback_home_dir() +{ + char buf[PATH_MAX]; + /* Windows does not have getuid(), but home can't be NULL */ +#ifdef HAVE_GETEUID + uid_t uid = geteuid(); + struct stat st; + + snprintf(buf, sizeof(buf), "/tmp/%i", (int)uid); + if (mkdir(buf, S_IRUSR | S_IWUSR | S_IXUSR) < 0) + { + if (errno != EEXIST) + { + if (stat("/tmp", &st) == 0) + snprintf(buf, sizeof(buf), "/tmp"); + else + snprintf(buf, sizeof(buf), "/"); + } + } + if (stat(buf, &st) != 0) + { + if (stat("/tmp", &st) == 0) + snprintf(buf, sizeof(buf), "/tmp"); + else + snprintf(buf, sizeof(buf), "/"); + } +#else + snprintf(buf, sizeof(buf), "/"); +#endif + return strdup(buf); +} + +static void +_eina_vpath_interface_sys_init(void) +{ + const char *home, *tmp; + + // $HOME / ~/ etc. + home = eina_environment_home_get(); + if (!home) + home = _fallback_home_dir(); + + // tmp dir - system wide + tmp = eina_environment_tmp_get(); + + _eina_vpath_data_add("home", home); + _eina_vpath_data_add("tmp", tmp); +} + +Eina_Bool +eina_vpath_init(void) +{ + vpath_data = eina_hash_string_superfast_new((Eina_Free_Cb)eina_stringshare_del); + + _eina_vpath_interface_sys_init(); + + _eina_vpath_log_dom = eina_log_domain_register("vpath", "cyan"); + return EINA_TRUE; +} + +Eina_Bool +eina_vpath_shutdown(void) +{ + eina_log_domain_unregister(_eina_vpath_log_dom); + _eina_vpath_log_dom = -1; + return EINA_TRUE; +} + +EAPI char* +eina_vpath_resolve(const char* path) +{ + // XXX: implement parse of path then look up in hash if not just create + // object where path and result are the same and return that with + // path set and result set to resolved path - return obj handler calls + // "do" on object to get the result inside fetched or failed callback. + // if it's a url then we need a new classs that overrides the do and + // begins a fetch and on finish calls the event cb or when wait is called + /* FIXME: not working for WIndows */ + // /* <- full path + + if (!path) return NULL; + + if (path[0] == '~') + { + // ~/ <- home directory + if (path[1] == '/') + { + char buf[PATH_MAX]; + const char *home = eina_hash_find(vpath_data, "home"); + + if (home) + { + snprintf(buf, sizeof(buf), "%s%s", home, path + 1); + return strdup(buf); + } + } +#ifdef HAVE_GETPWENT + // ~username/ <- homedir of user "username" + else + { + const char *p; + struct passwd pwent, *pwent2 = NULL; + char *name, buf[PATH_MAX], pwbuf[8129]; + + for (p = path + 1; *p; p++) + { + if (*p =='/') break; + } + name = alloca(p - path); + strncpy(name, path + 1, p - path - 1); + name[p - path - 1] = 0; + if (!getpwnam_r(name, &pwent, pwbuf, sizeof(pwbuf), &pwent2)) + { + if ((pwent2) && (pwent.pw_dir)) + { + return strdup(buf); + } + } + } +#endif /* HAVE_GETPWENT */ + return NULL; + } + // (:xxx:)/* ... <- meta hash table + else if ((path[0] == '(') && (path[1] == ':')) + { + const char *p, *end, *meta; + char *name, buf[PATH_MAX]; + int max_len = strlen(path); + Eina_Bool found = EINA_FALSE; + + for (p = path + 2; p <= path + max_len - 2; p++) + { + if ((p[0] ==':') && (p[1] == ')')) + { + end = p; + found = EINA_TRUE; + break; + } + } + p += 2; + + if (!found) + { + ERR("(: Needs to have a matching ':)'\nThe string was: %s", path); + return NULL; + } + + if (*p != '/') + { + ERR("A / is expected after :)\nThe string was: %s", path); + return NULL; + } + + if (found) + { + name = alloca(end - path); + strncpy(name, path + 2, end - path - 2); + name[end - path - 2] = 0; + meta = _eina_vpath_data_get(name); + if (meta) + { + snprintf(buf, sizeof(buf), "%s%s", meta, end + 2); + return strdup(buf); + } + else + { + ERR("Meta key '%s' was not registered!\nThe string was: %s", name, path); + return NULL; + } + } + } + //just return the path, since we assume that this is a normal path + else + { + return strdup(path); + } + + ERR("The path has to start with either '~/' or '(:NAME:)/' or be a normal path \nThe string was: %s", path); + + return NULL; +} + +EAPI void +eina_vpath_interface_app_set(const char *app_domain, Eina_Prefix *app_pfx) +{ + char buf[PATH_MAX]; + + EINA_SAFETY_ON_NULL_RETURN(app_domain); + EINA_SAFETY_ON_NULL_RETURN(app_pfx); + + _eina_vpath_data_add("app.dir", eina_prefix_get(app_pfx)); + _eina_vpath_data_add("app.bin", eina_prefix_bin_get(app_pfx)); + _eina_vpath_data_add("app.lib", eina_prefix_lib_get(app_pfx)); + _eina_vpath_data_add("app.data", eina_prefix_data_get(app_pfx)); + _eina_vpath_data_add("app.locale", eina_prefix_locale_get(app_pfx)); + snprintf(buf, sizeof(buf), "%s/%s", + _eina_vpath_data_get("config"), app_domain); + _eina_vpath_data_add("app.config", buf); + snprintf(buf, sizeof(buf), "%s/%s", + _eina_vpath_data_get("cache"), app_domain); + _eina_vpath_data_add("app.cache", buf); + snprintf(buf, sizeof(buf), "%s/%s", + _eina_vpath_data_get("data"), app_domain); + _eina_vpath_data_add("app.local", buf); +} + +EAPI void +eina_vpath_interface_user_set(Eina_Vpath_Interface_User *user) +{ + char buf[PATH_MAX]; + Eina_Bool free_run = EINA_FALSE; + + EINA_SAFETY_ON_NULL_RETURN(user); + + if (!user->run) + { + user->run = _fallback_runtime_dir(_eina_vpath_data_get("home")); + free_run = EINA_TRUE; + } + +#define ADD(a) _eina_vpath_data_add("usr." #a , user->a) + ADD(desktop); + ADD(documents); + ADD(downloads); + ADD(music); + ADD(pictures); + ADD(pub); + ADD(templates); + ADD(videos); + ADD(data); + ADD(config); + ADD(cache); + ADD(run); +#undef ADD + + if (free_run) + free((char*)user->run); +} diff --git a/src/lib/eina/eina_vpath.h b/src/lib/eina/eina_vpath.h new file mode 100644 index 0000000000..40adffeca1 --- /dev/null +++ b/src/lib/eina/eina_vpath.h @@ -0,0 +1,74 @@ +#ifndef EINA_VPATH_H +#define EINA_VPATH_H + +#include "eina_prefix.h" + +/* + * Eina_vpath + * eina vpath is a path that can be prefixed with a virtual path. + * + * A virutla path can either start with (:XXXXXXXX:) that indicates a virtual path, OR normal with / or ./ or ../ or ~ + * The char sequence in between (: and :) are used as key to lookup the real value. + * The key has to be set by a interface before, otherwise you will get a error. + * + * The symbol ~ is interpretated as the home directory of the running user, and will be replaced. + * Additional infos: https://phab.enlightenment.org/w/eina_vpath/ + */ + +/** + * This datatype is a vpath, this means you can use the syntax described above. + */ +typedef const char* Eina_Vpath; + + +typedef struct +{ + const char *desktop; + const char *documents; + const char *downloads; + const char *music; + const char *pictures; + const char *pub; + const char *templates; + const char *videos; + const char *data; + const char *config; + const char *cache; + const char *run; +} Eina_Vpath_Interface_User; + +/** + * Make the app specific paths accessable as virtual path + * + * This will create : + * - app.dir + * - app.bin + * - app.lib + * - app.data + * - app.locale + * - app.config + * - app.cache + * - app.local + * + * If you do NOT call this api the virtual paths for app.* will be unset + */ +EAPI void eina_vpath_interface_app_set(const char *app_name, Eina_Prefix *p); + +/** + * Create the desktop specific vpaths + * + * The virtual paths will be named usr.<field-name-of-struct> + * + * If you do NOT call this api the virtual paths for usr.* will be unset. + */ +EAPI void eina_vpath_interface_user_set(Eina_Vpath_Interface_User *user); + +/* + * Translate a virtual path into a normal path. + * + * @return a string that is not virtual anymore + * + */ +EAPI char* eina_vpath_resolve(Eina_Vpath path); + +#endif diff --git a/src/tests/eina/eina_suite.c b/src/tests/eina/eina_suite.c index 8e833356f3..bd3bf42a4e 100644 --- a/src/tests/eina/eina_suite.c +++ b/src/tests/eina/eina_suite.c @@ -87,6 +87,7 @@ static const Efl_Test_Case etc[] = { { "Free Queue", eina_test_freeq }, { "Util", eina_test_util }, { "Short Lived Strings", eina_test_slstr }, + { "Vpath", eina_test_vpath }, { NULL, NULL } }; diff --git a/src/tests/eina/eina_suite.h b/src/tests/eina/eina_suite.h index 7bf643e478..9b8351bd25 100644 --- a/src/tests/eina/eina_suite.h +++ b/src/tests/eina/eina_suite.h @@ -74,5 +74,6 @@ void eina_test_safepointer(TCase *tc); void eina_test_slice(TCase *tc); void eina_test_freeq(TCase *tc); void eina_test_slstr(TCase *tc); +void eina_test_vpath(TCase *tc); #endif /* EINA_SUITE_H_ */ diff --git a/src/tests/eina/eina_test_vpath.c b/src/tests/eina/eina_test_vpath.c new file mode 100644 index 0000000000..b09d2d5c9a --- /dev/null +++ b/src/tests/eina/eina_test_vpath.c @@ -0,0 +1,53 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <Eina.h> +#include <check.h> + + +START_TEST(eina_test_vpath_valid) +{ + int ret; + char test[PATH_MAX]; + + ret = eina_init(); + ck_assert_int_ne(ret, 0); + + ck_assert_str_eq(eina_vpath_resolve("/"), "/"); + ck_assert_str_eq(eina_vpath_resolve("./"), "./"); + ck_assert_str_eq(eina_vpath_resolve("..bla"), "..bla"); + ck_assert_str_eq(eina_vpath_resolve(".bla"), ".bla"); + + snprintf(test, sizeof(test), "%s/", eina_environment_home_get()); + ck_assert_str_eq(eina_vpath_resolve("~/"), test); + + snprintf(test, sizeof(test), "%s/bla", eina_environment_home_get()); + ck_assert_str_eq(eina_vpath_resolve("(:home:)/bla"), test); + + ret = eina_shutdown(); +} +END_TEST + +START_TEST(eina_test_vpath_invalid) +{ + int ret; + + ret = eina_init(); + ck_assert_int_ne(ret, 0); + + ck_assert_ptr_null(eina_vpath_resolve("(:asdfasdfafasdf")); + ck_assert_ptr_null(eina_vpath_resolve("(:missing_slash:)")); + ck_assert_ptr_null(eina_vpath_resolve("(:")); + ck_assert_ptr_null(eina_vpath_resolve("(:home:)")); + ck_assert_ptr_null(eina_vpath_resolve("(:wrong_meta_key:)/")); + + ret = eina_shutdown(); +} +END_TEST + +void eina_test_vpath(TCase *tc) +{ + tcase_add_test(tc, eina_test_vpath_invalid); + tcase_add_test(tc, eina_test_vpath_valid); +} |