summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarol Lewandowski <k.lewandowsk@samsung.com>2022-11-25 12:45:52 +0000
committerGerrit Code Review <gerrit@review>2022-11-25 12:45:52 +0000
commit138e9c0430f3b2b54bb5530e1c6b6c8f1fd54797 (patch)
tree3abfba51d7813e30e5eb5a848f7b38cb6ec1a4e6
parent2e2eb65eedbbdcd62122884b9207c3a0b758906c (diff)
parentdb46fedbf63132884ad7ebb473b6a1a814b0e72c (diff)
downloadsessiond-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.c140
-rw-r--r--tests/api_tests/test_api_get_user_list.cpp15
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;