Handle abnormally terminated installer process
[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 <ctype.h>
24 #include <dirent.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <glib.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <sys/wait.h>
35 #include <pwd.h>
36 #include <unistd.h>
37
38 #include <dlog.h>
39 #include <sqlite3.h>
40 #include <tzplatform_config.h>
41
42 #include <pkgmgr_parser_db.h>
43 #include <pkgmgr_parser_db_queries.h>
44
45 #ifdef LOG_TAG
46 #undef LOG_TAG
47 #endif
48
49 #define LOG_TAG "PKG_DB_RECOVERY"
50
51 #ifndef APPFW_USER
52 #define APPFW_USER "app_fw"
53 #endif
54 #ifndef OWNER_ROOT
55 #define OWNER_ROOT 0
56 #endif
57 #define REGULAR_USER 5000
58 #define MAX_QUERY_LEN   4096
59 #define BUFSIZE 4096
60
61 #define PKGMGR_PARSER_DB_FILE tzplatform_mkpath(TZ_SYS_DB, ".pkgmgr_parser.db")
62 #define PKGMGR_CERT_DB_FILE tzplatform_mkpath(TZ_SYS_DB, ".pkgmgr_cert.db")
63 #define NEED_RECOVERY_FILE tzplatform_mkpath(TZ_SYS_DB, ".need_pkg_recovery")
64
65 #define GLOBAL_USER tzplatform_getuid(TZ_SYS_GLOBALAPP_USER)
66
67 typedef struct user_info {
68         uid_t uid;
69         char *db_path;
70 } user_info;
71
72 static GList *user_info_list;
73
74 char *string_trim_inplace(char *s) {
75         char *original = s;
76         size_t len = 0;
77
78         while (isspace((unsigned char) *s))
79                 s++;
80
81         if (*s) {
82                 char *p = s;
83                 while (*p)
84                         p++;
85                 while (isspace((unsigned char) *(--p)));
86                 p[1] = '\0';
87                 len = (size_t) (p - s + 1);
88         }
89
90         if (len > MAX_QUERY_LEN)
91                 return NULL;
92         return (s == original) ? s : memmove(original, s, len + 1);
93 }
94
95 static char *__get_dbpath(uid_t uid)
96 {
97         const char *db_path;
98         char path[PATH_MAX];
99
100         db_path = tzplatform_getenv(TZ_SYS_DB);
101         if (db_path == NULL) {
102                 LOGE("Failed to get TZ_SYS_DB path");
103                 return NULL;
104         }
105
106         snprintf(path, sizeof(path), "%s/user/%d/.pkgmgr_parser.db", db_path, uid);
107         return strdup(path);
108 }
109
110 #define BUSY_WAITING_USEC (1000000 / 10 / 2) /* 0.05 sec */
111 #define BUSY_WAITING_MAX 100 /* wait for max 5 sec */
112 static int __db_busy_handler(void *data, int count)
113 {
114         if (count < BUSY_WAITING_MAX) {
115                 usleep(BUSY_WAITING_USEC);
116                 return 1;
117         } else {
118                 /* sqlite3_prepare_v2 will return SQLITE_BUSY */
119                 return 0;
120         }
121 }
122
123 static bool __check_aborted(uid_t uid)
124 {
125         char buf[BUFSIZE];
126
127         snprintf(buf, sizeof(buf), "%s_%d", NEED_RECOVERY_FILE, uid);
128         if (access(buf, F_OK) == 0) {
129                 return true;
130         } else {
131                 if (errno != ENOENT)
132                         LOGE("failed to access %s, errno: %d", buf, errno);
133                 return false;
134         }
135 }
136
137 static void __create_need_to_recovery_file(uid_t uid)
138 {
139         int fd;
140         char buf[BUFSIZE];
141
142         snprintf(buf, sizeof(buf), "%s_%d", NEED_RECOVERY_FILE, uid);
143         fd = open(buf, O_CREAT | O_WRONLY, S_IRWXU);
144         if (fd == -1)
145                 LOGE("failed to create file: %s, errno: %d", buf, errno);
146         else
147                 close(fd);
148 }
149
150 static void __remove_need_to_recovery_file(uid_t uid)
151 {
152         char buf[BUFSIZE];
153
154         snprintf(buf, sizeof(buf), "%s_%d", NEED_RECOVERY_FILE, uid);
155         if (unlink(buf))
156                 LOGE("failed to remove file: %s, errno: %d", buf, errno);
157 }
158
159 static bool __integrity_check(const char *db_path)
160 {
161         int ret = -1;
162         sqlite3_stmt *stmt = NULL;
163         const char *check_result;
164         static const char integrity_check_query[] =
165                         "PRAGMA integrity_check";
166         sqlite3 *db;
167
168         ret = sqlite3_open_v2(db_path, &db,
169                         SQLITE_OPEN_READWRITE, NULL);
170         if (ret != SQLITE_OK) {
171                 LOGE("Failed to open db");
172                 sqlite3_close_v2(db);
173                 return false;
174         }
175
176         ret = sqlite3_busy_handler(db, __db_busy_handler, NULL);
177         if (ret != SQLITE_OK) {
178                 LOGE("failed to register busy handler: %s",
179                                 sqlite3_errmsg(db));
180                 sqlite3_close_v2(db);
181                 return ret;
182         }
183
184         ret = sqlite3_prepare_v2(db, integrity_check_query,
185                         strlen(integrity_check_query), &stmt, NULL);
186         if (ret != SQLITE_OK) {
187                 LOGE("Failed to check integrity_check : %s", sqlite3_errmsg(db));
188                 goto err;
189         }
190
191         ret = sqlite3_step(stmt);
192         if (ret != SQLITE_ROW) {
193                 LOGE("Failed to check integrity db :%s", sqlite3_errmsg(db));
194                 goto err;
195         }
196
197         check_result = (const char *)sqlite3_column_text(stmt, 0);
198         if (!check_result) {
199                 LOGE("Failed to check integrity db :%s", sqlite3_errmsg(db));
200                 goto err;
201         }
202
203         if (strcasecmp(check_result, "ok") != 0) {
204                 LOGE("db corrupted :%s", db_path);
205                 goto err;
206         }
207
208         sqlite3_finalize(stmt);
209         sqlite3_close(db);
210         return true;
211
212 err:
213         sqlite3_finalize(stmt);
214         sqlite3_close(db);
215         return false;
216 }
217
218 static void _xsystem(const char *argv[])
219 {
220         int status = 0;
221         pid_t pid;
222
223         pid = fork();
224         switch (pid) {
225         case -1:
226                 perror("fork failed");
227                 return;
228         case 0:
229                 /* child */
230                 execvp(argv[0], (char *const *)argv);
231                 _exit(-1);
232         default:
233                 /* parent */
234                 break;
235         }
236         if (waitpid(pid, &status, 0) == -1) {
237                 perror("waitpid failed");
238                 return;
239         }
240         if (WIFSIGNALED(status)) {
241                 perror("signal");
242                 return;
243         }
244         if (!WIFEXITED(status)) {
245                 /* shouldn't happen */
246                 perror("should not happen");
247                 return;
248         }
249 }
250
251 static void __create_db(uid_t uid)
252 {
253         char uid_string[11];
254         const char *create_db[] = { "/usr/bin/pkg-db-creator", uid_string, NULL};
255
256         snprintf(uid_string, sizeof(uid_string), "%d", (int)uid);
257         _xsystem(create_db);
258 }
259
260 static void __initdb(uid_t uid)
261 {
262         char uid_string[11];
263         const char *initdb_rw[] = { "/usr/bin/pkg_initdb",
264                         "--recover-db", "--uid", uid_string, "--keep-db", NULL};
265         const char *initdb_ro[] =  { "/usr/bin/pkg_initdb",
266                         "--recover-db", "--keep-db", NULL};
267
268         __create_need_to_recovery_file(uid);
269         __create_db(uid);
270         snprintf(uid_string, sizeof(uid_string), "%d", (int)uid);
271         _xsystem((uid > REGULAR_USER) ? initdb_rw : initdb_ro);
272         __remove_need_to_recovery_file(uid);
273 }
274
275 static void __initdb_all()
276 {
277         GList *tmp_list = NULL;
278         user_info *tmp_info;
279         __initdb(GLOBAL_USER);
280
281         for (tmp_list = user_info_list;
282                         tmp_list != NULL; tmp_list = g_list_next(tmp_list)) {
283                 tmp_info = (user_info *)tmp_list->data;
284                 if (!tmp_info)
285                         continue;
286                 __initdb(tmp_info->uid);
287         }
288 }
289
290 static void __check_user_db()
291 {
292         GList *tmp_list = NULL;
293         user_info *tmp_info;
294         bool need_recovery;
295
296         for (tmp_list = user_info_list;
297                         tmp_list != NULL; tmp_list = g_list_next(tmp_list)) {
298                 need_recovery = false;
299                 tmp_info = (user_info *)tmp_list->data;
300                 if (!tmp_info)
301                         continue;
302                 if (!__integrity_check(tmp_info->db_path)) {
303                         LOGE("User db for %d has corrupted", (int)tmp_info->uid);
304                         need_recovery = true;
305                 } else if (__check_aborted(tmp_info->uid)) {
306                         need_recovery = true;
307                 }
308
309                 if (need_recovery)
310                         __initdb(tmp_info->uid);
311         }
312 }
313
314 static void _free_user_info(gpointer data)
315 {
316         user_info *info = (user_info *)data;
317         free(info->db_path);
318         free(info);
319 }
320
321 static void _get_user_list()
322 {
323         DIR *dir;
324         struct dirent *ent;
325         struct stat stats;
326         char traverse_path[PATH_MAX];
327         char abs_dirname[PATH_MAX];
328         const char *db_path;
329         int ret;
330         uid_t uid;
331         user_info *info;
332
333         db_path = tzplatform_getenv(TZ_SYS_DB);
334         if (db_path == NULL) {
335                 LOGE("Failed to get TZ_SYS_DB path");
336                 return;
337         }
338
339         snprintf(traverse_path, sizeof(traverse_path), "%s/user", db_path);
340         dir = opendir(traverse_path);
341         if (!dir) {
342                 LOGE("Failed to open dir: %d (%s)", errno, traverse_path);
343                 return;
344         }
345
346         while ((ent = readdir(dir)) != NULL) {
347                 ret = snprintf(abs_dirname, PATH_MAX, "%s/%s", traverse_path,
348                          ent->d_name);
349                 if (ret < 0 || ret > PATH_MAX) {
350                         LOGE("snprintf fail");
351                         closedir(dir);
352                         return;
353                 }
354
355                 ret = stat(abs_dirname, &stats);
356                 if (ret != 0) {
357                         LOGE("failed to stat: %d (%s)", errno, abs_dirname);
358                         continue;
359                 }
360
361                 if (!strcmp(".", ent->d_name) || !strcmp("..", ent->d_name) ||
362                                 !S_ISDIR(stats.st_mode))
363                         continue;
364                 uid = (uid_t)atoi(ent->d_name);
365                 if (uid < REGULAR_USER)
366                         continue;
367
368                 info = calloc(1, sizeof(user_info));
369                 if (!info) {
370                         closedir(dir);
371                         return;
372                 }
373
374                 info->uid = uid;
375                 info->db_path = __get_dbpath(uid);
376                 user_info_list = g_list_prepend(user_info_list, info);
377         }
378
379         closedir(dir);
380 }
381
382 static bool __check_db_schema(const char *db_path,
383                 const char **db_tables, const char **init_queries) {
384         int ret = -1;
385         int i;
386         sqlite3_stmt *stmt = NULL;
387         const char *check_result;
388         char *schema_in_library, *schema_in_db;
389         static const char table_schema_query[] =
390                         "SELECT sql from sqlite_master WHERE name=?";
391         sqlite3 *db;
392         char *schema_lib_tmp;
393         char *schema_lib_ptr;
394         char *schema_db_tmp;
395         char *schema_db_ptr;
396         char *line_db;
397         char *line_library;
398
399         ret = sqlite3_open_v2(db_path, &db,
400                         SQLITE_OPEN_READONLY, NULL);
401         if (ret != SQLITE_OK) {
402                 LOGE("Failed to open db");
403                 sqlite3_close_v2(db);
404                 return false;
405         }
406
407         ret = sqlite3_busy_handler(db, __db_busy_handler, NULL);
408         if (ret != SQLITE_OK) {
409                 LOGE("failed to register busy handler: %s",
410                                 sqlite3_errmsg(db));
411                 sqlite3_close_v2(db);
412                 return ret;
413         }
414         for (i = 0; db_tables[i] != NULL; i++) {
415                 ret = sqlite3_prepare_v2(db, table_schema_query,
416                                 strlen(table_schema_query), &stmt, NULL);
417                 if (ret != SQLITE_OK) {
418                         LOGE("Failed to check db schema : %s",
419                                         sqlite3_errmsg(db));
420                         goto err;
421                 }
422
423                 ret = sqlite3_bind_text(stmt, 1,
424                                 db_tables[i],-1, SQLITE_STATIC);
425
426                 ret = sqlite3_step(stmt);
427                 if (ret != SQLITE_ROW) {
428                         LOGE("Failed to check db schema :%s",
429                                         sqlite3_errmsg(db));
430                         goto err;
431                 }
432
433                 check_result = (const char *)sqlite3_column_text(stmt, 0);
434                 if (!check_result) {
435                         LOGE("Failed to check db schema :%s",
436                                         sqlite3_errmsg(db));
437                         goto err;
438                 }
439
440                 schema_in_library = strstr(init_queries[i], db_tables[i]);
441                 if (schema_in_library == NULL) {
442                         LOGE("Failed to get initialization query from library");
443                         goto err;
444                 }
445                 schema_lib_tmp = strdup(schema_in_library);
446                 schema_lib_ptr = schema_lib_tmp;
447                 if (schema_lib_tmp == NULL) {
448                         LOGE("Out of memory");
449                         goto err;
450                 }
451
452                 schema_in_db = strstr(check_result, db_tables[i]);
453                 if (schema_in_db == NULL) {
454                         LOGE("Failed to get initialization query from db");
455                         free(schema_lib_ptr);
456                         goto err;
457                 }
458                 schema_db_tmp = strdup(schema_in_db);
459                 schema_db_ptr = schema_db_tmp;
460                 if (schema_db_tmp == NULL) {
461                         LOGE("Out of memory");
462                         free(schema_lib_ptr);
463                         goto err;
464                 }
465
466                 while (true) {
467                         line_db = strsep(&schema_db_tmp, ",");
468                         line_library = strsep(&schema_lib_tmp, ",");
469                         if (line_db == NULL || line_library == NULL)
470                                 break;
471
472                         if (string_trim_inplace(line_db) == NULL ||
473                                         string_trim_inplace(line_library) == NULL)
474                                 break;
475
476                         ret = strcmp(string_trim_inplace(line_db),
477                                         string_trim_inplace(line_library));
478                         if (ret != 0)
479                                 break;
480                 }
481
482                 free(schema_lib_ptr);
483                 free(schema_db_ptr);
484
485                 if (ret != 0) {
486                         LOGE("Broken schema detected in table[%s], query[%s]",
487                                         db_tables[i], schema_in_db);
488                         goto err;
489                 }
490
491                 sqlite3_finalize(stmt);
492                 stmt = NULL;
493         }
494
495         sqlite3_close(db);
496         return true;
497
498 err:
499         sqlite3_finalize(stmt);
500         sqlite3_close(db);
501         return false;
502 }
503
504 static void _check_db()
505 {
506         int need_recovery = false;
507
508         if (!__integrity_check(PKGMGR_CERT_DB_FILE)) {
509                 LOGE("Cert db corrupted. restore entire pkgmgr db");
510                 need_recovery = true;
511         } else if (!__integrity_check(PKGMGR_PARSER_DB_FILE)) {
512                 LOGE("Global parser db corrupted. restore entire pkgmgr db");
513                 need_recovery = true;
514         } else if (__check_aborted(GLOBAL_USER)) {
515                 LOGE("Previous recovery was aborted. restore entire pkgmgr db");
516                 need_recovery = true;
517         } else if (!__check_db_schema(PKGMGR_PARSER_DB_FILE,
518                         PARSER_TABLES, PARSER_INIT_QUERIES)) {
519                 LOGE("Global parser db schema was broken. restore entire pkgmgr db");
520                 need_recovery = true;
521         } else if (!__check_db_schema(PKGMGR_CERT_DB_FILE,
522                         CERT_TABLES, CERT_INIT_QUERIES)) {
523                 LOGE("Cert db schema was broken. restore entire pkgmgr db");
524                 need_recovery = true;
525         }
526
527         if (need_recovery) {
528                 __initdb_all();
529                 return;
530         }
531
532         __check_user_db();
533 }
534
535 int main(int argc, char *argv[])
536 {
537         if ((int)getuid() > REGULAR_USER) {
538                 LOGE("This cmd is not allowed for regular user");
539                 return -1;
540         }
541
542         _get_user_list();
543         _check_db();
544
545         g_list_free_full(user_info_list, _free_user_info);
546         return 0;
547 }