Introduce defs.h with all cmake variables
[platform/core/system/crash-worker.git] / src / crash-manager / crash-manager.c
1 /*
2  * crash-manager
3  *
4  * Copyright (c) 2016 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <dirent.h>
20 #include <fcntl.h>
21 #include <gio/gio.h>
22 #include <iniparser.h>
23 #include <inttypes.h>
24 #include <libgen.h>
25 #include <limits.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/file.h>
30 #include <sys/mman.h>
31 #include <sys/prctl.h>
32 #include <sys/procfs.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/vfs.h>
36 #include <unistd.h>
37
38 #include <pkgmgr-info.h>
39 #include <tzplatform_config.h>
40
41 #undef LOG_TAG
42 #define LOG_TAG "CRASH_MANAGER"
43
44 #include "defs.h"
45 #include "dbus_notify.h"
46 #include "shared/log.h"
47 #include "shared/spawn.h"
48 #include "shared/util.h"
49 #include "so-info.h"
50
51 /* Parsing */
52 #define KEY_MAX              255
53 #define CRASH_SECTION        "CrashManager"
54
55 /* Crash-popup dbus */
56 #define POPUP_BUS_NAME       "org.tizen.system.popup"
57 #define POPUP_OBJECT_PATH    "/Org/Tizen/System/Popup/Crash"
58 #define POPUP_INTERFACE_NAME POPUP_BUS_NAME".Crash"
59 #define POPUP_METHOD         "PopupLaunch"
60
61 /* Configuration default values */
62 /* note: 0 means unlimited */
63 #define SYSTEM_MAX_USE       0
64 #define SYSTEM_KEEP_FREE     0
65 #define MAX_RETENTION_SEC    0
66 #define MAX_CRASH_DUMP       0
67 #define DUMP_CORE            1
68 #define ALLOW_ZIP            true
69
70 #define APPID_MAX                   128
71 #define PKGNAME_MAX                 128
72
73 #define CRASH_TEMP_SUBDIR    "/temp/"
74 #define CRASH_PATH_SUBDIR    "/dump/"
75
76 #define WAIT_FOR_OPT_TIMEOUT_SEC 60
77
78 #define MINICOREDUMPER_TIMEOUT_MS  DEFAULT_COMMAND_TIMEOUT_MS
79 #define CRASH_STACK_TIMEOUT_MS     DEFAULT_COMMAND_TIMEOUT_MS
80 #define ZIP_TIMEOUT_MS             DEFAULT_COMMAND_TIMEOUT_MS
81
82 enum {
83         RET_EXCEED = 1,
84         NUM_EXCEED,
85         USAGE_EXCEED
86 };
87
88 enum {
89         REP_TYPE_INFO = 0,
90         REP_TYPE_FULL
91 };
92
93 #define REP_DEFAULT_TYPE REP_TYPE_FULL
94
95 #define REP_TYPE_FULL_STR "FULL"
96 #define REP_TYPE_INFO_STR "INFO"
97
98 struct file_info {
99         bool   isdir;
100         size_t size;
101         time_t mtime;
102         char   *path;
103 };
104
105 /* Configuration variables */
106 static int system_max_use;
107 static int system_keep_free;
108 static int max_retention_sec;
109 static int max_crash_dump;
110 static int dump_core;
111 static bool allow_zip;
112 static char* crash_root_path;
113 static char* crash_crash_path;
114 static char* crash_temp_path;
115 static int report_type;
116
117 /* Paths and variables */
118 struct crash_info {
119         pid_t pid_info;
120         pid_t tid_info;
121         int uid_info;
122         int gid_info;
123         int sig_info;
124         char *cmd_line;
125         char *cmd_path;
126         time_t time_info;
127         char *temp_dir;
128         char *name;
129         char *result_path;
130         char *pfx;
131         char *info_path;
132         char *core_path;
133         char *log_path;
134         char appid[APPID_MAX];
135         char pkgid[PKGNAME_MAX];
136 #ifdef SYS_ASSERT
137         char *sysassert_cs_path;
138         bool have_sysassert_report;
139 #endif
140         int prstatus_fd;
141 };
142
143 /* pkgmgrinfo filter list function for getting application ID */
144 static int appinfo_get_appid_func(pkgmgrinfo_appinfo_h handle,
145                 void *user_data)
146 {
147         char *str = NULL;
148
149         int ret = pkgmgrinfo_appinfo_get_appid(handle, &str);
150         if (ret == PMINFO_R_OK && str)
151                 (*(char **)user_data) = strdup(str);
152
153         return ret;
154 }
155
156 /* get application ID by pkgmgrinfo filter */
157 static int get_appid(char *exepath, char *appid, int len)
158 {
159         pkgmgrinfo_appinfo_filter_h handle = NULL;
160         int count, ret;
161         char *aid = NULL;
162
163         ret = pkgmgrinfo_appinfo_filter_create(&handle);
164         if (ret != PMINFO_R_OK) {
165                 ret = -1;
166                 goto out;
167         }
168
169         ret = pkgmgrinfo_appinfo_filter_add_string(handle, PMINFO_APPINFO_PROP_APP_EXEC, exepath);
170         if (ret != PMINFO_R_OK) {
171                 ret = -1;
172                 goto out_free;
173         }
174
175         ret = pkgmgrinfo_appinfo_filter_count(handle, &count);
176         if (ret != PMINFO_R_OK) {
177                 ret = -1;
178                 goto out_free;
179         }
180
181         if (count < 1) {
182                 ret = -1;
183                 goto out_free;
184         }
185
186         ret = pkgmgrinfo_appinfo_filter_foreach_appinfo(handle, appinfo_get_appid_func, &aid);
187         if (ret != PMINFO_R_OK) {
188                 ret = -1;
189                 goto out_free;
190         }
191         if (aid) {
192                 snprintf(appid, len, "%s", aid);
193                 ret = 0;
194                 free(aid);
195         }
196
197 out_free:
198         pkgmgrinfo_appinfo_filter_destroy(handle);
199 out:
200         return ret;
201 }
202
203 /* get package ID by appid */
204 static int get_pkgid(char *appid, char *pkgid, int len)
205 {
206         pkgmgrinfo_appinfo_h handle = NULL;
207         int ret;
208         char *pkid = NULL;
209
210         ret = pkgmgrinfo_appinfo_get_appinfo(appid, &handle);
211         if (ret != PMINFO_R_OK) {
212                 ret = -1;
213                 goto out;
214         }
215         ret = pkgmgrinfo_appinfo_get_pkgid(handle, &pkid);
216         if (ret != PMINFO_R_OK) {
217                 ret = -1;
218                 goto out_free;
219         }
220         snprintf(pkgid, len, "%s", pkid);
221
222 out_free:
223         pkgmgrinfo_appinfo_destroy_appinfo(handle);
224 out:
225         return ret;
226 }
227
228 static int prepare_paths(void)
229 {
230         int tmp_len;
231         tmp_len = strlen(crash_root_path) + strlen(CRASH_PATH_SUBDIR);
232         crash_crash_path = (char*)malloc(tmp_len + 1);
233         if (crash_crash_path == NULL) {
234                 _E("Couldn't allocate memory for crash_crash_path: %m\n");
235                 return 0;
236         }
237         snprintf(crash_crash_path, tmp_len + 1, "%s%s", crash_root_path, CRASH_PATH_SUBDIR);
238
239         tmp_len = strlen(crash_root_path) + strlen(CRASH_TEMP_SUBDIR);
240         crash_temp_path = (char*)malloc(tmp_len + 1);
241         if (crash_temp_path == NULL) {
242                 _E("Couldn't allocate memory for crash_temp_path: %m\n");
243                 return 0;
244         }
245         snprintf(crash_temp_path, tmp_len + 1, "%s%s", crash_root_path, CRASH_TEMP_SUBDIR);
246         return 1;
247 }
248
249 static const char* report_type_to_str(const int report_type)
250 {
251         switch (report_type) {
252         case REP_TYPE_INFO:
253                 return REP_TYPE_INFO_STR;
254                 break;
255         case REP_TYPE_FULL:
256                 return REP_TYPE_FULL_STR;
257         default:
258                 return NULL;
259                 break;
260         }
261 }
262
263 static int report_type_from_str(const char* report_type_str)
264 {
265         if (report_type_str == NULL)
266                 return -1;
267
268         if (strncmp(report_type_str, REP_TYPE_FULL_STR, strlen(REP_TYPE_FULL_STR)) == 0)
269                 return REP_TYPE_FULL;
270         else if (strncmp(report_type_str, REP_TYPE_INFO_STR, strlen(REP_TYPE_INFO_STR)) == 0)
271                 return REP_TYPE_INFO;
272
273         return -1;
274 }
275
276 static int get_config(void)
277 {
278         dictionary *ini = NULL;
279         char key[KEY_MAX];
280         int value;
281         int result = 1;
282         char *value_str;
283
284         system_max_use = SYSTEM_MAX_USE;
285         system_keep_free = SYSTEM_KEEP_FREE;
286         max_retention_sec = MAX_RETENTION_SEC;
287         max_crash_dump = MAX_CRASH_DUMP;
288         dump_core = DUMP_CORE;
289         allow_zip = ALLOW_ZIP;
290         crash_root_path = strdup(CRASH_ROOT_PATH);
291         if (crash_root_path == NULL) {
292                 _E("strdup error: %m\n");
293                 return -1;
294         }
295         report_type = REP_DEFAULT_TYPE;
296
297         ini = iniparser_load(CRASH_MANAGER_CONFIG_PATH);
298         if (!ini) {
299                 _E("Failed to load conf file %s", CRASH_MANAGER_CONFIG_PATH);
300                 return 0;
301         }
302
303         snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "SystemMaxUse");
304         value = iniparser_getint(ini, key, -1);
305         if (value < 0) {
306                 _D("Invalid value for SystemMaxUse. Use default value [ %d kbyte]",
307                                 SYSTEM_MAX_USE);
308         } else {
309                 _D("SystemMaxUse [ %d kbyte]", value);
310                 system_max_use = value;
311         }
312
313         snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "SystemKeepFree");
314         value = iniparser_getint(ini, key, -1);
315         if (value < 0) {
316                 _D("Invalid value for SystemKeepFree. Use default value [ %d kbyte]",
317                                 SYSTEM_KEEP_FREE);
318         } else {
319                 _D("SystemKeepFree [ %d kbyte]", value);
320                 system_keep_free = value;
321         }
322
323
324         snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "MaxRetentionSec");
325         value = iniparser_getint(ini, key, -1);
326         if (value < 0) {
327                 _D("Invalid value for MaxRetentionSec. Use default value [ %d ]",
328                                 MAX_RETENTION_SEC);
329         } else {
330                 _D("MaxRetentionSec [ %d ]", value);
331                 max_retention_sec = value;
332         }
333
334         snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "MaxCrashDump");
335         value = iniparser_getint(ini, key, -1);
336         if (value < 0) {
337                 _D("Invalid value for MaxCrashDump. Use default value [ %d ]",
338                                 MAX_CRASH_DUMP);
339         } else {
340                 _D("MaxCrashDump [ %d ]", value);
341                 max_crash_dump = value;
342         }
343
344         snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "DumpCore");
345         value = iniparser_getint(ini, key, -1);
346         if (value != 0 && value != 1) {
347                 _D("Invalid value for DumpCore default value [ %d ]",
348                                 DUMP_CORE);
349         } else {
350                 _D("DumpCore [ %d ]", value);
351                 dump_core = value;
352         }
353
354         snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "AllowZip");
355         value = iniparser_getboolean(ini, key, -1);
356         if (value < 0) {
357                 _D("Invalid value for AllowZip. Use default value [ %s ]",
358                                 ALLOW_ZIP ? "true" : "false");
359         } else {
360                 _D("AllowZip [ %s ]", value ? "true" : "false");
361                 allow_zip = value;
362         }
363
364         snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "CrashRootPath");
365         value_str = iniparser_getstring(ini, key, NULL);
366         if (value_str == NULL) {
367                 _D("Invalid value for CrashRootPath. Use default value [ %s ]",
368                                 CRASH_ROOT_PATH);
369         } else {
370                 _D("CrashRootPath [ %s ]", value_str);
371                 free(crash_root_path);
372                 crash_root_path = strdup(value_str);
373                 if (crash_root_path == NULL) {
374                         _E("strdup error: %m\n");
375                         result = -1;
376                         goto out;
377                 }
378         }
379
380         snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "ReportType");
381         value_str = iniparser_getstring(ini, key, NULL);
382         if (value_str == NULL) {
383                 _D("Invalid value for ReportType. Use default value [ %s ]",
384                                 report_type_to_str(report_type));
385         } else {
386                 _D("ReportType [ %s ]", value_str);
387                 report_type = report_type_from_str(value_str);
388
389                 if (report_type < 0) {
390                         _E("Unknown ReportType %s. Fallback to default: %s",
391                            value_str, report_type_to_str(REP_DEFAULT_TYPE));
392                         report_type = REP_DEFAULT_TYPE;
393                 }
394         }
395
396 out:
397         iniparser_freedict(ini);
398         return result;
399 }
400
401 static int make_dump_dir(void)
402 {
403         struct stat st;
404
405         if (!stat(crash_crash_path, &st)) {
406                 if (!(st.st_mode & S_IFDIR)) {
407                         _E("%s (not DIR) is already exist", crash_crash_path);
408                         return -1;
409                 }
410         } else {
411                 if (mkdir(crash_crash_path, 0775) < 0) {
412                         _E("Failed to mkdir for %s", crash_crash_path);
413                         return -1;
414                 }
415         }
416
417         if (!stat(crash_temp_path, &st)) {
418                 if (!(st.st_mode & S_IFDIR)) {
419                         _E("%s (not DIR) is already exist", crash_temp_path);
420                         return -1;
421                 }
422         } else {
423                 if (mkdir(crash_temp_path, 0775) < 0) {
424                         _E("Failed to mkdir for %s", crash_temp_path);
425                         return -1;
426                 }
427         }
428
429         return 0;
430 }
431
432 static int get_cmd_info(struct crash_info *cinfo)
433 {
434         cinfo->cmd_line = get_cmd_line(cinfo->pid_info);
435         cinfo->cmd_path = get_exe_path(cinfo->pid_info);
436
437         return (cinfo->cmd_line != NULL && cinfo->cmd_path != NULL);
438 }
439
440 static int set_prstatus(struct crash_info *cinfo)
441 {
442         int ret;
443         char prstatus_name[NAME_MAX+1];
444
445         ret = snprintf(prstatus_name, NAME_MAX, "/%d.prstatus", cinfo->pid_info);
446         if (ret < 0) {
447                 _E("Failed to snprintf for prstatus path");
448                 goto close_fd;
449         }
450
451         cinfo->prstatus_fd = shm_open(prstatus_name, O_RDWR | O_CREAT, 0600);
452         if (cinfo->prstatus_fd < 0) {
453                 _E("shm_open: %m");
454                 goto close_fd;
455         }
456
457         ret = shm_unlink(prstatus_name);
458         if (ret < 0) {
459                 _E("shm_unlink: %m");
460                 goto close_fd;
461         }
462
463         ret = fcntl(cinfo->prstatus_fd, F_GETFD);
464         if (ret < 0) {
465                 _E("fcntl(): %m");
466                 goto close_fd;
467         }
468
469         ret = fcntl(cinfo->prstatus_fd, F_SETFD, ret & ~FD_CLOEXEC);
470         if (ret < 0) {
471                 _E("fcntl(): %m");
472                 goto close_fd;
473         }
474
475         ret = ftruncate(cinfo->prstatus_fd, sizeof(struct elf_prstatus));
476         if (ret < 0) {
477                 _E("ftruncate(): %m");
478                 goto close_fd;
479         }
480
481         return 0;
482
483 close_fd:
484         if (cinfo->prstatus_fd >= 0)
485                 close(cinfo->prstatus_fd);
486         return -1;
487 }
488
489 static int set_crash_info(struct crash_info *cinfo, int argc, char *argv[])
490 {
491         int ret;
492         char *temp_dir_ret = NULL;
493         char date[80];
494         struct tm loc_tm;
495
496         cinfo->pid_info = strtol(argv[1], NULL, 10);
497         cinfo->sig_info = atoi(argv[4]);
498         if (argc > 6)
499                 cinfo->tid_info = strtol(argv[6], NULL, 10);
500         else {
501                 cinfo->tid_info = find_crash_tid(cinfo->pid_info);
502                 if (cinfo->tid_info < 0) {
503                         _I("TID not found");
504                         cinfo->tid_info = cinfo->pid_info;
505                 }
506         }
507
508         ret = get_cmd_info(cinfo);
509         if (ret <= 0) {
510                 _E("Failed to get command info");
511                 return -1;
512         }
513
514         cinfo->time_info = strtol(argv[5], NULL, 10);
515         localtime_r(&cinfo->time_info, &loc_tm);
516         strftime(date, sizeof(date), "%Y%m%d%H%M%S", &loc_tm);
517
518         if (asprintf(&cinfo->temp_dir, "%s/crash.XXXXXX", crash_temp_path) == -1) {
519                 _E("Failed to asprintf for temp_dir");
520                 cinfo->temp_dir = NULL;
521                 return -1;
522         }
523
524         temp_dir_ret = mkdtemp(cinfo->temp_dir);
525         if (!temp_dir_ret || access(temp_dir_ret, F_OK)) {
526                 _E("Failed to mkdtemp for temp_dir");
527                 return -1;
528         }
529
530         if (asprintf(&cinfo->name, "%s_%d_%s", basename(cinfo->cmd_line),
531                         cinfo->pid_info, date) == -1) {
532                 _E("Failed to snprintf for name");
533                 cinfo->name = NULL;
534                 goto rm_temp;
535         }
536
537         if (allow_zip)
538                 ret = asprintf(&cinfo->result_path,
539                                 "%s/%s.zip", crash_crash_path, cinfo->name);
540         else
541                 ret = asprintf(&cinfo->result_path,
542                                 "%s/%s", crash_crash_path, cinfo->name);
543         if (ret == -1) {
544                 _E("Failed to asprintf for result path");
545                 cinfo->result_path = NULL;
546                 goto rm_temp;
547         }
548
549         if (asprintf(&cinfo->pfx, "%s/%s", cinfo->temp_dir, cinfo->name) == -1) {
550                 _E("Failed to asprintf for pfx");
551                 cinfo->pfx = NULL;
552                 goto rm_temp;
553         }
554         ret = mkdir(cinfo->pfx, 0775);
555         if (ret < 0) {
556                 _E("Failed to mkdir for %s", cinfo->pfx);
557                 goto rm_temp;
558         }
559
560         if (asprintf(&cinfo->info_path, "%s/%s.info", cinfo->pfx, cinfo->name) == -1) {
561                 _E("Failed to asprintf for info path");
562                 cinfo->info_path = NULL;
563                 goto rm_temp;
564         }
565
566         if (asprintf(&cinfo->core_path, "%s/%s.coredump", cinfo->pfx, cinfo->name) == -1) {
567                 _E("Failed to asprintf for core path");
568                 cinfo->core_path = NULL;
569                 goto rm_temp;
570         }
571
572         if (asprintf(&cinfo->log_path, "%s/%s.log", cinfo->pfx, cinfo->name) == -1) {
573                 _E("Failed to asprintf for log path");
574                 cinfo->log_path = NULL;
575                 goto rm_temp;
576         }
577
578 #ifdef SYS_ASSERT
579         if (asprintf(&cinfo->sysassert_cs_path, "/tmp/crash_stack/%s_%d.info",
580                        basename(cinfo->cmd_line), cinfo->pid_info) == -1) {
581                 _E("Failed to snprintf for sys-assert callstack path");
582                 cinfo->sysassert_cs_path = NULL;
583                 goto rm_temp;
584         }
585 #endif
586         if (set_prstatus(cinfo) < 0)
587                 goto rm_temp;
588
589         if (get_appid(cinfo->cmd_line, cinfo->appid, sizeof(cinfo->appid)) < 0) {
590                 snprintf(cinfo->appid, sizeof(cinfo->appid), "%s", basename(cinfo->cmd_line));
591                 snprintf(cinfo->pkgid, sizeof(cinfo->pkgid), "%s", basename(cinfo->cmd_line));
592         } else {
593                 if (get_pkgid(cinfo->appid, cinfo->pkgid, sizeof(cinfo->pkgid)) < 0)
594                         snprintf(cinfo->pkgid, sizeof(cinfo->pkgid), "%s", cinfo->appid);
595         }
596
597         return 0;
598
599 rm_temp:
600         remove_dir(cinfo->temp_dir, 1);
601         return -1;
602 }
603
604 #ifdef SYS_ASSERT
605 static int get_sysassert_cs(struct crash_info *cinfo)
606 {
607         int ret;
608         char move_path[PATH_MAX];
609
610         if (access(cinfo->sysassert_cs_path, F_OK)) {
611                 _E("The sys-assert cs file not found: %s",
612                         cinfo->sysassert_cs_path);
613                 cinfo->have_sysassert_report = 0;
614                 return -1;
615         } else
616                 cinfo->have_sysassert_report = 1;
617
618         ret = snprintf(move_path, sizeof(move_path), "%s/%s",
619                         cinfo->pfx, basename(cinfo->sysassert_cs_path));
620         if (ret < 0) {
621                 _E("Failed to snprintf for move path");
622                 return -1;
623         }
624
625         if (move_file(move_path, cinfo->sysassert_cs_path) < 0) {
626                 _E("Failed to move %s to %s",
627                                 cinfo->sysassert_cs_path, move_path);
628                 return -1;
629         }
630
631         return 0;
632 }
633 #endif
634
635 static void launch_crash_popup(struct crash_info *cinfo)
636 {
637         GDBusConnection *conn;
638         GVariantBuilder *builder;
639         GVariant *parameters = NULL;
640         GVariant *reply = NULL;
641         GError *error = NULL;
642         int ret;
643
644         conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
645         if (error) {
646                 _E("Failed to get dbus: %s", error->message);
647                 g_error_free(error);
648                 return;
649         }
650
651         builder = g_variant_builder_new(G_VARIANT_TYPE("a{ss}"));
652         g_variant_builder_add(builder, "{ss}", "_SYSPOPUP_CONTENT_", "crash");
653         g_variant_builder_add(builder, "{ss}", "_PROCESS_NAME_",
654                         basename(cinfo->cmd_line));
655         g_variant_builder_add(builder, "{ss}", "_EXEPATH_", cinfo->cmd_path);
656         parameters = g_variant_new("(a{ss})", builder);
657         g_variant_builder_unref(builder);
658
659         reply = g_dbus_connection_call_sync(conn,
660                                             POPUP_BUS_NAME,
661                                             POPUP_OBJECT_PATH,
662                                             POPUP_INTERFACE_NAME,
663                                             POPUP_METHOD,
664                                             parameters,
665                                             G_VARIANT_TYPE("(i)"),
666                                             G_DBUS_CALL_FLAGS_NONE,
667                                             120000,
668                                             NULL,
669                                             &error);
670         if (error) {
671                 _E("Failed to get reply: %s", error->message);
672                 g_error_free(error);
673                 goto exit;
674         }
675
676         g_variant_get(reply, "(i)", &ret);
677         _I("Crash_popup is launched: (%d)", ret);
678
679 exit:
680         if (reply)
681                 g_variant_unref(reply);
682         if (parameters)
683                 g_variant_unref(parameters);
684
685         g_object_unref(conn);
686 }
687
688 static bool dump_system_state(const struct crash_info *cinfo, pid_t *pid)
689 {
690         char *av[] = {"/usr/bin/dump_systemstate", "-d", "-k", "-j", "-p", "-f",  cinfo->log_path, NULL};
691         return spawn(av, NULL, NULL, NULL, pid, NULL);
692 }
693
694 static void save_so_info(const struct crash_info *cinfo)
695 {
696         char maps_path[PATH_MAX];
697         char so_info_path[PATH_MAX];
698         snprintf(maps_path, sizeof(maps_path), "/proc/%d/maps",
699                  cinfo->pid_info);
700
701         snprintf(so_info_path, sizeof(so_info_path),
702                          "%s/%s.%s", cinfo->pfx, cinfo->name, "so_info");
703
704         get_and_save_so_info(maps_path, so_info_path);
705 }
706
707 /* remove a file whose name begins with 'beggining_of_name'. This is needed
708  * when we don't want to include coredump in report, but we only know the
709  * beginning of the coredump file name. */
710 static int remove_file_in_dir(const char *directory, const char *beginning_of_name)
711 {
712         int ret = -1;
713         int errno_unlink = 0;
714
715         DIR *dir = opendir(directory);
716         if (dir == NULL)
717                 goto end;
718
719         int dir_fd = dirfd(dir);
720         if (dir_fd == -1) {
721                 closedir(dir);
722                 goto end;
723         }
724
725         struct dirent* file_info;
726         while ((file_info = readdir(dir)) != NULL) {
727                 if (strstr(file_info->d_name, beginning_of_name) == file_info->d_name) {
728                         ret = unlinkat(dir_fd, file_info->d_name, 0);
729                         errno_unlink = errno;
730                         break;
731                 }
732         }
733         closedir(dir);
734         errno = errno_unlink; /* for %m */
735 end:
736         return ret;
737 }
738
739 // These macros are used in execute_minicoredump() and execute_crash_stack()
740 #define SNPRINTF_OR_EXIT_W(name, format, member) if (snprintf(name##_str, sizeof(name##_str), format, cinfo->member) < 0) goto out;
741 #define SNPRINTF_OR_EXIT(name, format) SNPRINTF_OR_EXIT_W(name, format, name##_info)
742
743 static bool execute_minicoredump(struct crash_info *cinfo, int *exit_code)
744 {
745         char *coredump_name = NULL;
746         char *prstatus_fd_str = NULL;
747         bool is_ok = false;
748
749         if (asprintf(&coredump_name, "%s.coredump", cinfo->name) == -1
750             || asprintf(&prstatus_fd_str, "%d", cinfo->prstatus_fd) == -1) {
751                 _E("Unable to allocate memory");
752                 goto out;
753         }
754
755         char pid_str[11], uid_str[11], gid_str[11], sig_str[11], time_str[11];
756
757         SNPRINTF_OR_EXIT(pid, "%d")
758         SNPRINTF_OR_EXIT(uid, "%d")
759         SNPRINTF_OR_EXIT(gid, "%d")
760         SNPRINTF_OR_EXIT(sig, "%d")
761         SNPRINTF_OR_EXIT(time, "%ld")
762
763         /* Execute minicoredumper */
764         char *args[] = {
765                         MINICOREDUMPER_BIN_PATH,   // minicoredumper filename path
766                         pid_str,                   // %p - pid
767                         uid_str,                   // %u - UID
768                         gid_str,                   // %g - GID
769                         sig_str,                   // %s - number of signal
770                         time_str,                  // %t - time of dump
771                         "localhost",               // %h - hostname
772                         "core",                    // %e - exe name (need for result filename)
773                         MINICOREDUMPER_CONFIG_PATH, // config file
774                         "-d",
775                         cinfo->pfx,            // temp dir
776                         "-o",
777                         coredump_name,             // coredump filename
778                         "-P",
779                         prstatus_fd_str,
780                         NULL
781                         };
782
783         is_ok = spawn_wait(args, NULL, NULL, NULL, MINICOREDUMPER_TIMEOUT_MS, exit_code);
784
785         /* Minicoredumper must be executed to dump at least PRSTATUS for
786            other tools, coredump, however, might have been disabled. */
787         if (!dump_core) {
788                 if (remove_file_in_dir(cinfo->pfx, coredump_name) != 0)
789                         _E("Saving core disabled - removing coredump %s/%s failed: %m",
790                            cinfo->pfx, coredump_name);
791                 else
792                         _D("Saving core disabled - removed coredump %s/%s",
793                            cinfo->pfx, coredump_name);
794         }
795
796 out:
797         free(coredump_name);
798         free(prstatus_fd_str);
799
800         return is_ok;
801 }
802
803 static bool execute_crash_stack(const struct crash_info *cinfo, int *exit_code)
804 {
805         char pid_str[11], tid_str[11], sig_str[11], prstatus_fd_str[11];
806         bool is_ok = false;
807
808         SNPRINTF_OR_EXIT(pid, "%d")
809         SNPRINTF_OR_EXIT(tid, "%d")
810         SNPRINTF_OR_EXIT(sig, "%d")
811         SNPRINTF_OR_EXIT_W(prstatus_fd, "%d", prstatus_fd)
812
813         /* Execute crash-stack */
814         char *args[] = {
815                         CRASH_STACK_PATH,
816                         "--pid",
817                         pid_str,
818                         "--tid",
819                         tid_str,
820                         "--sig",
821                         sig_str,
822                         "--prstatus_fd",
823                         prstatus_fd_str,
824                         NULL
825         };
826
827         int fd = open(cinfo->info_path, O_WRONLY | O_CREAT, 0600);
828         if (fd < 0) {
829                 _E("open %s error: %m", cinfo->info_path);
830                 return false;
831         }
832
833         spawn_param_u param = { .int_val = fd };
834         is_ok = spawn_wait(args, NULL, spawn_setstdout, &param, CRASH_STACK_TIMEOUT_MS, exit_code);
835         close(fd);
836
837 out:
838         return is_ok;
839 }
840
841 #undef SNPRINTF_OR_EXIT
842 #undef SNPRINTF_OR_EXIT_W
843
844 static bool execute_crash_modules(struct crash_info *cinfo)
845 {
846         int exit_code = 0;
847         if (!execute_minicoredump(cinfo, &exit_code) || exit_code != 0) {
848                 _E("Failed to run minicoredumper - can not continue");
849                 return false;
850         }
851
852 #ifdef SYS_ASSERT
853         /* Use process_vm_readv() version as fallback if sys-assert
854          * failed to generate report */
855         if (cinfo->have_sysassert_report)
856                 return false;
857 #endif
858         execute_crash_stack(cinfo, NULL);
859
860         return true;
861 }
862
863 static int lock_dumpdir(void)
864 {
865         int fd;
866
867         if ((fd = open(crash_crash_path, O_RDONLY | O_DIRECTORY)) < 0) {
868                 _E("Failed to open %s", crash_crash_path);
869                 return -1;
870         }
871
872         if (flock(fd, LOCK_EX) < 0) {
873                 _E("Failed to flock (LOCK)");
874                 close(fd);
875                 return -1;
876         }
877
878         return fd;
879 }
880
881 static void unlock_dumpdir(int fd)
882 {
883         if (flock(fd, LOCK_UN) < 0)
884                 _E("Failed to unlink (UNLOCK)");
885         close(fd);
886 }
887
888 static int dump_filter(const struct dirent *de)
889 {
890         if (de->d_name[0] == '.')
891                 return 0;
892         return 1;
893 }
894
895 static int mtime_cmp(const void *_a, const void *_b)
896 {
897         const struct file_info *a = _a;
898         const struct file_info *b = _b;
899
900         if (a->mtime < b->mtime)
901                 return -1;
902         if (a->mtime > b->mtime)
903                 return 1;
904         return 0;
905 }
906
907 static int scan_dump(struct file_info **dump_list, off_t *usage)
908 {
909         struct file_info *temp_list;
910         struct dirent **scan_list = NULL;
911         struct stat st;
912         int i, scan_num, dump_num = 0;
913         int fd;
914
915         if ((fd = open(crash_crash_path, O_DIRECTORY)) < 0) {
916                 _E("Failed to open %s", crash_crash_path);
917                 return -1;
918         }
919
920         scan_num = scandir(crash_crash_path, &scan_list, &dump_filter, NULL);
921         if (scan_num < 0) {
922                 close(fd);
923                 return -1;
924         }
925
926         temp_list = (struct file_info *)calloc(scan_num,
927                         sizeof(struct file_info));
928         if (!temp_list) {
929                 _E("Failed to calloc for dump list");
930                 goto exit;
931         }
932
933         for (i = 0; i < scan_num; i++) {
934                 if (fstatat(fd, scan_list[i]->d_name, &st, 0) < 0) {
935                         _E("Failed to fstatat");
936                         continue;
937                 }
938
939                 if (asprintf(&(temp_list[dump_num].path), "%s/%s",
940                                         crash_crash_path, scan_list[i]->d_name) < 0) {
941                         _E("Failed to asprintf");
942                         continue;
943                 }
944
945                 if (scan_list[i]->d_type == DT_DIR) {
946                         temp_list[dump_num].isdir = 1;
947                         temp_list[dump_num].size =
948                                 get_directory_usage(temp_list[dump_num].path);
949                 } else {
950                         temp_list[dump_num].isdir = 0;
951                         temp_list[dump_num].size = st.st_size;
952                 }
953                 temp_list[dump_num].mtime = st.st_mtime;
954                 dump_num++;
955         }
956
957         if (dump_num <= 0) {
958                 free(temp_list);
959                 goto exit;
960         }
961
962         if (dump_num != scan_num) {
963                 struct file_info *const tmp = (struct file_info *)realloc(temp_list,
964                                 dump_num * sizeof(struct file_info));
965                 if (!tmp) {
966                         free(temp_list);
967                         dump_num = -1;
968                         goto exit;
969                 }
970                 temp_list = tmp;
971         }
972
973         qsort(temp_list, dump_num, sizeof(struct file_info), mtime_cmp);
974
975         *usage = 0;
976         for (i = 0; i < dump_num; i++) {
977                 _D("[%d] path: %s(%s), size: %zu kb, mtime: %s",
978                                 i,
979                                 temp_list[i].path,
980                                 temp_list[i].isdir ? "DIR" : "FILE",
981                                 temp_list[i].size / 1024,
982                                 ctime(&(temp_list[i].mtime)));
983                 *usage += temp_list[i].size;
984         }
985         *dump_list = temp_list;
986 exit:
987         for (i = 0; i < scan_num; i++)
988                 free(scan_list[i]);
989         free(scan_list);
990         close(fd);
991
992         return dump_num;
993 }
994
995 static int check_disk_available(const char *path, int check_size)
996 {
997         struct statfs lstatfs;
998         int avail_size = 0;
999
1000         if (!path)
1001                 return -1;
1002
1003         if (statfs(path, &lstatfs) < 0)
1004                 return -1;
1005         avail_size = (int)(lstatfs.f_bavail * (lstatfs.f_bsize / 1024));
1006
1007         if (check_size > avail_size) {
1008                 _I("avail_size is (%d)", avail_size);
1009                 return -1;
1010         }
1011
1012         return 0;
1013 }
1014
1015 static int remove_file_info(struct file_info file)
1016 {
1017         if (file.isdir)
1018                 return remove_dir(file.path, 1);
1019         else
1020                 return unlink(file.path);
1021 }
1022
1023 static void clean_dump(void)
1024 {
1025         struct file_info *dump_list = NULL;
1026         int i, scan_num, dump_num, remove_flag;
1027         off_t usage = 0;
1028         time_t cur_time;
1029
1030         scan_num = scan_dump(&dump_list, &usage);
1031         if (scan_num <= 0)
1032                 return;
1033
1034         dump_num = scan_num;
1035
1036         cur_time = time(NULL);
1037
1038         for (i = 0; i < scan_num; i++) {
1039                 remove_flag = 0;
1040
1041                 /* Retention time check */
1042                 if (max_retention_sec &&
1043                         dump_list[i].mtime > 0 &&
1044                         dump_list[i].mtime + max_retention_sec < cur_time)
1045                         remove_flag = RET_EXCEED;
1046                 /* Check the number of dumps */
1047                 else if (max_crash_dump &&
1048                         0 < dump_num && max_crash_dump < dump_num)
1049                         remove_flag = NUM_EXCEED;
1050                 /* Check the max system use size */
1051                 else if (system_max_use &&
1052                         0 < dump_num && system_max_use < usage / 1024)
1053                         remove_flag = USAGE_EXCEED;
1054
1055                 switch (remove_flag) {
1056                 case RET_EXCEED:
1057                         _I("Reached the maximum retention time %d, so remove (%s)",
1058                                         max_retention_sec, dump_list[i].path);
1059                         break;
1060                 case NUM_EXCEED:
1061                         _I("Reached the maximum number of dump %d/%d, so remove (%s)",
1062                                         dump_num, max_crash_dump,
1063                                         dump_list[i].path);
1064                         break;
1065                 case USAGE_EXCEED:
1066                         _I("Reached the maximum disk usage %" PRId64 "/%d kb, so remove (%s)",
1067                                         usage / 1024, system_max_use,
1068                                         dump_list[i].path);
1069                         break;
1070                 default:
1071                         break;
1072                 }
1073
1074                 if (!remove_flag)
1075                         continue;
1076
1077                 if (remove_file_info(dump_list[i]) < 0) {
1078                         _E("Failed to remove %s", dump_list[i].path);
1079                         continue;
1080                 }
1081                 dump_num--;
1082                 usage -= dump_list[i].size;
1083         }
1084
1085         /* Check disk free space to keep */
1086         if (system_keep_free &&
1087                         check_disk_available(crash_root_path,
1088                                              system_keep_free) < 0) {
1089                 _I("Disk is not available! so set the maximum number of dump to 1");
1090                 max_crash_dump = 1;
1091         }
1092
1093         for (i = 0; i < dump_num; i++)
1094                 free(dump_list[i].path);
1095         free(dump_list);
1096 }
1097
1098 static void compress(struct crash_info *cinfo)
1099 {
1100         int ret, lock_fd;
1101         char zip_path[PATH_MAX];
1102
1103         ret = snprintf(zip_path, sizeof(zip_path), "%s/report.zip",
1104                         cinfo->temp_dir);
1105         if (ret < 0) {
1106                 _E("Failed to snprintf for zip path");
1107                 return;
1108         }
1109
1110         char *args[] = {
1111                 "/bin/zip",
1112                 "-y",
1113                 "-r",
1114                 zip_path,
1115                 cinfo->name,
1116                 NULL
1117         };
1118
1119         spawn_param_u param = { .char_ptr = cinfo->temp_dir };
1120         (void)spawn_wait(args, NULL, spawn_chdir, &param, ZIP_TIMEOUT_MS, NULL);
1121
1122         if ((lock_fd = lock_dumpdir()) < 0)
1123                 return;
1124         if (!rename(zip_path, cinfo->result_path))
1125                 clean_dump();
1126         else
1127                 _E("Failed to move %s to %s", zip_path, cinfo->result_path);
1128         unlock_dumpdir(lock_fd);
1129
1130         if (remove_dir(cinfo->temp_dir, 1) < 0)
1131                 _E("Failed to delete temp directory");
1132 }
1133
1134 static void move_dump_data(const char *from_path, const struct crash_info *cinfo)
1135 {
1136         int lock_fd;
1137
1138         if ((lock_fd = lock_dumpdir()) < 0)
1139                 return;
1140         if (!rename(from_path, cinfo->result_path))
1141                 clean_dump();
1142         else
1143                 _E("Failed to move %s to %s",
1144                                 from_path, cinfo->result_path);
1145         unlock_dumpdir(lock_fd);
1146
1147         if (remove_dir(cinfo->temp_dir, 1) < 0)
1148                 _E("Failed to delete temp directory");
1149 }
1150
1151 static int wait_for_opt(unsigned int timeout)
1152 {
1153         unsigned int count = 0;
1154
1155         while (check_disk_available(crash_root_path, 0) < 0 && count < timeout) {
1156                 log_kmsg("crash-manager: path %s is not available\n", crash_root_path);
1157                 sleep(1);
1158                 count++;
1159         }
1160         if (count >= timeout) {
1161                 log_kmsg("crash-manager: timeout (%ds) while waiting for %s."
1162                         "Probably /opt is not mounted.\n", timeout, crash_root_path);
1163                 return 0;
1164         }
1165
1166         return 1;
1167 }
1168
1169 static void free_crash_info(struct crash_info *cinfo)
1170 {
1171         free(cinfo->cmd_line);
1172         free(cinfo->cmd_path);
1173         free(cinfo->temp_dir);
1174         free(cinfo->result_path);
1175         free(cinfo->pfx);
1176         free(cinfo->info_path);
1177         free(cinfo->core_path);
1178         free(cinfo->log_path);
1179
1180 #ifdef SYS_ASSERT
1181         free(cinfo->sysassert_cs_path);
1182 #endif
1183 }
1184
1185 int main(int argc, char *argv[])
1186 {
1187         struct crash_info cinfo = {.prstatus_fd = -1};
1188
1189         /* Execute dump_systemstate in parallel */
1190         static pid_t dump_state_pid;
1191         int debug_mode = access(DEBUGMODE_PATH, F_OK) == 0;
1192         int res = 0;
1193
1194         /*
1195          * prctl(PR_SET_DUMPABLE, 0) is not neccessary. Kernel runs the
1196          * crash-manager and sets RLIMIT_CORE to 1 for the process. This is special
1197          * value that prevents from running crash-manager recursively.
1198          */
1199
1200         /* Get Configuration */
1201         if (get_config() < 0) {
1202                 res = EXIT_FAILURE;
1203                 goto exit;
1204         }
1205
1206         /* Prepare paths */
1207         if (!prepare_paths()) {
1208                 res = EXIT_FAILURE;
1209                 goto exit;
1210         }
1211
1212         if (!wait_for_opt(WAIT_FOR_OPT_TIMEOUT_SEC)) {
1213                 res = EXIT_FAILURE;
1214                 goto exit;
1215         }
1216
1217         /* Create crash directories */
1218         if (make_dump_dir() < 0) {
1219                 res = EXIT_FAILURE;
1220                 goto exit;
1221         }
1222
1223         /* Set crash info */
1224         if (set_crash_info(&cinfo, argc, argv) < 0) {
1225                 res = EXIT_FAILURE;
1226                 goto exit;
1227         }
1228
1229 #ifdef SYS_ASSERT
1230         /* Fetch callstack of sys-assert */
1231         get_sysassert_cs(&cinfo);
1232 #endif
1233
1234         if (report_type >= REP_TYPE_FULL) {
1235                 /* Exec dump_systemstate */
1236                 if (!dump_system_state(&cinfo, &dump_state_pid)) {
1237                         res = EXIT_FAILURE;
1238                         _E("Failed to get system state report");
1239                         goto exit;
1240                 }
1241         }
1242
1243         /* Exec crash modules */
1244         if (!execute_crash_modules(&cinfo)) {
1245                 res = EXIT_FAILURE;
1246                 _E("Failed to get basic crash information");
1247                 goto exit;
1248         }
1249
1250         if (report_type >= REP_TYPE_FULL) {
1251                 /* Save shared objects info (file names, bulid IDs, rpm package names) */
1252                 save_so_info(&cinfo);
1253
1254                 /* Wait dump_system_state */
1255                 wait_for_pid(dump_state_pid, NULL);
1256
1257                 /* Tar compression */
1258                 if (allow_zip)
1259                         compress(&cinfo);
1260                 else
1261                         move_dump_data(cinfo.pfx, &cinfo);
1262         } else {
1263                 free(cinfo.result_path);
1264                 if (asprintf(&cinfo.result_path, "%s/%s.info",
1265                                 crash_crash_path, cinfo.name) == -1) {
1266                         cinfo.result_path = NULL;
1267                         _E("asprintf() error: %m");
1268                         res = EXIT_FAILURE;
1269                         goto exit;
1270                 }
1271                 move_dump_data(cinfo.info_path, &cinfo);
1272         }
1273
1274         struct NotifyParams notify_params = {
1275                 .prstatus_fd = cinfo.prstatus_fd,
1276                 .pid = cinfo.pid_info,
1277                 .tid = cinfo.tid_info,
1278                 .cmd_name = basename(cinfo.cmd_line),
1279                 .cmd_path = cinfo.cmd_path,
1280                 .report_path = cinfo.result_path,
1281                 .appid = cinfo.appid,
1282                 .pkgid = cinfo.pkgid
1283         };
1284
1285         send_notify(&notify_params);
1286
1287         /* Release the core pipe as passed by kernel, allowing another
1288          * coredump to be handled.
1289          *
1290          * Due to usage of core_pipe_limit there is limited number of
1291          * crash-manager processes that kernel is going to invoke
1292          * concurrently.  As the next and last step is a _synchronous_
1293          * call to crash-popup we close the descriptor here.
1294          *
1295          * Note: for VIP processes this will likely cause the system
1296          * to reboot without showing popup.
1297          */
1298         close(STDIN_FILENO);
1299
1300         /* launch crash-popup only if the .debugmode file exists */
1301         if (debug_mode)
1302                 launch_crash_popup(&cinfo);
1303
1304 exit:
1305         _I("Exiting with exit code %d", res);
1306         if (cinfo.prstatus_fd >= 0)
1307                 close(cinfo.prstatus_fd);
1308         free(crash_temp_path);
1309         free(crash_root_path);
1310         free(crash_crash_path);
1311
1312         free_crash_info(&cinfo);
1313
1314         return res;
1315 }