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