Use bool instead of int in remove.*() functions
[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                 _E("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                 _E("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 bool remove_dir_internal(int fd)
209 {
210         DIR *dir;
211         struct dirent *de;
212         bool ret = true;
213         int subfd = 0;
214
215         dir = fdopendir(fd);
216         if (!dir)
217                 return false;
218
219         while ((de = readdir(dir))) {
220                 if (de->d_type == DT_DIR) {
221                         if (!strncmp(de->d_name, ".", 2) || !strncmp(de->d_name, "..", 3))
222                                 continue;
223                         subfd = openat(fd, de->d_name, O_RDONLY | O_DIRECTORY);
224                         if (subfd < 0) {
225                                 _E("Couldn't openat %s: %d\n", de->d_name, errno);
226                                 ret = false;
227                                 continue;
228                         }
229                         if (!remove_dir_internal(subfd))
230                                 ret = false;
231                         close(subfd);
232                         if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
233                                 _E("Couldn't unlinkat %s: %d\n", de->d_name, errno);
234                                 ret = false;
235                         }
236                 } else {
237                         if (unlinkat(fd, de->d_name, 0) < 0) {
238                                 _E("Couldn't unlinkat %s: %d\n", de->d_name, errno);
239                                 ret = false;
240                         }
241                 }
242         }
243         closedir(dir);
244         return ret;
245 }
246
247 bool remove_dir(const char *path, bool del_dir)
248 {
249         bool ret = true;
250         int fd = 0;
251
252         if (!path)
253                 return false;
254         fd = open(path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
255         if (fd < 0) {
256                 _E("Couldn't opendir %s: %d\n", path, errno);
257                 return false;
258         }
259         ret = remove_dir_internal(fd);
260         close(fd);
261
262         if (del_dir) {
263                 if (rmdir(path)) {
264                         _E("Couldn't rmdir %s: %d\n", path, errno);
265                         ret = false;
266                 }
267         }
268         return ret;
269 }
270
271 int get_exec_pid(const char *execpath)
272 {
273         DIR *dp;
274         struct dirent *dentry;
275         int pid = -1, fd;
276         int ret;
277         char buf[PATH_MAX];
278         char buf2[PATH_MAX];
279         int len;
280
281         dp = opendir("/proc");
282         if (!dp) {
283                 _E("FAIL: open /proc");
284                 return -1;
285         }
286
287         len = strlen(execpath) + 1;
288
289         while ((dentry = readdir(dp))) {
290                 if (!isdigit(dentry->d_name[0]))
291                         continue;
292
293                 pid = atoi(dentry->d_name);
294
295                 snprintf(buf, PATH_MAX, "/proc/%d/cmdline", pid);
296                 fd = open(buf, O_RDONLY);
297                 if (fd < 0)
298                         continue;
299                 ret = read(fd, buf2, PATH_MAX);
300                 close(fd);
301
302                 if (ret < 0 || ret >= PATH_MAX)
303                         continue;
304
305                 buf2[ret] = '\0';
306
307                 if (!strncmp(buf2, execpath, len)) {
308                         closedir(dp);
309                         return pid;
310                 }
311         }
312
313         errno = ESRCH;
314         closedir(dp);
315         return -1;
316 }
317
318 int get_file_count(char *path)
319 {
320         DIR *dir;
321         struct dirent *dp;
322         int count = 0;
323
324         dir = opendir(path);
325         if (!dir)
326                 return 0;
327
328         while ((dp = readdir(dir))) {
329                 const char *name = dp->d_name;
330                 /* always skip "." and ".." */
331                 if (name[0] == '.') {
332                         if (name[1] == 0)
333                                 continue;
334                         if ((name[1] == '.') && (name[2] == 0))
335                                 continue;
336                 }
337                 count++;
338         }
339         closedir(dir);
340         return count;
341 }
342
343 off_t get_directory_usage(char *path)
344 {
345         DIR *dir;
346         struct dirent *de;
347         struct stat st;
348         off_t usage = 0;
349         int fd = -1;
350
351         fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
352         if (fd < 0)
353                 return -1;
354         dir = fdopendir(fd);
355         if (!dir) {
356                 close(fd);
357                 return -1;
358         }
359
360         while ((de = readdir(dir))) {
361                 if (!strncmp(de->d_name, ".", 2) || !strncmp(de->d_name, "..", 3))
362                         continue;
363                 if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
364                         _E("Failed to fstatat  %s: %d\n", de->d_name, errno);
365                         continue;
366                 }
367                 usage += st.st_size;
368         }
369         closedir(dir);
370         close(fd);
371         return usage;
372 }
373
374 int log_kmsg(char *fmt, ...)
375 {
376         int result = 1;
377         va_list ap;
378         FILE *file;
379
380         file = fopen("/dev/kmsg", "w");
381         if (file == NULL) {
382                 _E("Open /dev/kmsg error: %m");
383                 return 0;
384         }
385
386         va_start(ap, fmt);
387         if (vfprintf(file, fmt, ap) < 0) {
388                 _E("Write to /dev/kmsg error: %m");
389                 result = 0;
390         }
391         va_end(ap);
392         fclose(file);
393         return result;
394 }
395
396 /**
397  * @brief Check wchan of thread
398  *
399  * @param pid PID of the inspected process
400  * @param tid TID of the thread to check
401  */
402 static int check_thread_wchan(int pid, int tid)
403 {
404         int fd, cnt;
405         char path[PATH_MAX], buf[100];
406
407         snprintf(path, sizeof(path), "/proc/%d/task/%d/wchan", pid, tid);
408         fd = open(path, O_RDONLY);
409         if (fd == -1) {
410                 _E("cannot open %s: %m\n", path);
411                 return -errno;
412         }
413         cnt = read(fd, buf, sizeof(buf));
414         if (cnt == -1 || cnt == sizeof(buf)) {
415                 _E("read %s error: %m\n", path);
416                 close(fd);
417                 return -errno;
418         }
419         buf[cnt] = 0;
420         close(fd);
421
422         if (strncmp("do_coredump", buf, sizeof(buf)) == 0 || strncmp("pipe_wait", buf, sizeof(buf)) == 0)
423                 return tid;
424         else
425                 return 0;
426 }
427
428 /**
429  * @brief Find crashed tid if tid was not offered
430  *
431  * @param pid PID of the inspected process
432  */
433 int find_crash_tid(int pid)
434 {
435         int threadnum = 1;
436         int crash_tid = -1;
437         DIR *dir;
438         struct dirent *entry;
439         char task_path[PATH_MAX];
440         struct stat sb;
441
442         snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
443         if (stat(task_path, &sb) == -1) {
444                 _E("no such file: %s", task_path);
445                 return -1;
446         }
447
448         threadnum = sb.st_nlink - 2;
449
450         if (threadnum > 1) {
451                 dir = opendir(task_path);
452                 if (!dir) {
453                         _E("cannot open %s\n", task_path);
454                         return -1;
455                 } else {
456                         while ((entry = readdir(dir)) != NULL) {
457                                 if (strcmp(entry->d_name, ".") == 0 ||
458                                     strcmp(entry->d_name, "..") == 0)
459                                         continue;
460                                 crash_tid = check_thread_wchan(pid,
461                                                                atoi(entry->d_name));
462                                 if (crash_tid > 0)
463                                         break;
464                         }
465                         closedir(dir);
466                         return crash_tid;
467                 }
468         } else if (threadnum == 1) {
469                 return pid;
470         }
471         return -1;
472 }
473
474 bool filter_drop_trailing_whitespace(char *buf, int size, int datalen)
475 {
476         assert(buf);
477         assert(datalen <= size);
478
479         bool filtered = false;
480
481         for (int i = datalen; i >= 0 && isspace(buf[i]); --i) {
482                 buf[i] = '\0';
483                 filtered = true;
484         }
485
486         return filtered;
487 }
488
489 bool read_proc_file(pid_t pid, const char * const file, char *outbuf, size_t bufsize, charp0filter filter)
490 {
491         assert(file);
492         assert(outbuf);
493         assert(bufsize > 0);
494
495         char path[PATH_MAX];
496
497         snprintf(path, sizeof(path), "/proc/%d/%s", pid, file);
498
499         int fd = open(path, O_RDONLY);
500         if (fd < 0) {
501                 _E("Failed to open %s: %m\n", path);
502                 return false;
503         }
504
505         ssize_t ret = read(fd, outbuf, bufsize - 1);
506         if (ret < 0) {
507                 _E("Failed to read %s: %m\n", path);
508                 goto err_close;
509         }
510         outbuf[ret] = '\0';
511
512         close(fd);
513
514         if (ret > 0 && filter)
515                 (void)filter(outbuf, bufsize, ret - 1);
516
517         return true;
518
519 err_close:
520         close(fd);
521         return false;
522 }
523
524 bool get_exe_path(pid_t pid, char *outbuf, size_t outbuf_len)
525 {
526         assert(outbuf);
527         assert(outbuf_len > 0);
528
529         char exe_link[PATH_MAX];
530
531         snprintf(exe_link, sizeof(exe_link), "/proc/%d/exe", pid);
532
533         ssize_t ret = readlink(exe_link, outbuf, outbuf_len - 1);
534         if (ret <= 0) {
535                 _E("Failed to read link %s: %m", exe_link);
536                 return false;
537         }
538         outbuf[ret] = '\0';
539
540         if (access(outbuf, F_OK) == -1) {
541                 _E("Unable to access %s: %m", outbuf);
542                 return false;
543         }
544         return true;
545 }
546
547 /* This function is supposed to accept same data as passed to execve
548  * (argv and envp), which can be arrays of strings as well as NULL
549  * pointer.
550  */
551 char *concatenate(char *const vec[])
552 {
553         size_t length = 0;
554         for (char *const *p = vec; p && *p; p++)
555                 length += strlen(*p) + 1;
556
557         if (length == 0)
558                 return strdup("");
559
560         char *str = (char *)malloc(length);
561         if (!str)
562                 return NULL;
563
564         char *destp = str;
565         char *const *vecp = vec;
566         while (*vecp) {
567                 destp = stpcpy(destp, *(vecp++));
568                 if (*vecp)
569                         destp = stpcpy(destp, " ");
570         }
571
572         return str;
573 }
574
575 bool string_ends_with(const char *string, const char *suffix)
576 {
577         const size_t string_len = strlen(string);
578         const size_t suffix_len = strlen(suffix);
579
580         return (string_len >= suffix_len) && !strcmp(string + string_len - suffix_len, suffix);
581 }
582
583 /* what for: kernel.core_pattern's %E replaces / with !, this function does opposite process */
584 void kernel_exe_path_normalize(char *path)
585 {
586         assert(path);
587
588         for (size_t i = 0, len = strlen(path); i < len; i++)
589                 path[i] = path[i] == '!' ? '/' : path[i];
590 }
591
592 bool file_exists(const char *path)
593 {
594         struct stat buf;
595         return stat(path, &buf) == 0;
596 }
597
598 bool file_exists_in_dir(const char *base_dir, const char *file_name)
599 {
600         char *path;
601         bool result = false;
602
603         if (asprintf(&path, "%s/%s", base_dir, file_name) == -1) {
604                 _E("Failed to asprintf for path: %m");
605         } else {
606                 result = file_exists(path);
607                 free(path);
608         }
609
610         return result;
611 }
612
613 bool write_to_file(const char *content, const char *base_dir, const char *file_name)
614 {
615         char *path;
616         bool result = false;
617
618         if (asprintf(&path, "%s/%s", base_dir, file_name) == -1) {
619                 _E("Failed to asprintf for path: %m");
620                 return false;
621         }
622
623         int fd = open(path, O_WRONLY | O_CREAT, 0600);
624
625         if (fd < 0) {
626                 _E("Failed to open %s: %m", path);
627                 goto exit;
628         }
629
630         if (dprintf(fd, "%s", content) < 0) {
631                 _E("Failed to write to file %s: %m", path);
632                 close(fd);
633                 goto exit;
634         }
635
636         close(fd);
637 exit:
638         free(path);
639         return result;
640 }
641
642 bool is_dotnet_file(const char *path)
643 {
644         static const char *DOTNET_EXTS[] = { ".dll", ".exe", NULL };
645
646         bool result = false;
647         size_t p_len = strlen(path);
648         while (p_len > 0 && path[p_len-1] == '\n')
649                 p_len--;
650
651         for (const char **ext = DOTNET_EXTS; *ext != NULL; ext++) {
652                 size_t d_len = strlen(*ext);
653
654                 if ((p_len >= d_len) &&
655                     (strncasecmp(&path[p_len - d_len], *ext, d_len) == 0)) {
656                         result = true;
657                         break;
658                 }
659         }
660         return result;
661 }
662
663 /**
664  * @}
665  */