Remove boost dependency
[platform/core/appfw/app-installers.git] / src / common / utils / user_util.cc
1 // Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by an apache-2.0 license that can be
3 // found in the LICENSE file.
4
5 #include "common/utils/user_util.h"
6
7 #include <manifest_parser/utils/logging.h>
8 #include <tzplatform_config.h>
9
10 #include <glib.h>
11 #include <grp.h>
12 #include <gum/gum-user.h>
13 #include <gum/gum-user-service.h>
14 #include <gum/common/gum-user-types.h>
15
16 #include <filesystem>
17 #include <vector>
18
19 #include "common/utils/file_util.h"
20 #include "common/utils/glist_range.h"
21
22 namespace fs = std::filesystem;
23
24 namespace {
25
26 const int32_t kPWBufSize = sysconf(_SC_GETPW_R_SIZE_MAX);
27 const int32_t kGRBufSize = sysconf(_SC_GETGR_R_SIZE_MAX);
28
29 const int kRetryMax = 5;
30 const int kRetryDelay = 1000000 / 2;
31
32 const char kSubsessionDir[] = "subsession";
33
34 }
35
36 namespace common_installer {
37
38 UserList GetUserList() {
39   gchar** user_type_strv = gum_user_type_to_strv(
40       GUM_USERTYPE_ADMIN | GUM_USERTYPE_GUEST | GUM_USERTYPE_NORMAL);
41   GumUserService* service = nullptr;
42   GumUserList* gum_user_list = nullptr;
43   int count = 0;
44   bool is_offline = getuid() == 0;
45   // FIXME: Temporary retry logic, this should be removed
46   while (count < kRetryMax) {
47     service = gum_user_service_create_sync(is_offline ? TRUE : FALSE);
48     if (service == nullptr) {
49       if (is_offline) {
50         LOG(WARNING) << "Failed to create gum user service!";
51         break;
52       }
53       count++;
54       LOG(WARNING) << "Failed to create gum user service! ("
55                    << count << "/" << kRetryMax << ")";
56       usleep(kRetryDelay);
57       continue;
58     }
59
60     gum_user_list =
61         gum_user_service_get_user_list_sync(service, user_type_strv);
62     g_object_unref(service);
63     if (gum_user_list == nullptr) {
64       if (is_offline) {
65         LOG(WARNING) << "Failed to get gum user list!";
66         break;
67       }
68       count++;
69       LOG(WARNING) << "Failed to get gum user list! ("
70                    << count << "/" << kRetryMax << ")";
71       usleep(kRetryDelay);
72       continue;
73     } else {
74       break;
75     }
76   }
77   if (gum_user_list == nullptr) {
78     g_strfreev(user_type_strv);
79     return {};
80   }
81
82   UserList list;
83   for (GumUser* guser : GListRange<GumUser*>(gum_user_list)) {
84     uid_t uid;
85     g_object_get(G_OBJECT(guser), "uid", &uid, nullptr);
86     gid_t gid;
87     g_object_get(G_OBJECT(guser), "gid", &gid, nullptr);
88     gchar* homedir = nullptr;
89     g_object_get(G_OBJECT(guser), "homedir", &homedir, nullptr);
90     if (homedir == nullptr) {
91       LOG(WARNING) << "No homedir for uid: " << uid;
92       continue;
93     }
94     list.emplace_back(uid, gid, fs::path(homedir));
95     g_free(homedir);
96   }
97   g_strfreev(user_type_strv);
98   gum_user_service_list_free(gum_user_list);
99   return list;
100 }
101
102 std::optional<bool> IsAdminUser(uid_t uid) {
103   GumUser* guser;
104   int count = 0;
105   bool is_offline = getuid() == 0;
106   do {
107     guser = gum_user_get_sync(uid, is_offline ? TRUE : FALSE);
108     if (guser)
109       break;
110     if (is_offline) {
111       LOG(WARNING) << "Failed to get gum user!";
112       break;
113     }
114     count++;
115     LOG(WARNING) << "Failed to get gum user! ("
116                  << count << "/" << kRetryMax << ")";
117     usleep(kRetryDelay);
118   } while (count < kRetryMax);
119   if (!guser)
120     return {};
121   GumUserType ut;
122   g_object_get(G_OBJECT(guser), "usertype", &ut, NULL);
123   bool is_admin;
124   if (ut == GUM_USERTYPE_ADMIN)
125     is_admin = true;
126   else
127     is_admin = false;
128   g_object_unref(guser);
129   return is_admin;
130 }
131
132 std::optional<gid_t> GetGidByGroupName(const char* groupname) {
133   char buf[kGRBufSize];
134   struct group entry;
135   struct group* ge;
136   int ret = getgrnam_r(groupname, &entry, buf, sizeof(buf), &ge);
137   if (ret || ge == nullptr)
138     return {};
139   return entry.gr_gid;
140 }
141
142 std::string GetUsernameByUid(uid_t user) {
143   struct passwd pwd;
144   struct passwd* pwd_result;
145   char buf[kPWBufSize];
146   int ret = getpwuid_r(user, &pwd, buf, sizeof(buf), &pwd_result);
147   if (ret != 0 || pwd_result == nullptr)
148     return {};
149   return pwd.pw_name;
150 }
151
152 std::optional<uid_t> GetUidByUserName(const char* username) {
153   struct passwd pwd;
154   struct passwd* pwd_result;
155   char buf[kPWBufSize];
156   int ret = getpwnam_r(username, &pwd, buf, sizeof(buf), &pwd_result);
157   if (ret != 0 || pwd_result == nullptr)
158     return {};
159   return pwd.pw_uid;
160 }
161
162 std::optional<gid_t> GetGidByUid(uid_t uid) {
163   struct passwd pwd;
164   struct passwd* pwd_result;
165   char buf[kPWBufSize];
166   int ret = getpwuid_r(uid, &pwd, buf, sizeof(buf), &pwd_result);
167   if (ret != 0 || pwd_result == nullptr)
168     return {};
169   return pwd.pw_gid;
170 }
171
172 std::string GetGroupNameByGid(gid_t gid) {
173   char buf[kGRBufSize];
174   struct group entry;
175   struct group* ge;
176   int ret = getgrgid_r(gid, &entry, buf, sizeof(buf), &ge);
177   if (ret || ge == nullptr) {
178     return {};
179   }
180   return entry.gr_name;
181 }
182
183 std::vector<std::string> GetLightUserList(uid_t uid) {
184   std::string username = GetUsernameByUid(uid);
185   if (username.empty()) {
186     LOG(ERROR) << "Failed to get user name of uid: " << uid;
187     return {};
188   }
189
190   tzplatform_set_user(uid);
191   fs::path subsession_dir =
192       fs::path(tzplatform_getenv(TZ_USER_HOME)) / kSubsessionDir;
193   tzplatform_reset_user();
194
195   // Get subsuession user list by directory name instead of using sessiond API
196   // due to performance issue
197   return GetDirectoryList(subsession_dir);
198 }
199
200 }  // namespace common_installer