Supplement db recovery tool
[platform/core/appfw/pkgmgr-info.git] / tool / pkg-db-recovery.c
1 /*
2  * Copyright (c) 2000 - 2017 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * Contact: Junghyun Yeon <jungh.yeon@samsung.com>,
5  * Jongmyeong Ko <jongmyeong.ko@samsung.com>, Sangyoon Jang <s89.jang@samsung.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  */
20
21 #define _GNU_SOURCE
22
23 #include <dirent.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <glib.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34 #include <pwd.h>
35 #include <unistd.h>
36
37 #include <sqlite3.h>
38 #include <tzplatform_config.h>
39
40 #include <pkgmgr_parser_db.h>
41
42 #ifndef APPFW_USER
43 #define APPFW_USER "app_fw"
44 #endif
45 #ifndef OWNER_ROOT
46 #define OWNER_ROOT 0
47 #endif
48 #define REGULAR_USER 5000
49 #define MAX_QUERY_LEN   4096
50 #define BUFSIZE 4096
51
52 #define PKGMGR_PARSER_DB_FILE tzplatform_mkpath(TZ_SYS_DB, ".pkgmgr_parser.db")
53 #define PKGMGR_CERT_DB_FILE tzplatform_mkpath(TZ_SYS_DB, ".pkgmgr_cert.db")
54
55 typedef struct user_info {
56         uid_t uid;
57         char *db_path;
58 } user_info;
59
60 static GList *user_info_list;
61
62 static char *__get_dbpath(uid_t uid)
63 {
64         const char *db_path;
65         char path[PATH_MAX];
66
67         db_path = tzplatform_getenv(TZ_SYS_DB);
68         if (db_path == NULL) {
69                 printf("Failed to get TZ_SYS_DB path");
70                 return NULL;
71         }
72
73         snprintf(path, sizeof(path), "%s/user/%d/.pkgmgr_parser.db", db_path, uid);
74         return strdup(path);
75 }
76
77 static bool __integrity_check(const char *db_path)
78 {
79         int ret = -1;
80         sqlite3_stmt *stmt = NULL;
81         const char *check_result;
82         static const char integrity_check_query[] =
83                         "PRAGMA integrity_check";
84         sqlite3 *db;
85
86         ret = sqlite3_open_v2(db_path, &db,
87                         SQLITE_OPEN_READWRITE, NULL);
88         if (ret != SQLITE_OK) {
89                 printf("Failed to open db\n");
90                 return false;
91         }
92
93         ret = sqlite3_prepare_v2(db, integrity_check_query,
94                         strlen(integrity_check_query), &stmt, NULL);
95         if (ret != SQLITE_OK) {
96                 printf("Failed to check integrity_check : %s\n", sqlite3_errmsg(db));
97                 goto err;
98         }
99
100         ret = sqlite3_step(stmt);
101         if (ret != SQLITE_ROW) {
102                 printf("Failed to check integrity db :%s\n", sqlite3_errmsg(db));
103                 goto err;
104         }
105
106         check_result = (const char *)sqlite3_column_text(stmt, 0);
107         if (!check_result) {
108                 printf("Failed to check integrity db :%s\n", sqlite3_errmsg(db));
109                 goto err;
110         }
111
112         if (strcasecmp(check_result, "ok") != 0) {
113                 printf("db corrupted :%s\n", db_path);
114                 goto err;
115         }
116
117         sqlite3_finalize(stmt);
118         sqlite3_close(db);
119         return true;
120
121 err:
122         sqlite3_finalize(stmt);
123         sqlite3_close(db);
124         return false;
125 }
126
127 static bool __change_owner(const char *files[2], uid_t uid)
128 {
129         int ret;
130         int i;
131         int fd;
132         struct passwd pwd;
133         struct passwd *result;
134         char buf[BUFSIZE];
135         struct stat sb;
136         mode_t mode;
137
138         if (uid == OWNER_ROOT) {
139                 ret = getpwnam_r(APPFW_USER, &pwd, buf, sizeof(buf), &result);
140                 if (result == NULL) {
141                         if (ret == 0)
142                                 printf("no such user: %d\n", uid);
143                         else
144                                 printf("getpwnam_r failed: %d", errno);
145                         return false;
146                 }
147                 uid = pwd.pw_uid;
148         }
149
150         ret = getpwuid_r(uid, &pwd, buf, sizeof(buf), &result);
151         if (result == NULL) {
152                 if (ret == 0)
153                         printf("no such user: %d\n", uid);
154                 else
155                         printf("getpwuid_r failed: %d\n", errno);
156                 return false;
157         }
158
159         for (i = 0; i < 2; i++) {
160                 fd = open(files[i], O_RDONLY);
161                 if (fd == -1) {
162                         printf("open %s failed: %d\n", files[i], errno);
163                         return false;
164                 }
165                 ret = fstat(fd, &sb);
166                 if (ret == -1) {
167                         printf("stat %s failed: %d\n", files[i], errno);
168                         close(fd);
169                         return false;
170                 }
171                 if (S_ISLNK(sb.st_mode)) {
172                         printf("%s is symlink!\n", files[i]);
173                         close(fd);
174                         return false;
175                 }
176                 ret = fchown(fd, uid, pwd.pw_gid);
177                 if (ret == -1) {
178                         printf("fchown %s failed: %d\n", files[i], errno);
179                         close(fd);
180                         return false;
181                 }
182
183                 mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
184                 if (strstr(files[0], PKGMGR_CERT_DB_FILE) != NULL)
185                         mode |= S_IWOTH;
186                 ret = fchmod(fd, mode);
187                 if (ret == -1) {
188                         printf("fchmod %s failed: %d\n", files[i], errno);
189                         close(fd);
190                         return false;;
191                 }
192                 close(fd);
193         }
194
195         return true;
196 }
197
198 static void __change_permission(uid_t uid)
199 {
200         const char *files[2];
201         char *parser_dbpath = NULL;
202         char journal_file[PATH_MAX];
203         GList *tmp_list;
204         user_info *tmp_info;
205
206         if (uid < REGULAR_USER) {
207                 files[0] = PKGMGR_PARSER_DB_FILE;
208                 snprintf(journal_file, sizeof(journal_file),
209                                 "%s-journal", PKGMGR_PARSER_DB_FILE);
210                 files[1] = journal_file;
211
212                 if (!__change_owner(files, uid)) {
213                         printf("Failed to change ownership\n");
214                         return;
215                 }
216
217                 files[0] = PKGMGR_CERT_DB_FILE;
218                 snprintf(journal_file, sizeof(journal_file),
219                                 "%s-journal", PKGMGR_CERT_DB_FILE);
220                 files[1] = journal_file;
221                 if (!__change_owner(files, uid)) {
222                         printf("Failed to change ownership\n");
223                         return;
224                 }
225         } else {
226                 for (tmp_list = user_info_list; tmp_list != NULL;
227                                 tmp_list = g_list_next(tmp_list)) {
228                         tmp_info = (user_info *)tmp_list->data;
229                         if (!tmp_info)
230                                 continue;
231
232                         if (tmp_info->uid == uid) {
233                                 parser_dbpath = tmp_info->db_path;
234                                 break;
235                         }
236                 }
237                 files[0] = parser_dbpath;
238                 snprintf(journal_file, sizeof(journal_file),
239                                 "%s-journal", parser_dbpath);
240                 files[1] = journal_file;
241
242                 if (!__change_owner(files, uid)) {
243                         printf("Failed to change ownership\n");
244                         return;
245                 }
246         }
247 }
248
249 static void _xsystem(const char *argv[])
250 {
251         int status = 0;
252         pid_t pid;
253
254         pid = fork();
255         switch (pid) {
256         case -1:
257                 perror("fork failed");
258                 return;
259         case 0:
260                 /* child */
261                 execvp(argv[0], (char *const *)argv);
262                 _exit(-1);
263         default:
264                 /* parent */
265                 break;
266         }
267         if (waitpid(pid, &status, 0) == -1) {
268                 perror("waitpid failed");
269                 return;
270         }
271         if (WIFSIGNALED(status)) {
272                 perror("signal");
273                 return;
274         }
275         if (!WIFEXITED(status)) {
276                 /* shouldn't happen */
277                 perror("should not happen");
278                 return;
279         }
280 }
281
282 static void __create_db(uid_t uid)
283 {
284         char uid_string[11];
285         const char *create_db[] = { "/usr/bin/pkg-db-creator", uid_string, NULL};
286
287         snprintf(uid_string, sizeof(uid_string), "%d", (int)uid);
288         _xsystem(create_db);
289 }
290
291 static void __initdb(uid_t uid)
292 {
293         char uid_string[11];
294         const char *initdb_rw[] = { "/usr/bin/pkg_initdb",
295                         "--recover-db", "--uid", uid_string, "--keep-db", NULL};
296         const char *initdb_ro[] =  { "/usr/bin/pkg_initdb",
297                         "--recover-db", "--keep-db", NULL};
298
299         __create_db(uid);
300         __change_permission(uid);
301         snprintf(uid_string, sizeof(uid_string), "%d", (int)uid);
302         _xsystem((uid > REGULAR_USER) ? initdb_rw : initdb_ro);
303 }
304
305 static void __initdb_all()
306 {
307         GList *tmp_list = NULL;
308         user_info *tmp_info;
309         __initdb(getuid());
310
311         for (tmp_list = user_info_list;
312                         tmp_list != NULL; tmp_list = g_list_next(tmp_list)) {
313                 tmp_info = (user_info *)tmp_list->data;
314                 if (!tmp_info || tmp_info->uid < REGULAR_USER)
315                         continue;
316                 __initdb(tmp_info->uid);
317         }
318 }
319
320 static void __check_user_db()
321 {
322         GList *tmp_list = NULL;
323         user_info *tmp_info;
324
325         for (tmp_list = user_info_list;
326                         tmp_list != NULL; tmp_list = g_list_next(tmp_list)) {
327                 tmp_info = (user_info *)tmp_list->data;
328                 if (!tmp_info)
329                         continue;
330                 if (!__integrity_check(tmp_info->db_path)) {
331                         printf("User db for %d has corrupted\n", (int)tmp_info->uid);
332                         __initdb(tmp_info->uid);
333                 }
334         }
335 }
336
337 static void _free_user_info(gpointer data)
338 {
339         user_info *info = (user_info *)data;
340         free(info->db_path);
341         free(info);
342 }
343
344 static void _get_user_list()
345 {
346         DIR *dir;
347         struct dirent *ent;
348         struct stat stats;
349         char traverse_path[PATH_MAX];
350         char abs_dirname[PATH_MAX];
351         const char *db_path;
352         uid_t uid;
353         user_info *info;
354
355         db_path = tzplatform_getenv(TZ_SYS_DB);
356         if (db_path == NULL) {
357                 printf("Failed to get TZ_SYS_DB path");
358                 return;
359         }
360
361         snprintf(traverse_path, sizeof(traverse_path), "%s/user", db_path);
362         dir = opendir(traverse_path);
363
364         while ((ent = readdir(dir)) != NULL) {
365                 snprintf(abs_dirname, PATH_MAX, "%s/%s", traverse_path,
366                          ent->d_name);
367                 stat(abs_dirname, &stats);
368                 if (!strcmp(".", ent->d_name) || !strcmp("..", ent->d_name) ||
369                                 !S_ISDIR(stats.st_mode))
370                         continue;
371                 info = calloc(1, sizeof(user_info));
372                 if (!info) {
373                         closedir(dir);
374                         return;
375                 }
376
377                 uid = (uid_t)atoi(ent->d_name);
378                 if (!uid) {
379                         free(info);
380                         continue;
381                 }
382                 info->uid = uid;
383                 info->db_path = __get_dbpath(uid);
384                 user_info_list = g_list_append(user_info_list, info);
385         }
386
387         closedir(dir);
388 }
389
390 static void _check_db()
391 {
392         if (!__integrity_check(PKGMGR_CERT_DB_FILE)) {
393                 printf("Cert db corrupted. restore entire pkgmgr db\n");
394                 __initdb_all();
395                 return;
396         } else if (!__integrity_check(PKGMGR_PARSER_DB_FILE)) {
397                 printf("Global parser db corrupted. restore entire pkgmgr db\n");
398                 __initdb_all();
399                 return;
400         }
401
402         __check_user_db();
403 }
404
405 int main(int argc, char *argv[])
406 {
407         if ((int)getuid() > REGULAR_USER) {
408                 printf("This cmd is not allowed for regular user\n");
409                 return -1;
410         }
411
412         _get_user_list();
413         _check_db();
414
415         g_list_free_full(user_info_list, _free_user_info);
416         return 0;
417 }