3 * Copyright (c) 2012-2013 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 #define READ_BUFF_SIZE 4096
43 #define SELECT_TIMEOUT_US 100000
45 int system_command_parallel(char *command)
48 const char *environ[] = { NULL };
59 argv[2] = (char *)command;
61 execve("/bin/sh", argv, (char **)environ);
68 int wait_system_command(int pid)
76 if (waitpid(pid, &status, 0) == -1) {
80 if (WIFEXITED(status))
81 return WEXITSTATUS(status);
82 else if (WIFSIGNALED(status))
83 return WTERMSIG(status);
84 else if (WIFSTOPPED(status))
85 return WSTOPSIG(status);
87 } while (!WIFEXITED(status) && !WIFSIGNALED(status));
92 int system_command(char *command)
96 pid = system_command_parallel(command);
98 return wait_system_command(pid);
101 int system_command_with_timeout(int timeout_seconds, char *command)
103 const char *environ[] = { NULL };
107 clock_t start = clock();
109 /* handle error case */
111 _E("fork: %d\n", errno);
114 /* handle child case */
119 argv[2] = (char *)command;
122 execve("/bin/sh", argv, (char **)environ);
123 _SI("exec(%s): %d\n", command, errno);
126 /* handle parent case */
129 pid_t p = waitpid(pid, &status, WNOHANG);
130 float elapsed = (float) (clock() - start) / CLOCKS_PER_SEC;
132 if (WIFSIGNALED(status))
133 _SI("%s: Killed by signal %d\n", command, WTERMSIG(status));
134 else if (WIFEXITED(status) && WEXITSTATUS(status) > 0)
135 _SI("%s: Exit code %d\n", command, WEXITSTATUS(status));
136 return WEXITSTATUS(status);
138 if (timeout_seconds && elapsed > timeout_seconds) {
139 _SI("%s: Timed out after %.1fs (killing pid %d)\n",
140 command, elapsed, pid);
144 /* poll every 0.1 sec */
149 int file_exist(const char *file)
153 fp = fopen(file, "r");
160 int write_fd(int fd, const void *buf, int len)
166 count = write(fd, buf, len);
175 buf = ((const char *)buf) + count;
181 static int copy_bytes(int dfd, int sfd)
186 bytes_sent = sendfile(dfd, sfd, NULL, PIPE_BUF);
187 while (bytes_sent > 0);
189 if (bytes_sent == -1) {
190 _E("sendfile() error: %m\n");
196 int copy_file(char *src, char *dst)
203 _E("Invalid argument\n");
206 sfd = open(src, O_RDONLY);
208 _E("Failed to open (%s)\n", src);
211 dfd = open(dst, O_WRONLY|O_CREAT|O_EXCL, 0644);
214 _SE("Failed to open (%s)\n", dst);
218 res = copy_bytes(dfd, sfd);
225 int move_file(char *src, char *dst)
227 if (copy_file(src, dst) < 0)
234 int dump_file_write_fd(char *src, int dfd)
240 _E("Invalid argument\n");
243 sfd = open(src, O_RDONLY);
245 _SE("Failed to open (%s)\n", src);
249 res = copy_bytes(dfd, sfd);
255 static int run_command(char *path, char *args[], char *env[], int fd[])
257 if (dup2(fd[1], STDOUT_FILENO) == -1) {
258 _E("dup2 error: %m");
262 if (close(fd[1]) == -1) {
263 _E("close fd error: %m");
267 if (close(fd[0]) == -1) {
268 _E("close fd error: %m");
272 if (execvpe(path, args, env) == -1) {
273 _E("run command %s error: %m", path);
279 static int wait_for_child(pid_t pid, int *exit_code, int timeout)
281 for (int i = 0; i < 10*timeout; i++) {
283 pid_t p = waitpid(pid, &status, WNOHANG);
285 if (WIFSIGNALED(status)) {
286 _I("Killed by signal %d\n", WTERMSIG(status));
288 } else if (WIFEXITED(status)) {
289 *exit_code = WEXITSTATUS(status);
292 } else if (p == -1) {
293 _E("waitpid error: %m");
301 static int read_into_buff(int fd, char *buff, int size, int timeout_us, int *eof)
310 tout.tv_sec = timeout_us / 1000000;
311 tout.tv_usec = timeout_us % 1000000;
315 if ((sel_ret = select(fd+1, &set, NULL, NULL, &tout)) >= 0) {
317 // we can do nonblocking read
318 int readed = read(fd, &buff[buff_pos], size);
323 } else if (readed == 0) {
324 // no more data to read
328 _E("read data from the pipe error: %m");
333 _E("select() error: %m");
338 // if buff is not NULL then 'size' bytes of the result is written the buffer,
339 // otherwise result is written to the dfd descriptor
340 int run_command_write_fd_timeout(char *path, char *args[], char *env[], int dfd, char *buff, int size, int timeout)
342 char BUFF[READ_BUFF_SIZE];
344 struct timeval start, end;
345 int write_to_fd = buff == NULL ? 1 : 0;
347 if (!write_to_fd && size <= 0) {
348 _E("buffer size must be greather than zero");
353 _E("pipe create error: %m");
360 return run_command(path, args, env, fd);
361 } else if (pid > 0) {
362 if (close(fd[1]) == -1) {
363 _E("close fd error: %m");
367 if (gettimeofday(&start, NULL) == -1) {
368 _E("gettimeofday error: %m");
381 act_size = READ_BUFF_SIZE;
388 while ((readed = read_into_buff(fd[0], act_buff, act_size, 1000000, &eof)) >= 0) {
391 if (count < (INT_MAX - readed))
397 if (write_fd(dfd, act_buff, readed) == -1) {
398 _E("write data to pipe error: %m");
406 // buff is full, we can return
415 if (gettimeofday(&end, NULL) == -1) {
416 _E("gettimeofday error: %m");
420 if ((end.tv_sec - start.tv_sec) > timeout) {
430 _E("command timeout: %s", path);
431 if (kill(pid, 0) == 0) {
432 // we can kill a child because we don't
434 if (kill(pid, SIGTERM) == -1)
435 _E("kill child %d error: %m", pid);
439 if (close(fd[0]) == -1) {
440 _E("close fd error: %m");
444 // let's wait a second for a child
446 int wait_res = wait_for_child(pid, &exit_code, 1);
449 _I("wait_for_child for \%s\" returns non-zero value\n", path);
450 else if (exit_code != 0)
451 _I("\"%s\" exit code: %d\n", path, exit_code);
453 return (eof == 1 && exit_code == 0) ? count : -abs(exit_code);
455 _E("fork() error: %m");
462 int run_command_timeout(char *path, char *args[], char *env[], int timeout)
464 int fd = open("/dev/null", O_WRONLY);
466 _E("open /dev/null error: %m");
470 int res = run_command_write_fd_timeout(path, args, env, fd, NULL, 0, timeout);
477 static int remove_dir_internal(int fd)
487 while ((de = readdir(dir))) {
488 if (de->d_type == DT_DIR) {
489 if (!strncmp(de->d_name, ".", 2) || !strncmp(de->d_name, "..", 3))
491 subfd = openat(fd, de->d_name, O_RDONLY | O_DIRECTORY);
493 _SE("Couldn't openat %s: %d\n", de->d_name, errno);
497 if (remove_dir_internal(subfd))
500 if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
501 _SE("Couldn't unlinkat %s: %d\n", de->d_name, errno);
505 if (unlinkat(fd, de->d_name, 0) < 0) {
506 _SE("Couldn't unlinkat %s: %d\n", de->d_name, errno);
515 int remove_dir(const char *path, int del_dir)
521 fd = open(path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
523 _SE("Couldn't opendir %s: %d\n", path, errno);
526 ret = remove_dir_internal(fd);
531 _SE("Couldn't rmdir %s: %d\n", path, errno);
538 int get_exec_pid(const char *execpath)
541 struct dirent *dentry;
548 dp = opendir("/proc");
550 _E("FAIL: open /proc");
554 len = strlen(execpath) + 1;
556 while ((dentry = readdir(dp))) {
557 if (!isdigit(dentry->d_name[0]))
560 pid = atoi(dentry->d_name);
562 snprintf(buf, PATH_MAX, "/proc/%d/cmdline", pid);
563 fd = open(buf, O_RDONLY);
566 ret = read(fd, buf2, PATH_MAX);
569 if (ret < 0 || ret >= PATH_MAX)
574 if (!strncmp(buf2, execpath, len)) {
585 int get_file_count(char *path)
595 while ((dp = readdir(dir))) {
596 const char *name = dp->d_name;
597 /* always skip "." and ".." */
598 if (name[0] == '.') {
601 if ((name[1] == '.') && (name[2] == 0))
610 int get_directory_usage(char *path)
618 fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
627 while ((de = readdir(dir))) {
628 if (!strncmp(de->d_name, ".", 2) || !strncmp(de->d_name, "..", 3))
630 if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
631 _SE("Failed to fstatat %s: %d\n", de->d_name, errno);
641 int log_kmsg(char *fmt, ...)
647 file = fopen("/dev/kmsg", "w");
649 _E("Open /dev/kmsg error: %m");
654 if (vfprintf(file, fmt, ap) < 0) {
655 _E("Write to /dev/kmsg error: %m");
664 * @brief Check wchan of thread
666 * @param pid PID of the inspected process
667 * @param tid TID of the thread to check
669 static int check_thread_wchan(int pid, int tid)
672 char path[PATH_MAX], buf[100];
674 snprintf(path, sizeof(path), "/proc/%d/task/%d/wchan", pid, tid);
675 fd = open(path, O_RDONLY);
677 _E("cannot open %s: %m\n", path);
680 cnt = read(fd, buf, sizeof(buf));
681 if (cnt == -1 || cnt == sizeof(buf)) {
682 _E("read %s error: %m\n", path);
689 if (strncmp("do_coredump", buf, sizeof(buf)) == 0 || strncmp("pipe_wait", buf, sizeof(buf)) == 0)
696 * @brief Find crashed tid if tid was not offered
698 * @param pid PID of the inspected process
700 int find_crash_tid(int pid)
705 struct dirent *entry;
706 char task_path[PATH_MAX];
709 snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
710 if (stat(task_path, &sb) == -1) {
711 _E("no such file: %s", task_path);
715 threadnum = sb.st_nlink - 2;
718 dir = opendir(task_path);
720 _E("cannot open %s\n", task_path);
723 while ((entry = readdir(dir)) != NULL) {
724 if (strcmp(entry->d_name, ".") == 0 ||
725 strcmp(entry->d_name, "..") == 0)
727 crash_tid = check_thread_wchan(pid,
728 atoi(entry->d_name));
735 } else if (threadnum == 1) {
741 char* get_cmd_line(pid_t pid)
743 char cmdline_path[PATH_MAX];
745 snprintf(cmdline_path, sizeof(cmdline_path),
746 "/proc/%d/cmdline", pid);
748 int fd = open(cmdline_path, O_RDONLY);
750 _E("Failed to open %s: %m\n", cmdline_path);
754 char buffer[PATH_MAX];
755 ssize_t ret = read(fd, buffer, sizeof(buffer) - 1);
761 _E("Failed to read %s: %m\n", cmdline_path);
766 if (asprintf(&result, "%s", buffer) == -1) {
767 _E("asprintf() error: %m\n");
774 char* get_exe_path(pid_t pid)
776 char exe_link[PATH_MAX];
777 char buffer[PATH_MAX];
779 snprintf(exe_link, sizeof(exe_link),
780 "/proc/%d/exe", pid);
782 ssize_t ret = readlink(exe_link, buffer, sizeof(buffer) - 1);
786 _E("Failed to read link %s: %m", exe_link);
790 if (access(buffer, F_OK) == -1) {
791 _E("Invalid path %s", buffer);
796 if (asprintf(&result, "%s", buffer) == -1) {
797 _E("asprintf() error: %m\n");