1860bab927a3f2f598801385f438448878fdf79b
[platform/core/system/crash-worker.git] / src / shared / util.c
1 /*
2  * crash-manager
3  * Copyright (c) 2012-2019 Samsung Electronics Co., Ltd.
4  *
5  * Licensed under the Apache License, Version 2.0 (the License);
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <assert.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdint.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <sys/time.h>
25 #include <sys/sendfile.h>
26 #include <dirent.h>
27 #include <fcntl.h>
28 #include <stdarg.h>
29 #include <unistd.h>
30 #include <limits.h>
31 #include <string.h>
32 #include <signal.h>
33 #include <time.h>
34 #include <errno.h>
35 #include <wait.h>
36 #include <ctype.h>
37 #include <grp.h>
38
39 #include "util.h"
40 #include "log.h"
41
42 int write_fd(int fd, const void *buf, int len)
43 {
44         int count;
45         int total;
46         total = 0;
47         while (len) {
48                 count = write(fd, buf, len);
49                 if (count < 0) {
50                         if (errno == EINTR)
51                                 continue;
52                         if (total)
53                                 return total;
54                         return count;
55                 }
56                 total += count;
57                 buf = ((const char *)buf) + count;
58                 len -= count;
59         }
60         return total;
61 }
62
63 static int copy_bytes_sendfile(int destfd, int srcfd, off_t *ncopied)
64 {
65         ssize_t nsent;
66         off_t total = 0;
67
68         do {
69                 nsent = sendfile(destfd, srcfd, NULL, INT32_MAX);
70                 if (nsent > 0)
71                         total += nsent;
72                 else if (nsent < 0 && errno == EAGAIN)
73                         continue;
74                 else
75                         break;
76         } while (1);
77
78         if (ncopied)
79                 *ncopied = total;
80
81         return nsent == -1 ? -1 : 0;
82 }
83
84 static int copy_bytes_rw(int destfd, int srcfd, off_t *ncopied)
85 {
86         ssize_t n;
87         char buf[4096];
88         off_t total = 0;
89
90         do {
91                 n = read(srcfd, buf, sizeof buf);
92                 if (n < 0) {
93                         if (errno == EAGAIN || errno == EINTR)
94                                 continue;
95                         _E("read: %m");
96                         return -1;
97                 }
98
99                 int m = write_fd(destfd, buf, n);
100                 if (m != n) {
101                         _E("failed to write data to destination fd: %m");
102                         return -1;
103                 }
104                 total += m;
105         } while (n > 0);
106
107         if (ncopied)
108                 *ncopied = total;
109
110         return 0;
111 }
112
113 int copy_bytes(int destfd, int srcfd, off_t *ncopied)
114 {
115         int r = copy_bytes_sendfile(destfd, srcfd, ncopied);
116         return r == 0 ? r : copy_bytes_rw(destfd, srcfd, ncopied);
117 }
118
119 int copy_file(char *dst, char *src)
120 {
121         int res;
122         int sfd;
123         int dfd;
124
125         if (!src || !dst) {
126                 _E("Invalid argument\n");
127                 return -1;
128         }
129         sfd = open(src, O_RDONLY);
130         if (sfd < 0) {
131                 _E("Failed to open (%s)\n", src);
132                 return -1;
133         }
134         dfd = open(dst, O_WRONLY|O_CREAT|O_EXCL, 0644);
135         if (dfd < 0) {
136                 close(sfd);
137                 _SE("Failed to open (%s)\n", dst);
138                 return -1;
139         }
140
141         res = copy_bytes(dfd, sfd, NULL);
142
143         close(sfd);
144         close(dfd);
145         return res;
146 }
147
148 int move_file(char *dst, char *src)
149 {
150         if (copy_file(dst, src) < 0)
151                 return -1;
152         if (unlink(src) < 0)
153                 return -1;
154         return 1;
155 }
156
157 int dump_file_write_fd(int dfd, char *src)
158 {
159         int res;
160         int sfd;
161
162         if (!src) {
163                 _E("Invalid argument\n");
164                 return -1;
165         }
166         sfd = open(src, O_RDONLY);
167         if (sfd < 0) {
168                 _SE("Failed to open (%s)\n", src);
169                 return -1;
170         }
171
172         res = copy_bytes(dfd, sfd, NULL);
173
174         close(sfd);
175         return res;
176 }
177
178 int fsync_path(char *const path)
179 {
180         int fd, ret;
181
182         ret = fd = open(path, O_RDONLY);
183         if (fd >= 0) {
184                 ret = fsync(fd);
185                 close(fd);
186         }
187
188         if (ret < 0)
189                 _E("Unable to fsync %s: %m", path);
190
191         return ret;
192 }
193
194 int make_dir(const char *path, const char *name, int mode)
195 {
196         int r = -1;
197
198         DIR *dir = opendir(path);
199         if (dir) {
200                 int dfd = dirfd(dir);
201                 r = mkdirat(dfd, name, mode);
202                 closedir(dir);
203         }
204
205         return r == 0 || (r == -1 && errno == EEXIST) ? 0 : -1;
206 }
207
208 static int remove_dir_internal(int fd)
209 {
210         DIR *dir;
211         struct dirent *de;
212         int subfd, ret = 0;
213
214         dir = fdopendir(fd);
215         if (!dir)
216                 return -1;
217
218         while ((de = readdir(dir))) {
219                 if (de->d_type == DT_DIR) {
220                         if (!strncmp(de->d_name, ".", 2) || !strncmp(de->d_name, "..", 3))
221                                 continue;
222                         subfd = openat(fd, de->d_name, O_RDONLY | O_DIRECTORY);
223                         if (subfd < 0) {
224                                 _SE("Couldn't openat %s: %d\n", de->d_name, errno);
225                                 ret = -1;
226                                 continue;
227                         }
228                         if (remove_dir_internal(subfd))
229                                 ret = -1;
230                         close(subfd);
231                         if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
232                                 _SE("Couldn't unlinkat %s: %d\n", de->d_name, errno);
233                                 ret = -1;
234                         }
235                 } else {
236                         if (unlinkat(fd, de->d_name, 0) < 0) {
237                                 _SE("Couldn't unlinkat %s: %d\n", de->d_name, errno);
238                                 ret = -1;
239                         }
240                 }
241         }
242         closedir(dir);
243         return ret;
244 }
245
246 int remove_dir(const char *path, int del_dir)
247 {
248         int fd, ret = 0;
249
250         if (!path)
251                 return -1;
252         fd = open(path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
253         if (fd < 0) {
254                 _SE("Couldn't opendir %s: %d\n", path, errno);
255                 return -errno;
256         }
257         ret = remove_dir_internal(fd);
258         close(fd);
259
260         if (del_dir) {
261                 if (rmdir(path)) {
262                         _SE("Couldn't rmdir %s: %d\n", path, errno);
263                         ret = -1;
264                 }
265         }
266         return ret;
267 }
268
269 int get_exec_pid(const char *execpath)
270 {
271         DIR *dp;
272         struct dirent *dentry;
273         int pid = -1, fd;
274         int ret;
275         char buf[PATH_MAX];
276         char buf2[PATH_MAX];
277         int len;
278
279         dp = opendir("/proc");
280         if (!dp) {
281                 _E("FAIL: open /proc");
282                 return -1;
283         }
284
285         len = strlen(execpath) + 1;
286
287         while ((dentry = readdir(dp))) {
288                 if (!isdigit(dentry->d_name[0]))
289                         continue;
290
291                 pid = atoi(dentry->d_name);
292
293                 snprintf(buf, PATH_MAX, "/proc/%d/cmdline", pid);
294                 fd = open(buf, O_RDONLY);
295                 if (fd < 0)
296                         continue;
297                 ret = read(fd, buf2, PATH_MAX);
298                 close(fd);
299
300                 if (ret < 0 || ret >= PATH_MAX)
301                         continue;
302
303                 buf2[ret] = '\0';
304
305                 if (!strncmp(buf2, execpath, len)) {
306                         closedir(dp);
307                         return pid;
308                 }
309         }
310
311         errno = ESRCH;
312         closedir(dp);
313         return -1;
314 }
315
316 int get_file_count(char *path)
317 {
318         DIR *dir;
319         struct dirent *dp;
320         int count = 0;
321
322         dir = opendir(path);
323         if (!dir)
324                 return 0;
325
326         while ((dp = readdir(dir))) {
327                 const char *name = dp->d_name;
328                 /* always skip "." and ".." */
329                 if (name[0] == '.') {
330                         if (name[1] == 0)
331                                 continue;
332                         if ((name[1] == '.') && (name[2] == 0))
333                                 continue;
334                 }
335                 count++;
336         }
337         closedir(dir);
338         return count;
339 }
340
341 off_t get_directory_usage(char *path)
342 {
343         DIR *dir;
344         struct dirent *de;
345         struct stat st;
346         off_t usage = 0;
347         int fd = -1;
348
349         fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
350         if (fd < 0)
351                 return -1;
352         dir = fdopendir(fd);
353         if (!dir) {
354                 close(fd);
355                 return -1;
356         }
357
358         while ((de = readdir(dir))) {
359                 if (!strncmp(de->d_name, ".", 2) || !strncmp(de->d_name, "..", 3))
360                         continue;
361                 if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
362                         _SE("Failed to fstatat  %s: %d\n", de->d_name, errno);
363                         continue;
364                 }
365                 usage += st.st_size;
366         }
367         closedir(dir);
368         close(fd);
369         return usage;
370 }
371
372 int log_kmsg(char *fmt, ...)
373 {
374         int result = 1;
375         va_list ap;
376         FILE *file;
377
378         file = fopen("/dev/kmsg", "w");
379         if (file == NULL) {
380                 _E("Open /dev/kmsg error: %m");
381                 return 0;
382         }
383
384         va_start(ap, fmt);
385         if (vfprintf(file, fmt, ap) < 0) {
386                 _E("Write to /dev/kmsg error: %m");
387                 result = 0;
388         }
389         va_end(ap);
390         fclose(file);
391         return result;
392 }
393
394 /**
395  * @brief Check wchan of thread
396  *
397  * @param pid PID of the inspected process
398  * @param tid TID of the thread to check
399  */
400 static int check_thread_wchan(int pid, int tid)
401 {
402         int fd, cnt;
403         char path[PATH_MAX], buf[100];
404
405         snprintf(path, sizeof(path), "/proc/%d/task/%d/wchan", pid, tid);
406         fd = open(path, O_RDONLY);
407         if (fd == -1) {
408                 _E("cannot open %s: %m\n", path);
409                 return -errno;
410         }
411         cnt = read(fd, buf, sizeof(buf));
412         if (cnt == -1 || cnt == sizeof(buf)) {
413                 _E("read %s error: %m\n", path);
414                 close(fd);
415                 return -errno;
416         }
417         buf[cnt] = 0;
418         close(fd);
419
420         if (strncmp("do_coredump", buf, sizeof(buf)) == 0 || strncmp("pipe_wait", buf, sizeof(buf)) == 0)
421                 return tid;
422         else
423                 return 0;
424 }
425
426 /**
427  * @brief Find crashed tid if tid was not offered
428  *
429  * @param pid PID of the inspected process
430  */
431 int find_crash_tid(int pid)
432 {
433         int threadnum = 1;
434         int crash_tid = -1;
435         DIR *dir;
436         struct dirent *entry;
437         char task_path[PATH_MAX];
438         struct stat sb;
439
440         snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
441         if (stat(task_path, &sb) == -1) {
442                 _E("no such file: %s", task_path);
443                 return -1;
444         }
445
446         threadnum = sb.st_nlink - 2;
447
448         if (threadnum > 1) {
449                 dir = opendir(task_path);
450                 if (!dir) {
451                         _E("cannot open %s\n", task_path);
452                         return -1;
453                 } else {
454                         while ((entry = readdir(dir)) != NULL) {
455                                 if (strcmp(entry->d_name, ".") == 0 ||
456                                     strcmp(entry->d_name, "..") == 0)
457                                         continue;
458                                 crash_tid = check_thread_wchan(pid,
459                                                                atoi(entry->d_name));
460                                 if (crash_tid > 0)
461                                         break;
462                         }
463                         closedir(dir);
464                         return crash_tid;
465                 }
466         } else if (threadnum == 1) {
467                 return pid;
468         }
469         return -1;
470 }
471
472 bool filter_drop_trailing_whitespace(char *buf, int size, int datalen)
473 {
474         assert(buf);
475         assert(datalen <= size);
476
477         bool filtered = false;
478
479         for (int i = datalen; i >= 0 && isspace(buf[i]); --i) {
480                 buf[i] = '\0';
481                 filtered = true;
482         }
483
484         return filtered;
485 }
486
487 bool read_proc_file(pid_t pid, const char * const file, char *outbuf, size_t bufsize, charp0filter filter)
488 {
489         assert(file);
490         assert(outbuf);
491         assert(bufsize > 0);
492
493         char path[PATH_MAX];
494
495         snprintf(path, sizeof(path), "/proc/%d/%s", pid, file);
496
497         int fd = open(path, O_RDONLY);
498         if (fd < 0) {
499                 _E("Failed to open %s: %m\n", path);
500                 return false;
501         }
502
503         ssize_t ret = read(fd, outbuf, bufsize - 1);
504         if (ret < 0) {
505                 _E("Failed to read %s: %m\n", path);
506                 goto err_close;
507         }
508         outbuf[ret] = '\0';
509
510         close(fd);
511
512         if (ret > 0 && filter)
513                 (void)filter(outbuf, bufsize, ret - 1);
514
515         return true;
516
517 err_close:
518         close(fd);
519         return false;
520 }
521
522 bool get_exe_path(pid_t pid, char *outbuf, size_t outbuf_len)
523 {
524         assert(outbuf);
525         assert(outbuf_len > 0);
526
527         char exe_link[PATH_MAX];
528
529         snprintf(exe_link, sizeof(exe_link), "/proc/%d/exe", pid);
530
531         ssize_t ret = readlink(exe_link, outbuf, outbuf_len - 1);
532         if (ret <= 0) {
533                 _E("Failed to read link %s: %m", exe_link);
534                 return false;
535         }
536         outbuf[ret] = '\0';
537
538         if (access(outbuf, F_OK) == -1) {
539                 _E("Unable to access %s: %m", outbuf);
540                 return false;
541         }
542         return true;
543 }
544
545 /* This function is supposed to accept same data as passed to execve
546  * (argv and envp), which can be arrays of strings as well as NULL
547  * pointer.
548  */
549 char* concatenate(char *const vec[])
550 {
551         size_t length = 0;
552         for (char *const *p = vec; p && *p; p++)
553                 length += strlen(*p) + 1;
554
555         if (length == 0)
556                 return strdup("");
557
558         char *str = (char *)malloc(length);
559         if (!str)
560                 return NULL;
561
562         char *destp = str;
563         char *const *vecp = vec;
564         while (*vecp) {
565                 destp = stpcpy(destp, *(vecp++));
566                 if (*vecp)
567                         destp = stpcpy(destp, " ");
568         }
569
570         return str;
571 }
572
573 bool string_ends_with(const char *string, const char *suffix)
574 {
575         const size_t string_len = strlen(string);
576         const size_t suffix_len = strlen(suffix);
577
578         return (string_len >= suffix_len) && !strcmp(string + string_len - suffix_len, suffix);
579 }
580
581 bool file_exists(const char *path)
582 {
583         struct stat buf;
584         return stat(path, &buf) == 0;
585 }
586
587 /**
588  * @}
589  */