diff options
author | Karol Lewandowski <k.lewandowsk@samsung.com> | 2022-11-25 12:45:52 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@review> | 2022-11-25 12:45:52 +0000 |
commit | 138e9c0430f3b2b54bb5530e1c6b6c8f1fd54797 (patch) | |
tree | 3abfba51d7813e30e5eb5a848f7b38cb6ec1a4e6 | |
parent | 2e2eb65eedbbdcd62122884b9207c3a0b758906c (diff) | |
parent | db46fedbf63132884ad7ebb473b6a1a814b0e72c (diff) | |
download | sessiond-138e9c0430f3b2b54bb5530e1c6b6c8f1fd54797.tar.gz sessiond-138e9c0430f3b2b54bb5530e1c6b6c8f1fd54797.tar.bz2 sessiond-138e9c0430f3b2b54bb5530e1c6b6c8f1fd54797.zip |
Merge "Reimplement `subsession_get_user_list` by performing subsession directory interrogation directly whenever possible" into tizen
-rw-r--r-- | src/library/src/lib.c | 140 | ||||
-rw-r--r-- | tests/api_tests/test_api_get_user_list.cpp | 15 |
2 files changed, 137 insertions, 18 deletions
diff --git a/src/library/src/lib.c b/src/library/src/lib.c index 94b4459..6b494e0 100644 --- a/src/library/src/lib.c +++ b/src/library/src/lib.c @@ -24,6 +24,10 @@ #include <gio/gio.h> #include <tizen.h> #include <ctype.h> +#include <unistd.h> +#include <sys/types.h> +#include <pwd.h> +#include <dirent.h> #undef LOG_TAG #define LOG_TAG "LIBSESSIOND" @@ -34,6 +38,7 @@ #include "sessiond-internal.h" const int libsessiond_default_timeout = 20000; +static const char subsession_subdir[] = "/subsession"; static session_connection_data_t session_connection_data = { .connection = NULL, @@ -843,40 +848,147 @@ EXPORT_API int subsession_event_wait_done(subsession_event_info info) return SUBSESSION_ERROR_INVALID_PARAMETER; } -EXPORT_API int subsession_get_user_list(int session_uid, subsession_user_t **user_list, int *user_count) +static inline void free_ptr(const void *ptr) { - return_if(session_uid_is_not_valid(session_uid, user_count_ptr_is_null(user_count))) + free(*(void * const *)ptr); +} +static int query_dir_via_dbus(int session_uid, subsession_user_t **const user_list, int *const user_count) +{ g_autoptr(GVariant) out = NULL; - int ret = method_call_sync(dbus_method_call.GetUserList, - g_variant_new("(i)", session_uid), - G_VARIANT_TYPE("(as)"), - &out - ); - + const int ret = method_call_sync(dbus_method_call.GetUserList, + g_variant_new("(i)", session_uid), + G_VARIANT_TYPE("(as)"), + &out + ); if (ret != SUBSESSION_ERROR_NONE) return ret; g_autoptr(GVariant) array = g_variant_get_child_value(out, 0); + if (!array) + return SUBSESSION_ERROR_OUT_OF_MEMORY; + gsize elem_no = 0; - g_autofree const char** data = g_variant_get_strv(array, &elem_no); + g_autofree const char **data = g_variant_get_strv(array, &elem_no); if (user_list != NULL && elem_no > 0) { - subsession_user_t *list = calloc(elem_no, sizeof(subsession_user_t)); - if (list == NULL) { + subsession_user_t *const list = calloc(elem_no, sizeof *list); + if (list == NULL) return SUBSESSION_ERROR_OUT_OF_MEMORY; - } + *user_list = list; - for (gsize i = 0; i < elem_no; ++i) { + for (gsize i = 0; i < elem_no; ++i) subsession_user_copy((*user_list)[i], data[i]); - } } *user_count = elem_no; return SUBSESSION_ERROR_NONE; } +/* A twin implementation of the same logic in the daemon can be found + * in the `src/service/src/fs_helpers.cpp` file, + * `get_user_list(const int session_uid)` function + */ +static int query_dir_via_readdir(DIR *const dir_ptr, subsession_user_t **const user_list, int *const user_count) +{ + struct dirent *entry_ptr; + size_t elems = 1; // because of SUBSESSION_INITIAL_SID + + if (dir_ptr != NULL) + while ((entry_ptr = readdir(dir_ptr)) != NULL) + if (!error_on_bad_user_id(entry_ptr->d_name)) + ++elems; + + *user_count = 1; + + subsession_user_t *const list = calloc(elems, sizeof *list); + if (list == NULL) + return SUBSESSION_ERROR_OUT_OF_MEMORY; + if (user_list != NULL) { + *user_list = list; + subsession_user_copy((*user_list)[0], SUBSESSION_INITIAL_SID); + } + + if (elems == 1) + return SUBSESSION_ERROR_NONE; + + rewinddir(dir_ptr); + + size_t dir_idx = 1; + while ((entry_ptr = readdir(dir_ptr)) != NULL && dir_idx < elems) { + if (!error_on_bad_user_id(entry_ptr->d_name)) { + if (user_list != NULL) + subsession_user_copy((*user_list)[dir_idx], entry_ptr->d_name); + dir_idx++; + } + } + + /* During the second iteration it's possible to get a different number + * of directories than in the first one and - in case of less - we'll + * eventually end up with some empty strings ("\0"s) in the `user_list`. + * This of course is undesirable. Therefore, we set `user_count` to + * the number of read directories during the second iteration. + */ + *user_count = dir_idx; + + return SUBSESSION_ERROR_NONE; +} + +EXPORT_API int subsession_get_user_list(int session_uid, subsession_user_t **user_list, int *user_count) +{ + return_if(session_uid_is_not_valid(session_uid, user_count_ptr_is_null(user_count))) + + const long int max_buf_len = sysconf(_SC_GETPW_R_SIZE_MAX); + if (max_buf_len <= 0) + return SUBSESSION_ERROR_IO_ERROR; + + __attribute__((cleanup(free_ptr))) char *const pw_buf = (char *)malloc(max_buf_len); + if (pw_buf == NULL) + return SUBSESSION_ERROR_OUT_OF_MEMORY; + + struct passwd pass_buf, *pass_ptr; + getpwuid_r(session_uid, &pass_buf, pw_buf, max_buf_len, &pass_ptr); + if (!pass_ptr) + return SUBSESSION_ERROR_IO_ERROR; + + const size_t home_dir_len = strlen(pass_ptr->pw_dir); + const size_t main_dir_len = home_dir_len + sizeof subsession_subdir; + __attribute__((cleanup(free_ptr))) char *main_dir = (char*)malloc(main_dir_len); + if (main_dir == NULL) + return SUBSESSION_ERROR_OUT_OF_MEMORY; + + memcpy(main_dir, pass_ptr->pw_dir, main_dir_len - 1); + memcpy(main_dir + home_dir_len, subsession_subdir, main_dir_len - home_dir_len); + + DIR *const dir_ptr = opendir(main_dir); + if (dir_ptr == NULL) { + /* The DBus service has permissions to access the dir. + * Note that it does the same thing we do, but it's + * better to attempt to do it locally because that's + * faster and getting the user list is often done + * in time-sensitive contexts (such as at boot). */ + if (errno == EACCES) + return query_dir_via_dbus(session_uid, user_list, user_count); + + /* ENOENT here means we have permission to access the dir, + * but the `subsession` subdirectory does not exist. This in + * turn means that there are currently no subsessions in the + * system for the queried `session_uid` and this case will be + * handled later by the `query_dir_via_readdir` function. + * + * Any other system error here means that input parameters are + * invalid. + */ + if (errno != ENOENT) + return SUBSESSION_ERROR_INVALID_PARAMETER; + } + + const int ret = query_dir_via_readdir(dir_ptr, user_list, user_count); + closedir(dir_ptr); + return ret; +} + EXPORT_API int subsession_get_current_user(int session_uid, subsession_user_t user) { return_if(session_uid_is_not_valid(session_uid, current_user_ptr_is_null(user))) diff --git a/tests/api_tests/test_api_get_user_list.cpp b/tests/api_tests/test_api_get_user_list.cpp index eebcf39..3351bfa 100644 --- a/tests/api_tests/test_api_get_user_list.cpp +++ b/tests/api_tests/test_api_get_user_list.cpp @@ -28,8 +28,14 @@ struct get_user { void summarize_results_for_counted_only_get_user_list(tgl_ &data, int expected_count_users) { - std::cout << head_ << "Check if got users number ( " << expected_count_users << " ) is same as expected ( "<< data.user_count << " )."<< std::endl; - EXPECT_EQ(expected_count_users, data.user_count); + /* Since the calls to `subsession_add_user` in this test are asynchronous, + * there is no guarantee that adding users will actually be completed + * before the call to `subsession_get_user_list`. That's why we expect + * the actual number of users returned by the latter call to be less or + * equal than the declarative size of `expected_users` array. + */ + std::cout << head_ << "Check if got users number ( " << data.user_count << " ) is less or equal than expected ( " << expected_count_users << " )." << std::endl; + EXPECT_TRUE(data.user_count <= expected_count_users); std::cout << head_ << "Check if got users list is not filled" << std::endl; EXPECT_EQ(true, data.user_list == NULL); @@ -38,8 +44,9 @@ void summarize_results_for_counted_only_get_user_list(tgl_ &data, int expected_c template<typename T> void summarize_results_for_get_user_list(tgl_ &data, T expected_users) { - std::cout << head_ << "Check if got users number ( " << expected_users.size() << " ) is same as expected ( "<< data.user_count << " )."<< std::endl; - EXPECT_EQ(expected_users.size(), data.user_count); + // See the remark in the above function. + std::cout << head_ << "Check if got users number ( " << data.user_count << " ) is less or equal than expected ( " << expected_users.size() << " )." << std::endl; + EXPECT_TRUE(data.user_count <= expected_users.size()); if(expected_users.size() != data.user_count) { return; |