1043d2f9020ef803a47d1a70d216a8c4067b429f
[platform/core/system/system-server.git] / src / bs / bs.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2012 - 2013 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
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <limits.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <sys/statfs.h>
29 #include <sys/types.h>
30 #include <grp.h>
31 #include <dirent.h>
32 #include <Ecore_File.h>
33 #include "core/log.h"
34 #include "core/launch.h"
35 #include "core/devices.h"
36
37 #define CRASH_PID_MAX 7
38 #define CRASH_MODE_MAX 2
39 #define CRASH_TIME_MAX 65
40 #define CRASH_POPUP_ON  1
41 #define CRASH_POPUP_OFF 0
42 #define CRASH_CHECK_SIZE (512 * 1024)
43 #define CRASH_CHECK_DISK_PATH   "/opt/usr"
44 #define CRASH_LIMIT_NUM 5
45 #define CRASH_ARG_NUM 6
46 #define CRASH_DELIMITER "|"
47 #define CRASH_VERIFY_MAX 5
48 #define CRASH_PROCESSNAME_MAX NAME_MAX
49 #define CRASH_EXEPATH_MAX NAME_MAX
50 #define CRASH_ARG_MAX (CRASH_PROCESSNAME_MAX + CRASH_EXEPATH_MAX + CRASH_TIME_MAX + CRASH_PID_MAX + CRASH_MODE_MAX + CRASH_VERIFY_MAX)
51 #define CRASH_NOTI_DIR          "/opt/share/crash"
52 #define CRASH_NOTI_FILE         "curbs.log"
53 #define CRASH_NOTI_PATH CRASH_NOTI_DIR"/"CRASH_NOTI_FILE
54 #define CRASH_COREDUMP_PATH             "/opt/usr/share/crash/core"
55 #define CRASH_DUMP_PATH         "/opt/usr/share/crash/dump"
56 #define CRASH_INFO_PATH         "/opt/share/crash/info"
57 #define CRASH_WORKER_PATH       "/usr/bin/crash-worker"
58 #define CRASH_POPUP_PATH        "/usr/apps/org.tizen.crash-popup/bin/crash-popup"
59
60 static int noti_fd;
61 static int add_noti(void);
62 struct crash_arg
63 {
64         char crash_mode[CRASH_MODE_MAX];
65         char crash_processname[CRASH_PROCESSNAME_MAX];
66         char crash_timestr[CRASH_TIME_MAX];
67         char crash_pid[CRASH_PID_MAX];
68         char crash_exepath[CRASH_EXEPATH_MAX];
69         char crash_verify[CRASH_VERIFY_MAX];
70 };
71
72 static int is_running_process(pid_t pid)
73 {
74         char buf[PATH_MAX + 1];
75         snprintf(buf, sizeof(buf), "/proc/%d", pid);
76         if (!access(buf, R_OK))
77                 return 1;
78         return 0;
79 }
80 static int make_noti_file(const char *path, const char *file)
81 {
82         int fd;
83         char buf[PATH_MAX];
84         gid_t group_id;
85         struct group *group_entry;
86         mode_t old_mask;
87
88         snprintf(buf, sizeof(buf), "%s/%s", path, file);        /* buf - full path to file */
89         if (access(buf, F_OK) == 0)                             /* if file exists then return -1 */
90                 return -1;
91
92         /* save old mask and set new calling process's file mode creation mask */
93         old_mask = umask(0);
94
95         mkdir(path, 0775);              /* make directory, if exest then errno==EEXIST */
96         group_entry = getgrnam("crash");                /* need to find out group ID of "crash" group name */
97         if (group_entry == NULL) {
98                 umask(old_mask);        /* restore old file mask */
99                 return -1;
100         }
101         chown(path, 0, group_entry->gr_gid);                    /* chown root:crash */
102         if ((fd = open(buf, O_CREAT, 0666)) < 0) {      /* create crash file */
103                 umask(old_mask);                        /* restore old file mask */
104                 return -1;
105         }
106         fchown(fd, 0, group_entry->gr_gid);     /* chown root:crash */
107         close(fd);
108         umask(old_mask);                /* restore old file mask */
109
110         return 0;
111 }
112 static int make_coredump_dir(void)
113 {
114         mode_t old_mask;
115         gid_t group_id;
116         struct group *group_entry;
117
118         if (access(CRASH_COREDUMP_PATH, F_OK) == 0)                             /* if file exists then return -1 */
119                 return -1;
120
121         /* save old mask and set new calling process's file mode creation mask */
122         old_mask = umask(0);
123
124         mkdir(CRASH_COREDUMP_PATH, 0775);               /* make directory, if exest then errno==EEXIST */
125         group_entry = getgrnam("crash");                /* need to find out group ID of "crash" group name*/
126         if (group_entry == NULL) {
127                 umask(old_mask);        /* restore old file mask */
128                 return -1;
129         }
130         chown(CRASH_COREDUMP_PATH, 0, group_entry->gr_gid);                     /* chown root:crash */
131         umask(old_mask);                /* restore old file mask */
132
133         return 0;
134 }
135 static int make_info_dir(void)
136 {
137         mode_t old_mask;
138         gid_t group_id;
139         struct group *group_entry;
140
141         if (access(CRASH_INFO_PATH, F_OK) == 0)                         /* if file exists then return -1 */
142                 return -1;
143
144         /* save old mask and set new calling process's file mode creation mask */
145         old_mask = umask(0);
146
147         mkdir(CRASH_INFO_PATH, 0775);           /* make directory, if exest then errno==EEXIST */
148         group_entry = getgrnam("crash");                /* need to find out group ID of "crash" group name*/
149         if (group_entry == NULL) {
150                 umask(old_mask);        /* restore old file mask */
151                 return -1;
152         }
153         chown(CRASH_INFO_PATH, 0, group_entry->gr_gid);                 /* chown root:crash */
154         umask(old_mask);                /* restore old file mask */
155
156         return 0;
157 }
158 static int clean_coredump_dir(void)
159 {
160         DIR *dir;
161         struct dirent *dp;
162         int dfd;
163         dir = opendir(CRASH_COREDUMP_PATH);
164         if (!dir) {
165                 _E("opendir failed");
166                 return 0;
167         }
168         dfd = dirfd(dir);
169         if (dfd < 0) return 0;
170         while ((dp = readdir(dir)) != NULL) {
171                 const char *name = dp->d_name;
172                 /* always skip "." and ".." */
173                 if (name[0] == '.') {
174                         if (name[1] == 0) continue;
175                         if ((name[1] == '.') && (name[2] == 0)) continue;
176                 }
177                 if (unlinkat(dfd, name, 0) < 0) {
178                         _E("FAIL: clean_coredump_dir (%s)",name);
179                         continue;
180                 }
181         }
182         closedir(dir);
183         return 1;
184 }
185 static int clean_dump_dir(void)
186 {
187         DIR *dir;
188         struct dirent *dp;
189         char dirname[PATH_MAX];
190
191         dir = opendir(CRASH_DUMP_PATH);
192         if (!dir) {
193                 _E("opendir failed");
194                 return 0;
195         }
196         while ((dp = readdir(dir)) != NULL) {
197                 if (dp->d_type == DT_DIR) {
198                         const char *name = dp->d_name;
199                         /* always skip "." and ".." */
200                         if (name[0] == '.') {
201                                 if (name[1] == 0) continue;
202                                 if ((name[1] == '.') && (name[2] == 0)) continue;
203                         }
204                         snprintf(dirname, sizeof(dirname), "%s/%s", CRASH_DUMP_PATH, name);
205                         if (ecore_file_recursive_rm(dirname) == EINA_FALSE) {
206                                 _E("FAIL: clean_dump_dir (%s)",dirname);
207                                 continue;
208                         }
209                 }
210         }
211         closedir(dir);
212         return 1;
213 }
214 static int clean_info_dir(void)
215 {
216         DIR *dir;
217         struct dirent *dp;
218         int dfd;
219         dir = opendir(CRASH_INFO_PATH);
220         if (!dir) {
221                 _E("opendir failed");
222                 return 0;
223         }
224         dfd = dirfd(dir);
225         if (dfd < 0) return 0;
226         while ((dp = readdir(dir)) != NULL) {
227                 const char *name = dp->d_name;
228                 /* always skip "." and ".." */
229                 if (name[0] == '.') {
230                         if (name[1] == 0) continue;
231                         if ((name[1] == '.') && (name[2] == 0)) continue;
232                 }
233                 if (unlinkat(dfd, name, 0) < 0) {
234                         _E("FAIL: clean_info_dir (%s)",name);
235                         continue;
236                 }
237         }
238         closedir(dir);
239         return 1;
240 }
241 static int crash_arg_parser(char *linebuffer, struct crash_arg *arg)
242 {
243         char *ptr = NULL;
244         int verify_num = 0;
245         int verify_arg_num = 0;
246
247         if (linebuffer == NULL || arg == NULL) {
248                 _E("crash_arg_parser input arguments is NULL");
249                 return -1;
250         }
251         ptr = strtok(linebuffer, CRASH_DELIMITER);
252         if (ptr == NULL) {
253                 _E("can't strtok linebuffer ptr(%s)", ptr);
254                 return -1;
255         }
256         snprintf(arg->crash_mode, CRASH_MODE_MAX, "%s",  ptr);
257         ptr = strtok(NULL, CRASH_DELIMITER);
258         if (ptr == NULL) {
259                 _E("can't strtok linebuffer ptr(%s)", ptr);
260                 return -1;
261         }
262         snprintf(arg->crash_processname, CRASH_PROCESSNAME_MAX, "%s",  ptr);
263         ptr = strtok(NULL, CRASH_DELIMITER);
264         if (ptr == NULL) {
265                 _E("can't strtok linebuffer ptr(%s)", ptr);
266                 return -1;
267         }
268         snprintf(arg->crash_timestr, CRASH_TIME_MAX, "%s", ptr);
269         ptr = strtok(NULL, CRASH_DELIMITER);
270         if (ptr == NULL) {
271                 _E("can't strtok linebuffer ptr(%s)", ptr);
272                 return -1;
273         }
274         snprintf(arg->crash_pid, CRASH_PID_MAX, "%s", ptr);
275         ptr = strtok(NULL, CRASH_DELIMITER);
276         if (ptr == NULL) {
277                 _E("can't strtok linebuffer ptr(%s)", ptr);
278                 return -1;
279         }
280         snprintf(arg->crash_exepath, CRASH_EXEPATH_MAX, "%s", ptr);
281         ptr = strtok(NULL, CRASH_DELIMITER);
282         if (ptr == NULL) {
283                 _E("can't strtok linebuffer ptr(%s)", ptr);
284                 return -1;
285         }
286         snprintf(arg->crash_verify, CRASH_VERIFY_MAX, "%s", ptr);
287         verify_num = strlen(arg->crash_processname) + strlen(arg->crash_exepath);
288         verify_arg_num = atoi(arg->crash_verify);
289         _D("vnum %d vanum %d", verify_num, verify_arg_num);
290         if (verify_num == verify_arg_num)
291                 return 1;
292         else
293                 return 0;
294 }
295 static void launch_crash_worker(const char *filename, int popup_on)
296 {
297         static int popup_pid = 0;
298         FILE *fp;
299         int ret = -1;
300         int len = 0;
301         char linebuffer[CRASH_ARG_MAX] = {0,};
302         char crash_worker_args[CRASH_ARG_MAX] = {0,};
303         struct crash_arg parsing_arg;
304         fp = fopen(filename, "r");
305         if (fp == NULL) {
306                 return;
307         }
308         /* launch crash process */
309         while (fgets(linebuffer, CRASH_ARG_MAX, fp) != NULL) {
310                 len = strlen(linebuffer);
311                 if (!len || linebuffer[len - 1] != '\n') {
312                         _E("crash inoti msg  must be terminated with new line character\n");
313                         break;
314                 }
315                 /* change last caracter from \n to \0 */
316                 linebuffer[strlen(linebuffer) - 1] = '\0';
317                 if (crash_arg_parser(linebuffer, &parsing_arg) != 1)
318                         continue;
319                 snprintf(crash_worker_args, sizeof(crash_worker_args), "%s %s %s %s %s",
320                                 parsing_arg.crash_mode, parsing_arg.crash_processname,
321                                 parsing_arg.crash_timestr, parsing_arg.crash_pid, parsing_arg.crash_exepath);
322                 _D("crash_worker args(%s)", crash_worker_args);
323                 _D("(%s%s%s)", parsing_arg.crash_mode,
324                                 parsing_arg.crash_processname, parsing_arg.crash_timestr);
325                 ret = ss_launch_evenif_exist (CRASH_WORKER_PATH, crash_worker_args);
326                 if (ret > 0) {
327                         char buf[PATH_MAX];
328                         FILE *fpAdj;
329                         snprintf(buf, sizeof(buf), "/proc/%d/oom_adj", ret);
330                         fpAdj = fopen(buf, "w");
331                         if (fpAdj != NULL) {
332                                 fprintf(fpAdj, "%d", (-17));
333                                 fclose(fpAdj);
334                         }
335                 }
336                 if (popup_on) {
337                         if (!is_running_process(popup_pid))
338                                 popup_pid = ss_launch_evenif_exist (CRASH_POPUP_PATH, parsing_arg.crash_processname);
339                 }
340
341                 if (popup_pid < 0) {
342                         _E("popup failed)\n");
343                         break;
344                 }
345         }
346         fclose(fp);
347         if (ret != -1) {
348                 fp = fopen(filename, "w");
349                 if (fp == NULL) {
350                         return;
351                 }
352                 fclose(fp);
353         }
354         return;
355 }
356
357 static Ecore_File_Monitor *crash_file_monitor;
358
359 static Ecore_File_Monitor_Cb __crash_file_cb(void *data, Ecore_File_Monitor *em, Ecore_File_Event event, const char *path)
360 {
361         switch (event) {
362         case ECORE_FILE_EVENT_DELETED_DIRECTORY:
363         case ECORE_FILE_EVENT_DELETED_SELF:
364                 if (make_noti_file(CRASH_NOTI_DIR, CRASH_NOTI_FILE) < 0) {
365                         launch_crash_worker(path, CRASH_POPUP_ON);
366                 }
367                 break;
368         case ECORE_FILE_EVENT_MODIFIED:
369         default:
370                 launch_crash_worker(path, CRASH_POPUP_ON);
371                 break;
372         }
373         return NULL;
374 }
375 static int _get_file_count(char *path)
376 {
377         DIR *dir;
378         struct dirent *dp;
379         int count = 0;
380         dir = opendir(path);
381         if (!dir) return 0;
382         while ((dp = readdir(dir)) != NULL) {
383                 const char *name = dp->d_name;
384                 /* always skip "." and ".." */
385                 if (name[0] == '.') {
386                         if (name[1] == 0) continue;
387                         if ((name[1] == '.') && (name[2] == 0)) continue;
388                 }
389                 count++;
390         }
391         closedir(dir);
392         return count;
393 }
394 /* check disk available size */
395 static int _check_disk_available(void)
396 {
397         struct statfs lstatfs;
398         int avail_size = 0;
399         if (statfs(CRASH_CHECK_DISK_PATH, &lstatfs) < 0)
400                 return -1;
401         avail_size = (int)(lstatfs.f_bavail * (lstatfs.f_bsize/1024));
402         if (CRASH_CHECK_SIZE > avail_size)
403                 return -1;
404         return 1;
405 }
406
407 static void bs_init(void *data)
408 {
409         if (make_noti_file(CRASH_NOTI_DIR, CRASH_NOTI_FILE) < 0) {
410                 _E("make_noti_file() failed");
411                 launch_crash_worker(CRASH_NOTI_PATH, CRASH_POPUP_OFF);
412         }
413         if (make_info_dir() < 0) {
414                 if (CRASH_LIMIT_NUM < _get_file_count(CRASH_INFO_PATH))
415                         clean_info_dir();
416         }
417         if (make_coredump_dir() < 0) {
418                 if (CRASH_LIMIT_NUM < _get_file_count(CRASH_COREDUMP_PATH)
419                                         || _check_disk_available() < 0)
420                         clean_coredump_dir();
421         }
422         if (CRASH_LIMIT_NUM < _get_file_count(CRASH_DUMP_PATH))
423                 clean_dump_dir();
424
425         if (ecore_file_init() == 0) {
426                 _E("ecore_file_init() failed");
427                 launch_crash_worker(CRASH_NOTI_PATH, CRASH_POPUP_OFF);
428         }
429         crash_file_monitor = ecore_file_monitor_add(CRASH_NOTI_PATH,(void *) __crash_file_cb, NULL);
430         if (!crash_file_monitor) {
431                 _E("ecore_file_monitor_add() failed");
432                 launch_crash_worker(CRASH_NOTI_PATH, CRASH_POPUP_OFF);
433                 return;
434         }
435 }
436
437 const struct device_ops bs_device_ops = {
438         .init = bs_init,
439 };