2 * Copyright (c) 2000 - 2017 Samsung Electronics Co., Ltd. All rights reserved.
4 * Contact: Junghyun Yeon <jungh.yeon@samsung.com>,
5 * Jongmyeong Ko <jongmyeong.ko@samsung.com>, Sangyoon Jang <s89.jang@samsung.com>
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
33 #include <sys/types.h>
40 #include <tzplatform_config.h>
42 #include <pkgmgr_parser_db.h>
43 #include <pkgmgr_parser_db_queries.h>
49 #define LOG_TAG "PKG_DB_RECOVERY"
52 #define APPFW_USER "app_fw"
57 #define REGULAR_USER 5000
58 #define MAX_QUERY_LEN 4096
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")
65 #define GLOBAL_USER tzplatform_getuid(TZ_SYS_GLOBALAPP_USER)
67 typedef struct user_info {
72 static GList *user_info_list;
74 char *string_trim_inplace(char *s) {
78 while (isspace((unsigned char) *s))
85 while (isspace((unsigned char) *(--p)));
87 len = (size_t) (p - s + 1);
90 if (len > MAX_QUERY_LEN)
92 return (s == original) ? s : memmove(original, s, len + 1);
95 static char *__get_dbpath(uid_t uid)
100 db_path = tzplatform_getenv(TZ_SYS_DB);
101 if (db_path == NULL) {
102 LOGE("Failed to get TZ_SYS_DB path");
106 snprintf(path, sizeof(path), "%s/user/%d/.pkgmgr_parser.db", db_path, uid);
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)
114 if (count < BUSY_WAITING_MAX) {
115 usleep(BUSY_WAITING_USEC);
118 /* sqlite3_prepare_v2 will return SQLITE_BUSY */
123 static bool __check_aborted(uid_t uid)
127 snprintf(buf, sizeof(buf), "%s_%d", NEED_RECOVERY_FILE, uid);
128 if (access(buf, F_OK) == 0) {
132 LOGE("failed to access %s, errno: %d", buf, errno);
137 static void __create_need_to_recovery_file(uid_t uid)
142 snprintf(buf, sizeof(buf), "%s_%d", NEED_RECOVERY_FILE, uid);
143 fd = open(buf, O_CREAT | O_WRONLY, S_IRWXU);
145 LOGE("failed to create file: %s, errno: %d", buf, errno);
150 static void __remove_need_to_recovery_file(uid_t uid)
154 snprintf(buf, sizeof(buf), "%s_%d", NEED_RECOVERY_FILE, uid);
156 LOGE("failed to remove file: %s, errno: %d", buf, errno);
159 static bool __integrity_check(const char *db_path)
162 sqlite3_stmt *stmt = NULL;
163 const char *check_result;
164 static const char integrity_check_query[] =
165 "PRAGMA integrity_check";
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);
176 ret = sqlite3_busy_handler(db, __db_busy_handler, NULL);
177 if (ret != SQLITE_OK) {
178 LOGE("failed to register busy handler: %s",
180 sqlite3_close_v2(db);
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));
191 ret = sqlite3_step(stmt);
192 if (ret != SQLITE_ROW) {
193 LOGE("Failed to check integrity db :%s", sqlite3_errmsg(db));
197 check_result = (const char *)sqlite3_column_text(stmt, 0);
199 LOGE("Failed to check integrity db :%s", sqlite3_errmsg(db));
203 if (strcasecmp(check_result, "ok") != 0) {
204 LOGE("db corrupted :%s", db_path);
208 sqlite3_finalize(stmt);
213 sqlite3_finalize(stmt);
218 static void _xsystem(const char *argv[])
226 perror("fork failed");
230 execvp(argv[0], (char *const *)argv);
236 if (waitpid(pid, &status, 0) == -1) {
237 perror("waitpid failed");
240 if (WIFSIGNALED(status)) {
244 if (!WIFEXITED(status)) {
245 /* shouldn't happen */
246 perror("should not happen");
251 static void __create_db(uid_t uid)
254 const char *create_db[] = { "/usr/bin/pkg-db-creator", uid_string, NULL};
256 snprintf(uid_string, sizeof(uid_string), "%d", (int)uid);
260 static void __initdb(uid_t uid)
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};
268 __create_need_to_recovery_file(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);
275 static void __initdb_all()
277 GList *tmp_list = NULL;
279 __initdb(GLOBAL_USER);
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;
286 __initdb(tmp_info->uid);
290 static void __check_user_db()
292 GList *tmp_list = NULL;
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;
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;
310 __initdb(tmp_info->uid);
314 static void _free_user_info(gpointer data)
316 user_info *info = (user_info *)data;
321 static void _get_user_list()
326 char traverse_path[PATH_MAX];
327 char abs_dirname[PATH_MAX];
333 db_path = tzplatform_getenv(TZ_SYS_DB);
334 if (db_path == NULL) {
335 LOGE("Failed to get TZ_SYS_DB path");
339 snprintf(traverse_path, sizeof(traverse_path), "%s/user", db_path);
340 dir = opendir(traverse_path);
342 LOGE("Failed to open dir: %d (%s)", errno, traverse_path);
346 while ((ent = readdir(dir)) != NULL) {
347 ret = snprintf(abs_dirname, PATH_MAX, "%s/%s", traverse_path,
349 if (ret < 0 || ret > PATH_MAX) {
350 LOGE("snprintf fail");
355 ret = stat(abs_dirname, &stats);
357 LOGE("failed to stat: %d (%s)", errno, abs_dirname);
361 if (!strcmp(".", ent->d_name) || !strcmp("..", ent->d_name) ||
362 !S_ISDIR(stats.st_mode))
364 uid = (uid_t)atoi(ent->d_name);
365 if (uid < REGULAR_USER)
368 info = calloc(1, sizeof(user_info));
375 info->db_path = __get_dbpath(uid);
376 user_info_list = g_list_prepend(user_info_list, info);
382 static bool __check_db_schema(const char *db_path,
383 const char **db_tables, const char **init_queries) {
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=?";
392 char *schema_lib_tmp;
393 char *schema_lib_ptr;
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);
407 ret = sqlite3_busy_handler(db, __db_busy_handler, NULL);
408 if (ret != SQLITE_OK) {
409 LOGE("failed to register busy handler: %s",
411 sqlite3_close_v2(db);
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",
423 ret = sqlite3_bind_text(stmt, 1,
424 db_tables[i],-1, SQLITE_STATIC);
426 ret = sqlite3_step(stmt);
427 if (ret != SQLITE_ROW) {
428 LOGE("Failed to check db schema :%s",
433 check_result = (const char *)sqlite3_column_text(stmt, 0);
435 LOGE("Failed to check db schema :%s",
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");
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");
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);
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);
467 line_db = strsep(&schema_db_tmp, ",");
468 line_library = strsep(&schema_lib_tmp, ",");
469 if (line_db == NULL || line_library == NULL)
472 if (string_trim_inplace(line_db) == NULL ||
473 string_trim_inplace(line_library) == NULL)
476 ret = strcmp(string_trim_inplace(line_db),
477 string_trim_inplace(line_library));
482 free(schema_lib_ptr);
486 LOGE("Broken schema detected in table[%s], query[%s]",
487 db_tables[i], schema_in_db);
491 sqlite3_finalize(stmt);
499 sqlite3_finalize(stmt);
504 static void _check_db()
506 int need_recovery = false;
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;
535 int main(int argc, char *argv[])
537 if ((int)getuid() > REGULAR_USER) {
538 LOGE("This cmd is not allowed for regular user");
545 g_list_free_full(user_info_list, _free_user_info);