1cba5ee8b4407b12084568fdda5286cd7a5fb630
[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 <stdio.h>
20 #include <stdlib.h>
21 #include <stdbool.h>
22 #include <limits.h>
23 #include <unistd.h>
24 #include <libgen.h>
25 #include <fcntl.h>
26 #include <inttypes.h>
27 #include <sys/mman.h>
28 #include <sys/procfs.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <sys/prctl.h>
32 #include <sys/file.h>
33 #include <sys/vfs.h>
34 #include <fcntl.h>
35 #include <gio/gio.h>
36 #include <iniparser.h>
37 #include <tzplatform_config.h>
38 #include <pkgmgr-info.h>
39 #include "crash-manager.h"
40 #include "so-info.h"
41 #include "shared/log.h"
42 #include "shared/util.h"
43 #include "dbus_notify.h"
44
45 #undef LOG_TAG
46 #define LOG_TAG "CRASH_MANAGER"
47
48 /* Parsing */
49 #define KEY_MAX              255
50 #define CRASH_SECTION        "CrashManager"
51
52 /* Crash-popup dbus */
53 #define POPUP_BUS_NAME       "org.tizen.system.popup"
54 #define POPUP_OBJECT_PATH    "/Org/Tizen/System/Popup/Crash"
55 #define POPUP_INTERFACE_NAME POPUP_BUS_NAME".Crash"
56 #define POPUP_METHOD         "PopupLaunch"
57
58 /* Configuration default values */
59 /* note: 0 means unlimited */
60 #define SYSTEM_MAX_USE       0
61 #define SYSTEM_KEEP_FREE     0
62 #define MAX_RETENTION_SEC    0
63 #define MAX_CRASH_DUMP       0
64 #define DUMP_CORE            1
65 #define ALLOW_ZIP            true
66
67 #define APPID_MAX                   128
68 #define PKGNAME_MAX                 128
69
70 #define CRASH_TEMP_SUBDIR    "/temp/"
71 #define CRASH_PATH_SUBDIR    "/dump/"
72
73 #define WAIT_FOR_OPT_TIMEOUT_SEC 60
74
75 #define DEFAULT_COMMAND_TIMEOUT 12*60
76 #define MINICOREDUMPER_TIMEOUT  DEFAULT_COMMAND_TIMEOUT
77 #define CRASH_STACK_TIMEOUT     DEFAULT_COMMAND_TIMEOUT
78 #define ZIP_TIMEOUT             DEFAULT_COMMAND_TIMEOUT
79
80 enum {
81         RET_EXCEED = 1,
82         NUM_EXCEED,
83         USAGE_EXCEED
84 };
85
86 enum {
87         REP_TYPE_INFO = 0,
88         REP_TYPE_FULL
89 };
90
91 #define REP_DEFAULT_TYPE REP_TYPE_FULL
92
93 #define REP_TYPE_FULL_STR "FULL"
94 #define REP_TYPE_INFO_STR "INFO"
95
96 struct file_info {
97         bool   isdir;
98         size_t size;
99         time_t mtime;
100         char   *path;
101 };
102
103 /* Configuration variables */
104 static int system_max_use;
105 static int system_keep_free;
106 static int max_retention_sec;
107 static int max_crash_dump;
108 static int dump_core;
109 static bool allow_zip;
110 static char* crash_root_path;
111 static char* crash_crash_path;
112 static char* crash_temp_path;
113 static int report_type;
114
115 /* Paths and variables */
116 struct crash_info {
117         pid_t pid_info;
118         pid_t tid_info;
119         int uid_info;
120         int gid_info;
121         int sig_info;
122         char *cmd_line;
123         char *cmd_path;
124         time_t time_info;
125         char *temp_dir;
126         char *name;
127         char *result_path;
128         char *pfx;
129         char *info_path;
130         char *core_path;
131         char *log_path;
132         char appid[APPID_MAX];
133         char pkgid[PKGNAME_MAX];
134 #ifdef SYS_ASSERT
135         char *sysassert_cs_path;
136         bool have_sysassert_report;
137 #endif
138         int prstatus_fd;
139 };
140
141 /* pkgmgrinfo filter list function for getting application ID */
142 static int appinfo_get_appid_func(pkgmgrinfo_appinfo_h handle,
143                 void *user_data)
144 {
145         char *str = NULL;
146         int ret = PMINFO_R_ERROR;
147
148         pkgmgrinfo_appinfo_get_appid(handle, &str);
149         if (str) {
150                 (*(char **)user_data) = strdup(str);
151                 ret = PMINFO_R_OK;
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 int dump_system_state(const struct crash_info *cinfo)
689 {
690         int ret;
691         char command[PATH_MAX];
692
693         ret = snprintf(command, sizeof(command),
694                         "/usr/bin/dump_systemstate -d -k -j -f '%s'",
695                         cinfo->log_path);
696         if (ret < 0) {
697                 _E("Failed to snprintf for dump_systemstate command");
698                 return -1;
699         }
700
701         return system_command_parallel(command);
702 }
703
704 static void save_so_info(const struct crash_info *cinfo)
705 {
706         char maps_path[PATH_MAX];
707         char so_info_path[PATH_MAX];
708         snprintf(maps_path, sizeof(maps_path), "/proc/%d/maps",
709                  cinfo->pid_info);
710
711         snprintf(so_info_path, sizeof(so_info_path),
712                          "%s/%s.%s", cinfo->pfx, cinfo->name, "so_info");
713
714         get_and_save_so_info(maps_path, so_info_path);
715 }
716
717 // These macros are used in execute_minicoredump() and execute_crash_stack()
718 #define SNPRINTF_OR_EXIT_W(name, format, member) if (snprintf(name##_str, sizeof(name##_str), format, cinfo->member) < 0) goto out;
719 #define SNPRINTF_OR_EXIT(name, format) SNPRINTF_OR_EXIT_W(name, format, name##_info)
720
721 static int execute_minicoredump(struct crash_info *cinfo)
722 {
723         char *coredump_name = NULL;
724         char *prstatus_fd_str = NULL;
725         int ret = -1;
726
727         if (asprintf(&coredump_name, "%s.coredump", cinfo->name) == -1
728             || asprintf(&prstatus_fd_str, "%d", cinfo->prstatus_fd) == -1) {
729                 _E("Unable to allocate memory");
730                 goto out;
731         }
732
733         char pid_str[11], uid_str[11], gid_str[11], sig_str[11], time_str[11];
734
735         SNPRINTF_OR_EXIT(pid, "%d")
736         SNPRINTF_OR_EXIT(uid, "%d")
737         SNPRINTF_OR_EXIT(gid, "%d")
738         SNPRINTF_OR_EXIT(sig, "%d")
739         SNPRINTF_OR_EXIT(time, "%ld")
740
741         /* Execute minicoredumper */
742         char *args[] = {
743                         MINICOREDUMPER_BIN_PATH,   // minicoredumper filename path
744                         pid_str,                   // %p - pid
745                         uid_str,                   // %u - UID
746                         gid_str,                   // %g - GID
747                         sig_str,                   // %s - number of signal
748                         time_str,                  // %t - time of dump
749                         "localhost",               // %h - hostname
750                         "core",                    // %e - exe name (need for result filename)
751                         MINICOREDUMPER_CONFIG_PATH, // config file
752                         "-d",
753                         cinfo->pfx,            // temp dir
754                         "-o",
755                         coredump_name,             // coredump filename
756                         "-P",
757                         prstatus_fd_str,
758                         NULL
759                         };
760
761         _D("    %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s",
762                 args[0], args[1], args[2], args[3],
763                 args[4], args[5], args[6], args[7],
764                 args[8], args[9], args[10], args[11],
765                 args[12], args[13], args[14]);
766
767         run_command_timeout(args[0], args, NULL, MINICOREDUMPER_TIMEOUT);
768         ret = 0;
769
770         /* Minicoredumper must be executed to dump at least PRSTATUS for
771            other tools, coredump, however, might have been disabled. */
772         if (!dump_core) {
773                 int ret = -1;
774                 int errno_unlink = 0;
775                 int dirfd = open(cinfo->pfx, O_DIRECTORY);
776                 if (dirfd != -1) {
777                         ret = unlinkat(dirfd, coredump_name, 0);
778                         errno_unlink = errno;
779                         close(dirfd);
780                         errno = errno_unlink; /* for %m below */
781                 }
782
783                 if (ret != 0)
784                         _E("Saving core disabled - removing coredump %s/%s failed: %m",
785                            cinfo->pfx, coredump_name);
786                 else
787                         _D("Saving core disabled - removed coredump %s/%s",
788                            cinfo->pfx, coredump_name);
789         }
790
791 out:
792         free(coredump_name);
793         free(prstatus_fd_str);
794
795         return ret;
796 }
797
798 static int execute_crash_stack(const struct crash_info *cinfo)
799 {
800         int ret = -1;
801         int fd = -1;
802
803         char pid_str[11], tid_str[11], sig_str[11], prstatus_fd_str[11];
804
805         SNPRINTF_OR_EXIT(pid, "%d")
806         SNPRINTF_OR_EXIT(tid, "%d")
807         SNPRINTF_OR_EXIT(sig, "%d")
808         SNPRINTF_OR_EXIT_W(prstatus_fd, "%d", prstatus_fd)
809
810         /* Execute crash-stack */
811         char *args[] = {
812                         CRASH_STACK_PATH,
813                         "--pid",
814                         pid_str,
815                         "--tid",
816                         tid_str,
817                         "--sig",
818                         sig_str,
819                         "--prstatus_fd",
820                         prstatus_fd_str,
821                         NULL
822         };
823
824         _D("    %s %s %s %s %s %s %s %s %s",
825                 args[0], args[1], args[2], args[3],
826                 args[4], args[5], args[6], args[7], args[8]);
827
828         fd = open(cinfo->info_path, O_WRONLY | O_CREAT, 0600);
829         if (fd < 0) {
830                 _E("open %s error: %m", cinfo->info_path);
831                 goto out;
832         }
833
834         ret = run_command_write_fd_timeout(CRASH_STACK_PATH, args, NULL, fd, NULL, 0, CRASH_STACK_TIMEOUT);
835
836 out:
837         if (fd >= 0)
838                 close(fd);
839
840         return ret;
841 }
842
843 #undef SNPRINTF_OR_EXIT
844 #undef SNPRINTF_OR_EXIT_W
845
846 static int execute_crash_modules(struct crash_info *cinfo)
847 {
848         _D("Execute crash module: ");
849
850         if (execute_minicoredump(cinfo) < 0) {
851                 _E("Failed to run minicoredumper - can not continue");
852                 return -1;
853         }
854
855 #ifdef SYS_ASSERT
856         /* Use process_vm_readv() version as fallback if sys-assert
857          * failed to generate report */
858         if (cinfo->have_sysassert_report)
859                 return -1;
860 #endif
861         execute_crash_stack(cinfo);
862
863         return 0;
864 }
865
866 static int lock_dumpdir(void)
867 {
868         int fd;
869
870         if ((fd = open(crash_crash_path, O_RDONLY | O_DIRECTORY)) < 0) {
871                 _E("Failed to open %s", crash_crash_path);
872                 return -1;
873         }
874
875         if (flock(fd, LOCK_EX) < 0) {
876                 _E("Failed to flock (LOCK)");
877                 close(fd);
878                 return -1;
879         }
880
881         return fd;
882 }
883
884 static void unlock_dumpdir(int fd)
885 {
886         if (flock(fd, LOCK_UN) < 0)
887                 _E("Failed to unlink (UNLOCK)");
888         close(fd);
889 }
890
891 static int dump_filter(const struct dirent *de)
892 {
893         if (de->d_name[0] == '.')
894                 return 0;
895         return 1;
896 }
897
898 static int mtime_cmp(const void *_a, const void *_b)
899 {
900         const struct file_info *a = _a;
901         const struct file_info *b = _b;
902
903         if (a->mtime < b->mtime)
904                 return -1;
905         if (a->mtime > b->mtime)
906                 return 1;
907         return 0;
908 }
909
910 static int scan_dump(struct file_info **dump_list, off_t *usage)
911 {
912         struct file_info *temp_list;
913         struct dirent **scan_list = NULL;
914         struct stat st;
915         int i, scan_num, dump_num = 0;
916         int fd;
917
918         if ((fd = open(crash_crash_path, O_DIRECTORY)) < 0) {
919                 _E("Failed to open %s", crash_crash_path);
920                 return -1;
921         }
922
923         scan_num = scandir(crash_crash_path, &scan_list, &dump_filter, NULL);
924         if (scan_num < 0) {
925                 close(fd);
926                 return -1;
927         }
928
929         temp_list = (struct file_info *)calloc(scan_num,
930                         sizeof(struct file_info));
931         if (!temp_list) {
932                 _E("Failed to calloc for dump list");
933                 goto exit;
934         }
935
936         for (i = 0; i < scan_num; i++) {
937                 if (fstatat(fd, scan_list[i]->d_name, &st, 0) < 0) {
938                         _E("Failed to fstatat");
939                         continue;
940                 }
941
942                 if (asprintf(&(temp_list[dump_num].path), "%s/%s",
943                                         crash_crash_path, scan_list[i]->d_name) < 0) {
944                         _E("Failed to asprintf");
945                         continue;
946                 }
947
948                 if (scan_list[i]->d_type == DT_DIR) {
949                         temp_list[dump_num].isdir = 1;
950                         temp_list[dump_num].size =
951                                 get_directory_usage(temp_list[dump_num].path);
952                 } else {
953                         temp_list[dump_num].isdir = 0;
954                         temp_list[dump_num].size = st.st_size;
955                 }
956                 temp_list[dump_num].mtime = st.st_mtime;
957                 dump_num++;
958         }
959
960         if (dump_num <= 0) {
961                 free(temp_list);
962                 goto exit;
963         }
964
965         if (dump_num != scan_num) {
966                 struct file_info *const tmp = (struct file_info *)realloc(temp_list,
967                                 dump_num * sizeof(struct file_info));
968                 if (!tmp) {
969                         free(temp_list);
970                         dump_num = -1;
971                         goto exit;
972                 }
973                 temp_list = tmp;
974         }
975
976         qsort(temp_list, dump_num, sizeof(struct file_info), mtime_cmp);
977
978         *usage = 0;
979         for (i = 0; i < dump_num; i++) {
980                 _D("[%d] path: %s(%s), size: %zu kb, mtime: %s",
981                                 i,
982                                 temp_list[i].path,
983                                 temp_list[i].isdir ? "DIR" : "FILE",
984                                 temp_list[i].size / 1024,
985                                 ctime(&(temp_list[i].mtime)));
986                 *usage += temp_list[i].size;
987         }
988         *dump_list = temp_list;
989 exit:
990         for (i = 0; i < scan_num; i++)
991                 free(scan_list[i]);
992         free(scan_list);
993         close(fd);
994
995         return dump_num;
996 }
997
998 static int check_disk_available(const char *path, int check_size)
999 {
1000         struct statfs lstatfs;
1001         int avail_size = 0;
1002
1003         if (!path)
1004                 return -1;
1005
1006         if (statfs(path, &lstatfs) < 0)
1007                 return -1;
1008         avail_size = (int)(lstatfs.f_bavail * (lstatfs.f_bsize / 1024));
1009
1010         if (check_size > avail_size) {
1011                 _I("avail_size is (%d)", avail_size);
1012                 return -1;
1013         }
1014
1015         return 0;
1016 }
1017
1018 static int remove_file(struct file_info file)
1019 {
1020         if (file.isdir)
1021                 return remove_dir(file.path, 1);
1022         else
1023                 return unlink(file.path);
1024 }
1025
1026 static void clean_dump(void)
1027 {
1028         struct file_info *dump_list = NULL;
1029         int i, scan_num, dump_num, remove_flag;
1030         off_t usage = 0;
1031         time_t cur_time;
1032
1033         scan_num = scan_dump(&dump_list, &usage);
1034         if (scan_num <= 0)
1035                 return;
1036
1037         dump_num = scan_num;
1038
1039         cur_time = time(NULL);
1040
1041         for (i = 0; i < scan_num; i++) {
1042                 remove_flag = 0;
1043
1044                 /* Retention time check */
1045                 if (max_retention_sec &&
1046                         dump_list[i].mtime > 0 &&
1047                         dump_list[i].mtime + max_retention_sec < cur_time)
1048                         remove_flag = RET_EXCEED;
1049                 /* Check the number of dumps */
1050                 else if (max_crash_dump &&
1051                         0 < dump_num && max_crash_dump < dump_num)
1052                         remove_flag = NUM_EXCEED;
1053                 /* Check the max system use size */
1054                 else if (system_max_use &&
1055                         0 < dump_num && system_max_use < usage / 1024)
1056                         remove_flag = USAGE_EXCEED;
1057
1058                 switch (remove_flag) {
1059                 case RET_EXCEED:
1060                         _I("Reached the maximum retention time %d, so remove (%s)",
1061                                         max_retention_sec, dump_list[i].path);
1062                         break;
1063                 case NUM_EXCEED:
1064                         _I("Reached the maximum number of dump %d/%d, so remove (%s)",
1065                                         dump_num, max_crash_dump,
1066                                         dump_list[i].path);
1067                         break;
1068                 case USAGE_EXCEED:
1069                         _I("Reached the maximum disk usage %" PRId64 "/%d kb, so remove (%s)",
1070                                         usage / 1024, system_max_use,
1071                                         dump_list[i].path);
1072                         break;
1073                 default:
1074                         break;
1075                 }
1076
1077                 if (!remove_flag)
1078                         continue;
1079
1080                 if (remove_file(dump_list[i]) < 0) {
1081                         _E("Failed to remove %s", dump_list[i].path);
1082                         continue;
1083                 }
1084                 dump_num--;
1085                 usage -= dump_list[i].size;
1086         }
1087
1088         /* Check disk free space to keep */
1089         if (system_keep_free &&
1090                         check_disk_available(crash_root_path,
1091                                              system_keep_free) < 0) {
1092                 _I("Disk is not available! so set the maximum number of dump to 1");
1093                 max_crash_dump = 1;
1094         }
1095
1096         for (i = 0; i < dump_num; i++)
1097                 free(dump_list[i].path);
1098         free(dump_list);
1099 }
1100
1101 static void compress(struct crash_info *cinfo)
1102 {
1103         int ret, lock_fd;
1104         char zip_path[PATH_MAX];
1105         char cwd[PATH_MAX];
1106
1107         ret = snprintf(zip_path, sizeof(zip_path), "%s/report.zip",
1108                         cinfo->temp_dir);
1109         if (ret < 0) {
1110                 _E("Failed to snprintf for zip path");
1111                 return;
1112         }
1113
1114         if (getcwd(cwd, sizeof(cwd)) == NULL) {
1115                 _E("getcwd() error: %m\n");
1116                 return;
1117         }
1118
1119         if (chdir(cinfo->temp_dir) == -1) {
1120                 _E("chdir() to %s error: %m\n", cinfo->temp_dir);
1121                 return;
1122         }
1123
1124         char *args[] = {
1125                 "/bin/zip",
1126                 "-y",
1127                 "-r",
1128                 zip_path,
1129                 cinfo->name,
1130                 NULL
1131         };
1132
1133         run_command_timeout(args[0], args, NULL, ZIP_TIMEOUT);
1134
1135         if (chdir(cwd) == -1) {
1136                 _E("chdir() to %s error: %m\n", cwd);
1137                 return;
1138         }
1139
1140         if ((lock_fd = lock_dumpdir()) < 0)
1141                 return;
1142         if (!rename(zip_path, cinfo->result_path))
1143                 clean_dump();
1144         else
1145                 _E("Failed to move %s to %s", zip_path, cinfo->result_path);
1146         unlock_dumpdir(lock_fd);
1147
1148         if (remove_dir(cinfo->temp_dir, 1) < 0)
1149                 _E("Failed to delete temp directory");
1150 }
1151
1152 static void move_dump_data(const char *from_path, const struct crash_info *cinfo)
1153 {
1154         int lock_fd;
1155
1156         if ((lock_fd = lock_dumpdir()) < 0)
1157                 return;
1158         if (!rename(from_path, cinfo->result_path))
1159                 clean_dump();
1160         else
1161                 _E("Failed to move %s to %s",
1162                                 from_path, cinfo->result_path);
1163         unlock_dumpdir(lock_fd);
1164
1165         if (remove_dir(cinfo->temp_dir, 1) < 0)
1166                 _E("Failed to delete temp directory");
1167 }
1168
1169 static int wait_for_opt(unsigned int timeout)
1170 {
1171         unsigned int count = 0;
1172
1173         while (check_disk_available(crash_root_path, 0) < 0 && count < timeout) {
1174                 log_kmsg("crash-manager: path %s is not available\n", crash_root_path);
1175                 sleep(1);
1176                 count++;
1177         }
1178         if (count >= timeout) {
1179                 log_kmsg("crash-manager: timeout (%ds) while waiting for %s."
1180                         "Probably /opt is not mounted.\n", timeout, crash_root_path);
1181                 return 0;
1182         }
1183
1184         return 1;
1185 }
1186
1187 static void free_crash_info(struct crash_info *cinfo)
1188 {
1189         free(cinfo->cmd_line);
1190         free(cinfo->cmd_path);
1191         free(cinfo->temp_dir);
1192         free(cinfo->result_path);
1193         free(cinfo->pfx);
1194         free(cinfo->info_path);
1195         free(cinfo->core_path);
1196         free(cinfo->log_path);
1197
1198 #ifdef SYS_ASSERT
1199         free(cinfo->sysassert_cs_path);
1200 #endif
1201 }
1202
1203 int main(int argc, char *argv[])
1204 {
1205         struct crash_info cinfo = {0};
1206
1207         /* Execute dump_systemstate in parallel */
1208         static int dump_state_pid;
1209         int debug_mode = access(DEBUGMODE_PATH, F_OK) == 0;
1210         int res = 0;
1211
1212         /*
1213          * prctl(PR_SET_DUMPABLE, 0) is not neccessary. Kernel runs the
1214          * crash-manager and sets RLIMIT_CORE to 1 for the process. This is special
1215          * value that prevents from running crash-manager recursively.
1216          */
1217
1218         /* Get Configuration */
1219         if (get_config() < 0) {
1220                 res = EXIT_FAILURE;
1221                 goto exit;
1222         }
1223
1224         /* Prepare paths */
1225         if (!prepare_paths()) {
1226                 res = EXIT_FAILURE;
1227                 goto exit;
1228         }
1229
1230         if (!wait_for_opt(WAIT_FOR_OPT_TIMEOUT_SEC)) {
1231                 res = EXIT_FAILURE;
1232                 goto exit;
1233         }
1234
1235         /* Create crash directories */
1236         if (make_dump_dir() < 0) {
1237                 res = EXIT_FAILURE;
1238                 goto exit;
1239         }
1240
1241         /* Set crash info */
1242         if (set_crash_info(&cinfo, argc, argv) < 0) {
1243                 res = EXIT_FAILURE;
1244                 goto exit;
1245         }
1246
1247 #ifdef SYS_ASSERT
1248         /* Fetch callstack of sys-assert */
1249         get_sysassert_cs(&cinfo);
1250 #endif
1251
1252         if (report_type >= REP_TYPE_FULL) {
1253                 /* Exec dump_systemstate */
1254                 dump_state_pid = dump_system_state(&cinfo);
1255
1256                 if (dump_state_pid < 0) {
1257                         res = EXIT_FAILURE;
1258                         goto exit;
1259                 }
1260         }
1261         /* Exec crash modules */
1262         if (execute_crash_modules(&cinfo) < 0) {
1263                 res = EXIT_FAILURE;
1264                 goto exit;
1265         }
1266
1267         if (report_type >= REP_TYPE_FULL) {
1268                 /* Save shared objects info (file names, bulid IDs, rpm package names) */
1269                 save_so_info(&cinfo);
1270
1271                 /* Wait dump_system_state */
1272                 wait_system_command(dump_state_pid);
1273
1274                 /* Tar compression */
1275                 if (allow_zip)
1276                         compress(&cinfo);
1277                 else
1278                         move_dump_data(cinfo.pfx, &cinfo);
1279         } else {
1280                 free(cinfo.result_path);
1281                 if (asprintf(&cinfo.result_path, "%s/%s.info",
1282                                 crash_crash_path, cinfo.name) == -1) {
1283                         cinfo.result_path = NULL;
1284                         _E("asprintf() error: %m");
1285                         res = EXIT_FAILURE;
1286                         goto exit;
1287                 }
1288                 move_dump_data(cinfo.info_path, &cinfo);
1289         }
1290
1291         struct NotifyParams notify_params = {
1292                 .prstatus_fd = cinfo.prstatus_fd,
1293                 .pid = cinfo.pid_info,
1294                 .tid = cinfo.tid_info,
1295                 .cmd_name = basename(cinfo.cmd_line),
1296                 .cmd_path = cinfo.cmd_path,
1297                 .report_path = cinfo.result_path,
1298                 .appid = cinfo.appid,
1299                 .pkgid = cinfo.pkgid
1300         };
1301
1302         send_notify(&notify_params);
1303
1304         /* Release the core pipe as passed by kernel, allowing another
1305          * coredump to be handled.
1306          *
1307          * Due to usage of core_pipe_limit there is limited number of
1308          * crash-manager processes that kernel is going to invoke
1309          * concurrently.  As the next and last step is a _synchronous_
1310          * call to crash-popup we close the descriptor here.
1311          *
1312          * Note: for VIP processes this will likely cause the system
1313          * to reboot without showing popup.
1314          */
1315         close(STDIN_FILENO);
1316
1317         /* launch crash-popup only if the .debugmode file exists */
1318         if (debug_mode)
1319                 launch_crash_popup(&cinfo);
1320
1321 exit:
1322         close(cinfo.prstatus_fd);
1323         free(crash_temp_path);
1324         free(crash_root_path);
1325         free(crash_crash_path);
1326
1327         free_crash_info(&cinfo);
1328
1329         return res;
1330 }