3 * Copyright (c) 2012-2019 Samsung Electronics Co., Ltd.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include <sys/types.h>
25 #include <sys/sendfile.h>
42 int write_fd(int fd, const void *buf, int len)
48 count = write(fd, buf, len);
57 buf = ((const char *)buf) + count;
63 static int copy_bytes_sendfile(int destfd, int srcfd, off_t *ncopied)
69 nsent = sendfile(destfd, srcfd, NULL, INT32_MAX);
72 else if (nsent < 0 && errno == EAGAIN)
81 return nsent == -1 ? -1 : 0;
84 static int copy_bytes_rw(int destfd, int srcfd, off_t *ncopied)
91 n = read(srcfd, buf, sizeof buf);
93 if (errno == EAGAIN || errno == EINTR)
99 int m = write_fd(destfd, buf, n);
101 _E("failed to write data to destination fd: %m");
113 int copy_bytes(int destfd, int srcfd, off_t *ncopied)
115 int r = copy_bytes_sendfile(destfd, srcfd, ncopied);
116 return r == 0 ? r : copy_bytes_rw(destfd, srcfd, ncopied);
119 int copy_file(char *dst, char *src)
126 _E("Invalid argument\n");
129 sfd = open(src, O_RDONLY);
131 _E("Failed to open (%s)\n", src);
134 dfd = open(dst, O_WRONLY|O_CREAT|O_EXCL, 0644);
137 _E("Failed to open (%s)\n", dst);
141 res = copy_bytes(dfd, sfd, NULL);
148 int move_file(char *dst, char *src)
150 if (copy_file(dst, src) < 0)
157 int dump_file_write_fd(int dfd, char *src)
163 _E("Invalid argument\n");
166 sfd = open(src, O_RDONLY);
168 _E("Failed to open (%s)\n", src);
172 res = copy_bytes(dfd, sfd, NULL);
178 int fsync_path(char *const path)
182 ret = fd = open(path, O_RDONLY);
189 _E("Unable to fsync %s: %m", path);
194 int make_dir(const char *path, const char *name, int mode)
198 DIR *dir = opendir(path);
200 int dfd = dirfd(dir);
201 r = mkdirat(dfd, name, mode);
205 return r == 0 || (r == -1 && errno == EEXIST) ? 0 : -1;
208 static bool remove_dir_internal(int fd)
219 while ((de = readdir(dir))) {
220 if (de->d_type == DT_DIR) {
221 if (!strncmp(de->d_name, ".", 2) || !strncmp(de->d_name, "..", 3))
223 subfd = openat(fd, de->d_name, O_RDONLY | O_DIRECTORY);
225 _E("Couldn't openat %s: %d\n", de->d_name, errno);
229 if (!remove_dir_internal(subfd))
232 if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
233 _E("Couldn't unlinkat %s: %d\n", de->d_name, errno);
237 if (unlinkat(fd, de->d_name, 0) < 0) {
238 _E("Couldn't unlinkat %s: %d\n", de->d_name, errno);
247 bool remove_dir(const char *path, bool del_dir)
254 fd = open(path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
256 _E("Couldn't opendir %s: %d\n", path, errno);
259 ret = remove_dir_internal(fd);
264 _E("Couldn't rmdir %s: %d\n", path, errno);
271 int get_exec_pid(const char *execpath)
274 struct dirent *dentry;
281 dp = opendir("/proc");
283 _E("FAIL: open /proc");
287 len = strlen(execpath) + 1;
289 while ((dentry = readdir(dp))) {
290 if (!isdigit(dentry->d_name[0]))
293 pid = atoi(dentry->d_name);
295 snprintf(buf, PATH_MAX, "/proc/%d/cmdline", pid);
296 fd = open(buf, O_RDONLY);
299 ret = read(fd, buf2, PATH_MAX);
302 if (ret < 0 || ret >= PATH_MAX)
307 if (!strncmp(buf2, execpath, len)) {
318 int get_file_count(char *path)
328 while ((dp = readdir(dir))) {
329 const char *name = dp->d_name;
330 /* always skip "." and ".." */
331 if (name[0] == '.') {
334 if ((name[1] == '.') && (name[2] == 0))
343 off_t get_directory_usage(char *path)
351 fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
360 while ((de = readdir(dir))) {
361 if (!strncmp(de->d_name, ".", 2) || !strncmp(de->d_name, "..", 3))
363 if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
364 _E("Failed to fstatat %s: %d\n", de->d_name, errno);
374 int log_kmsg(char *fmt, ...)
380 file = fopen("/dev/kmsg", "w");
382 _E("Open /dev/kmsg error: %m");
387 if (vfprintf(file, fmt, ap) < 0) {
388 _E("Write to /dev/kmsg error: %m");
397 * @brief Check wchan of thread
399 * @param pid PID of the inspected process
400 * @param tid TID of the thread to check
402 static int check_thread_wchan(int pid, int tid)
405 char path[PATH_MAX], buf[100];
407 snprintf(path, sizeof(path), "/proc/%d/task/%d/wchan", pid, tid);
408 fd = open(path, O_RDONLY);
410 _E("cannot open %s: %m\n", path);
413 cnt = read(fd, buf, sizeof(buf));
414 if (cnt == -1 || cnt == sizeof(buf)) {
415 _E("read %s error: %m\n", path);
422 if (strncmp("do_coredump", buf, sizeof(buf)) == 0 || strncmp("pipe_wait", buf, sizeof(buf)) == 0)
429 * @brief Find crashed tid if tid was not offered
431 * @param pid PID of the inspected process
433 int find_crash_tid(int pid)
438 struct dirent *entry;
439 char task_path[PATH_MAX];
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);
448 threadnum = sb.st_nlink - 2;
451 dir = opendir(task_path);
453 _E("cannot open %s\n", task_path);
456 while ((entry = readdir(dir)) != NULL) {
457 if (strcmp(entry->d_name, ".") == 0 ||
458 strcmp(entry->d_name, "..") == 0)
460 crash_tid = check_thread_wchan(pid,
461 atoi(entry->d_name));
468 } else if (threadnum == 1) {
474 bool filter_drop_trailing_whitespace(char *buf, int size, int datalen)
477 assert(datalen <= size);
479 bool filtered = false;
481 for (int i = datalen; i >= 0 && isspace(buf[i]); --i) {
489 bool read_proc_file(pid_t pid, const char * const file, char *outbuf, size_t bufsize, charp0filter filter)
497 snprintf(path, sizeof(path), "/proc/%d/%s", pid, file);
499 int fd = open(path, O_RDONLY);
501 _E("Failed to open %s: %m\n", path);
505 ssize_t ret = read(fd, outbuf, bufsize - 1);
507 _E("Failed to read %s: %m\n", path);
514 if (ret > 0 && filter)
515 (void)filter(outbuf, bufsize, ret - 1);
524 bool get_exe_path(pid_t pid, char *outbuf, size_t outbuf_len)
527 assert(outbuf_len > 0);
529 char exe_link[PATH_MAX];
531 snprintf(exe_link, sizeof(exe_link), "/proc/%d/exe", pid);
533 ssize_t ret = readlink(exe_link, outbuf, outbuf_len - 1);
535 _E("Failed to read link %s: %m", exe_link);
540 if (access(outbuf, F_OK) == -1) {
541 _E("Unable to access %s: %m", outbuf);
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
551 char *concatenate(char *const vec[])
554 for (char *const *p = vec; p && *p; p++)
555 length += strlen(*p) + 1;
560 char *str = (char *)malloc(length);
565 char *const *vecp = vec;
567 destp = stpcpy(destp, *(vecp++));
569 destp = stpcpy(destp, " ");
575 bool string_ends_with(const char *string, const char *suffix)
577 const size_t string_len = strlen(string);
578 const size_t suffix_len = strlen(suffix);
580 return (string_len >= suffix_len) && !strcmp(string + string_len - suffix_len, suffix);
583 /* what for: kernel.core_pattern's %E replaces / with !, this function does opposite process */
584 void kernel_exe_path_normalize(char *path)
588 for (size_t i = 0, len = strlen(path); i < len; i++)
589 path[i] = path[i] == '!' ? '/' : path[i];
592 bool file_exists(const char *path)
595 return stat(path, &buf) == 0;
598 bool file_exists_in_dir(const char *base_dir, const char *file_name)
603 if (asprintf(&path, "%s/%s", base_dir, file_name) == -1) {
604 _E("Failed to asprintf for path: %m");
606 result = file_exists(path);
613 bool write_to_file(const char *content, const char *base_dir, const char *file_name)
618 if (asprintf(&path, "%s/%s", base_dir, file_name) == -1) {
619 _E("Failed to asprintf for path: %m");
623 int fd = open(path, O_WRONLY | O_CREAT, 0600);
626 _E("Failed to open %s: %m", path);
630 if (dprintf(fd, "%s", content) < 0) {
631 _E("Failed to write to file %s: %m", path);
642 bool is_dotnet_file(const char *path)
644 static const char *DOTNET_EXTS[] = { ".dll", ".exe", NULL };
647 size_t p_len = strlen(path);
648 while (p_len > 0 && path[p_len-1] == '\n')
651 for (const char **ext = DOTNET_EXTS; *ext != NULL; ext++) {
652 size_t d_len = strlen(*ext);
654 if ((p_len >= d_len) &&
655 (strncasecmp(&path[p_len - d_len], *ext, d_len) == 0)) {