Fix svace issues
[platform/core/system/crash-worker.git] / src / shared / util.c
index 775d6be..0606045 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * crash-manager
- * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2012-2019 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the License);
  * you may not use this file except in compliance with the License.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <sys/file.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <ctype.h>
 #include <grp.h>
 
+#include "defs.h"
 #include "util.h"
 #include "log.h"
 
-#define READ_BUFF_SIZE 4096
-#define SELECT_TIMEOUT_US 100000
-
-int system_command_parallel(char *command)
-{
-       int pid = 0;
-       const char *environ[] = { NULL };
-
-       if (command == NULL)
-               return -1;
-       pid = fork();
-       if (pid == -1)
-               return -1;
-       if (pid == 0) {
-               char *argv[4];
-               argv[0] = "sh";
-               argv[1] = "-c";
-               argv[2] = (char *)command;
-               argv[3] = 0;
-               execve("/bin/sh", argv, (char **)environ);
-               exit(127);
-       }
-
-       return pid;
-}
-
-int wait_system_command(int pid)
-{
-       int status = 0;
-
-       if (pid < 0)
-               return -1;
-
-       do {
-               if (waitpid(pid, &status, 0) == -1) {
-                       if (errno != EINTR)
-                               return -1;
-               } else {
-                       if (WIFEXITED(status))
-                               return WEXITSTATUS(status);
-                       else if (WIFSIGNALED(status))
-                               return WTERMSIG(status);
-                       else if (WIFSTOPPED(status))
-                               return WSTOPSIG(status);
-               }
-       } while (!WIFEXITED(status) && !WIFSIGNALED(status));
-
-       return 0;
-}
-
-int system_command(char *command)
-{
-       int pid = 0;
-
-       pid = system_command_parallel(command);
-
-       return wait_system_command(pid);
-}
-
-int system_command_with_timeout(int timeout_seconds, char *command)
+bool have_livecoredumper(void)
 {
-       const char *environ[] = { NULL };
-
-       if (command == NULL)
-               return -1;
-       clock_t start = clock();
-       pid_t pid = fork();
-       /* handle error case */
-       if (pid < 0) {
-               _E("fork: %d\n", errno);
-               return pid;
-       }
-       /* handle child case */
-       if (pid == 0) {
-               char *argv[4];
-               argv[0] = "sh";
-               argv[1] = "-c";
-               argv[2] = (char *)command;
-               argv[3] = 0;
-
-               execve("/bin/sh", argv, (char **)environ);
-               _SI("exec(%s): %d\n", command, errno);
-               _exit(-1);
-       }
-       /* handle parent case */
-       for (;;) {
-               int status;
-               pid_t p = waitpid(pid, &status, WNOHANG);
-               float elapsed = (float) (clock() - start) / CLOCKS_PER_SEC;
-               if (p == pid) {
-                       if (WIFSIGNALED(status))
-                               _SI("%s: Killed by signal %d\n", command, WTERMSIG(status));
-                       else if (WIFEXITED(status) && WEXITSTATUS(status) > 0)
-                               _SI("%s: Exit code %d\n", command, WEXITSTATUS(status));
-                       return WEXITSTATUS(status);
-               }
-               if (timeout_seconds && elapsed > timeout_seconds) {
-                       _SI("%s: Timed out after %.1fs (killing pid %d)\n",
-                                       command, elapsed, pid);
-                       kill(pid, SIGTERM);
-                       return -1;
-               }
-               /* poll every 0.1 sec */
-               usleep(100000);
-       }
+       return access(LIVEDUMPER_BIN_PATH, X_OK) == 0;
 }
 
 int write_fd(int fd, const void *buf, int len)
@@ -221,7 +123,7 @@ int copy_bytes(int destfd, int srcfd, off_t *ncopied)
        return r == 0 ? r : copy_bytes_rw(destfd, srcfd, ncopied);
 }
 
-int copy_file(char *dst, char *src)
+int copy_file(const char *dst, const char *src)
 {
        int res;
        int sfd;
@@ -239,7 +141,7 @@ int copy_file(char *dst, char *src)
        dfd = open(dst, O_WRONLY|O_CREAT|O_EXCL, 0644);
        if (dfd < 0) {
                close(sfd);
-               _SE("Failed to open (%s)\n", dst);
+               _E("Failed to open (%s)\n", dst);
                return -1;
        }
 
@@ -250,7 +152,7 @@ int copy_file(char *dst, char *src)
        return res;
 }
 
-int move_file(char *dst, char *src)
+int move_file(const char *dst, const char *src)
 {
        if (copy_file(dst, src) < 0)
                return -1;
@@ -270,7 +172,7 @@ int dump_file_write_fd(int dfd, char *src)
        }
        sfd = open(src, O_RDONLY);
        if (sfd < 0) {
-               _SE("Failed to open (%s)\n", src);
+               _E("Failed to open (%s)\n", src);
                return -1;
        }
 
@@ -280,237 +182,60 @@ int dump_file_write_fd(int dfd, char *src)
        return res;
 }
 
-static int run_command(char *path, char *args[], char *env[], int fd[])
+int fsync_path(char *const path)
 {
-       if (dup2(fd[1], STDOUT_FILENO) == -1) {
-               _E("dup2 error: %m");
-               return -1;
-       }
-
-       if (close(fd[1]) == -1) {
-               _E("close fd error: %m");
-               return -1;
-       }
+       int fd, ret;
 
-       if (close(fd[0]) == -1) {
-               _E("close fd error: %m");
-               return -1;
+       ret = fd = open(path, O_RDONLY);
+       if (fd >= 0) {
+               ret = fsync(fd);
+               close(fd);
        }
 
-       if (execvpe(path, args, env) == -1) {
-               _E("run command %s error: %m", path);
-               return -1;
-       }
-       return -1;
-}
+       if (ret < 0)
+               _E("Unable to fsync %s: %m", path);
 
-static int wait_for_child(pid_t pid, int *exit_code, int timeout)
-{
-       for (int i = 0; i < 10*timeout; i++) {
-               int status;
-               pid_t p = waitpid(pid, &status, WNOHANG);
-               if (p == pid) {
-                       if (WIFSIGNALED(status)) {
-                               _I("Killed by signal %d\n", WTERMSIG(status));
-                               return -1;
-                       } else if (WIFEXITED(status)) {
-                               *exit_code = WEXITSTATUS(status);
-                               return 0;
-                       }
-               } else if (p == -1) {
-                       _E("waitpid error: %m");
-                       return -1;
-               }
-               usleep(100000);
-       }
-       return -1;
-}
-
-static int read_into_buff(int fd, char *buff, int size, int timeout_us, int *eof)
-{
-       struct timeval tout;
-       int sel_ret;
-       fd_set set;
-
-       FD_ZERO(&set);
-       FD_SET(fd, &set);
-
-       tout.tv_sec = timeout_us / 1000000;
-       tout.tv_usec = timeout_us % 1000000;
-       *eof = 0;
-
-       int buff_pos = 0;
-       if ((sel_ret = select(fd+1, &set, NULL, NULL, &tout)) >= 0) {
-               if (sel_ret > 0) {
-                       // we can do nonblocking read
-                       int readed = read(fd, &buff[buff_pos], size);
-
-                       if (readed > 0) {
-                               buff_pos += readed;
-                               size -= readed;
-                       } else if (readed == 0) {
-                               // no more data to read
-                               *eof = 1;
-                       } else {
-                               // error
-                               _E("read data from the pipe error: %m");
-                               return -1;
-                       }
-               }
-       } else
-               _E("select() error: %m");
-       return buff_pos;
+       return ret;
 }
 
-// Usage:
-// if buff is not NULL then 'size' bytes of the result is written the buffer,
-// otherwise result is written to the dfd descriptor
-int run_command_write_fd_timeout(char *path, char *args[], char *env[], int dfd, char *buff, int size, int timeout)
+bool make_dir(const char *path, int mode)
 {
-       char BUFF[READ_BUFF_SIZE];
-       int fd[2];
-       struct timeval start, end;
-       int write_to_fd = buff == NULL ? 1 : 0;
-
-       if (!write_to_fd && size <= 0) {
-               _E("buffer size must be greather than zero");
-               return -1;
-       }
+       int r = mkdir(path, mode);
 
-       if (pipe(fd)) {
-               _E("pipe create error: %m");
-               return -1;
+       if (r < 0 && errno != EEXIST) {
+               _E("Unable to create directory %s: %m", path);
+               return false;
        }
 
-       pid_t pid = fork();
-
-       if (pid == 0) {
-               return run_command(path, args, env, fd);
-       } else if (pid > 0) {
-               if (close(fd[1]) == -1) {
-                       _E("close fd error: %m");
-                       return -1;
-               }
-
-               if (gettimeofday(&start, NULL) == -1) {
-                       _E("gettimeofday error: %m");
-                       return -1;
-               }
-
-               int readed;
-               int eof = 0;
-               int outdated = 0;
-               int count = 0;
+       if (chmod(path, mode) != 0) // Fixup permissions for directories created with bad umask
+               _E("Error changing permission for %s to %d: %m", path, mode);
 
-               int act_size;
-               char *act_buff;
+       if (r >= 0)
+               return true;
 
-               if (write_to_fd) {
-                       act_size = READ_BUFF_SIZE;
-                       act_buff = BUFF;
-               } else {
-                       act_size = size;
-                       act_buff = buff;
-               }
+       struct stat st = {0};
+       r = stat(path, &st);
+       bool isdir = !!(st.st_mode & S_IFDIR);
 
-               while ((readed = read_into_buff(fd[0], act_buff, act_size, 1000000, &eof)) >= 0) {
-                       if (readed > 0) {
-                               // we have some data
-                               if (count < (INT_MAX - readed))
-                                       count += readed;
-                               else
-                                       count = INT_MAX;
-
-                               if (write_to_fd) {
-                                       if (write_fd(dfd, act_buff, readed) == -1) {
-                                               _E("write data to pipe error: %m");
-                                               break;
-                                       }
-                               } else {
-                                       act_buff += readed;
-                                       act_size -= readed;
-
-                                       if (act_size == 0) {
-                                               // buff is full, we can return
-                                               eof = 1;
-                                       }
-                               }
-                       }
+       if (!r && isdir)
+               return true;
+       else if (!r && !isdir)
+               errno = ENOTDIR;
 
-                       if (eof)
-                               break;
-
-                       if (gettimeofday(&end, NULL) == -1) {
-                               _E("gettimeofday error: %m");
-                               break;
-                       }
-
-                       if ((end.tv_sec - start.tv_sec) > timeout) {
-                               outdated = 1;
-                               break;
-                       }
-
-                       if (readed == 0)
-                               usleep(100000);
-               }
-
-               if (outdated) {
-                       _E("command timeout: %s", path);
-                       if (kill(pid, 0) == 0) {
-                               // we can kill a child because we don't
-                               // need it anymore
-                               if (kill(pid, SIGTERM) == -1)
-                                       _E("kill child %d error: %m", pid);
-                       }
-               }
-
-               if (close(fd[0]) == -1) {
-                       _E("close fd error: %m");
-                       return -1;
-               }
-
-               // let's wait a second for a child
-               int exit_code = -1;
-               int wait_res = wait_for_child(pid, &exit_code, 1);
-
-               if (wait_res != 0)
-                       _I("wait_for_child for \%s\" returns non-zero value\n", path);
-               else if (exit_code != 0)
-                       _I("\"%s\" exit code: %d\n", path, exit_code);
-
-               return (eof == 1 && exit_code == 0) ? count : -abs(exit_code);
-       } else {
-               _E("fork() error: %m");
-               return -1;
-       }
-
-       return -1;
+       _E("Failure while trying to ensure %s exists and it is directory: %m", path);
+       return false;
 }
 
-int run_command_timeout(char *path, char *args[], char *env[], int timeout)
-{
-       int fd = open("/dev/null", O_WRONLY);
-       if (fd < 0) {
-               _E("open /dev/null error: %m");
-               return -1;
-       }
-
-       int res = run_command_write_fd_timeout(path, args, env, fd, NULL, 0, timeout);
-
-       close(fd);
-
-       return res;
-}
-
-static int remove_dir_internal(int fd)
+static bool remove_dir_internal(int fd)
 {
        DIR *dir;
        struct dirent *de;
-       int subfd, ret = 0;
+       bool ret = true;
+       int subfd = 0;
 
        dir = fdopendir(fd);
        if (!dir)
-               return -1;
+               return false;
 
        while ((de = readdir(dir))) {
                if (de->d_type == DT_DIR) {
@@ -518,21 +243,21 @@ static int remove_dir_internal(int fd)
                                continue;
                        subfd = openat(fd, de->d_name, O_RDONLY | O_DIRECTORY);
                        if (subfd < 0) {
-                               _SE("Couldn't openat %s: %d\n", de->d_name, errno);
-                               ret = -1;
+                               _E("Couldn't openat %s: %d\n", de->d_name, errno);
+                               ret = false;
                                continue;
                        }
-                       if (remove_dir_internal(subfd))
-                               ret = -1;
+                       if (!remove_dir_internal(subfd))
+                               ret = false;
                        close(subfd);
                        if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
-                               _SE("Couldn't unlinkat %s: %d\n", de->d_name, errno);
-                               ret = -1;
+                               _E("Couldn't unlinkat %s: %d\n", de->d_name, errno);
+                               ret = false;
                        }
                } else {
                        if (unlinkat(fd, de->d_name, 0) < 0) {
-                               _SE("Couldn't unlinkat %s: %d\n", de->d_name, errno);
-                               ret = -1;
+                               _E("Couldn't unlinkat %s: %d\n", de->d_name, errno);
+                               ret = false;
                        }
                }
        }
@@ -540,29 +265,62 @@ static int remove_dir_internal(int fd)
        return ret;
 }
 
-int remove_dir(const char *path, int del_dir)
+bool remove_dir(const char *path, bool del_dir)
 {
-       int fd, ret = 0;
+       bool ret = true;
+       int fd = 0;
 
        if (!path)
-               return -1;
+               return false;
        fd = open(path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
        if (fd < 0) {
-               _SE("Couldn't opendir %s: %d\n", path, errno);
-               return -errno;
+               _E("Couldn't opendir %s: %d\n", path, errno);
+               return false;
        }
        ret = remove_dir_internal(fd);
        close(fd);
 
        if (del_dir) {
                if (rmdir(path)) {
-                       _SE("Couldn't rmdir %s: %d\n", path, errno);
-                       ret = -1;
+                       _E("Couldn't rmdir %s: %d\n", path, errno);
+                       ret = false;
                }
        }
        return ret;
 }
 
+int lock_dir(const char *path, bool block)
+{
+       assert(path);
+
+       int fd;
+
+       if ((fd = open(path, O_RDONLY | O_DIRECTORY)) == -1) {
+               _E("Failed to open %s: %m", path);
+               return -1;
+       }
+
+       int flock_flags = LOCK_EX;
+
+       if (!block)
+               flock_flags |= LOCK_NB;
+
+       if (flock(fd, flock_flags) == -1) {
+               _E("Failed to lock %s for exclusive access: %m", path);
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+void unlock_dir(int fd)
+{
+       if (flock(fd, LOCK_UN) < 0)
+               _E("Failed to unlock file descriptor: %m");
+       close(fd);
+}
+
 int get_exec_pid(const char *execpath)
 {
        DIR *dp;
@@ -635,12 +393,12 @@ int get_file_count(char *path)
        return count;
 }
 
-int get_directory_usage(char *path)
+off_t get_directory_usage(char *path)
 {
        DIR *dir;
        struct dirent *de;
        struct stat st;
-       size_t usage = 0;
+       off_t usage = 0;
        int fd = -1;
 
        fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
@@ -656,7 +414,7 @@ int get_directory_usage(char *path)
                if (!strncmp(de->d_name, ".", 2) || !strncmp(de->d_name, "..", 3))
                        continue;
                if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
-                       _SE("Failed to fstatat  %s: %d\n", de->d_name, errno);
+                       _E("Failed to fstatat  %s: %d\n", de->d_name, errno);
                        continue;
                }
                usage += st.st_size;
@@ -766,79 +524,195 @@ int find_crash_tid(int pid)
        return -1;
 }
 
-char* get_cmd_line(pid_t pid)
+bool filter_drop_trailing_whitespace(char *buf, int size, int datalen)
+{
+       assert(buf);
+       assert(datalen <= size);
+
+       bool filtered = false;
+
+       for (int i = datalen; i >= 0 && isspace(buf[i]); --i) {
+               buf[i] = '\0';
+               filtered = true;
+       }
+
+       return filtered;
+}
+
+bool read_proc_file(pid_t pid, const char * const file, char *outbuf, size_t bufsize, charp0filter filter)
 {
-       char cmdline_path[PATH_MAX];
+       assert(file);
+       assert(outbuf);
+       assert(bufsize > 0);
 
-       snprintf(cmdline_path, sizeof(cmdline_path),
-                       "/proc/%d/cmdline", pid);
+       char path[PATH_MAX];
 
-       int fd = open(cmdline_path, O_RDONLY);
+       snprintf(path, sizeof(path), "/proc/%d/%s", pid, file);
+
+       int fd = open(path, O_RDONLY);
        if (fd < 0) {
-               _E("Failed to open %s: %m\n", cmdline_path);
-               return NULL;
+               _E("Failed to open %s: %m\n", path);
+               return false;
+       }
+
+       ssize_t ret = read(fd, outbuf, bufsize - 1);
+       if (ret < 0) {
+               _E("Failed to read %s: %m\n", path);
+               goto err_close;
        }
+       outbuf[ret] = '\0';
 
-       char buffer[PATH_MAX];
-       ssize_t ret = read(fd, buffer, sizeof(buffer) - 1);
        close(fd);
 
+       if (ret > 0 && filter)
+               (void)filter(outbuf, bufsize, ret - 1);
+
+       return true;
+
+err_close:
+       close(fd);
+       return false;
+}
+
+bool get_exe_path(pid_t pid, char *outbuf, size_t outbuf_len)
+{
+       assert(outbuf);
+       assert(outbuf_len > 0);
+
+       char exe_link[PATH_MAX];
+
+       snprintf(exe_link, sizeof(exe_link), "/proc/%d/exe", pid);
+
+       ssize_t ret = readlink(exe_link, outbuf, outbuf_len - 1);
        if (ret <= 0) {
-               _E("Failed to read %s: %m\n", cmdline_path);
-               return NULL;
+               _E("Failed to read link %s: %m", exe_link);
+               return false;
+       }
+       outbuf[ret] = '\0';
+
+       if (access(outbuf, F_OK) == -1) {
+               _E("Unable to access %s: %m", outbuf);
+               return false;
        }
-       buffer[ret] = '\0';
+       return true;
+}
+
+/* This function is supposed to accept same data as passed to execve
+ * (argv and envp), which can be arrays of strings as well as NULL
+ * pointer.
+ */
+char *concatenate(char *const vec[])
+{
+       size_t length = 0;
+       for (char *const *p = vec; p && *p; p++)
+               length += strlen(*p) + 1;
+
+       if (length == 0)
+               return strdup("");
 
-       char *result;
-       if (asprintf(&result, "%s", buffer) == -1) {
-               _E("asprintf() error: %m\n");
+       char *str = (char *)malloc(length);
+       if (!str)
                return NULL;
+
+       char *destp = str;
+       char *const *vecp = vec;
+       while (*vecp) {
+               destp = stpcpy(destp, *(vecp++));
+               if (*vecp)
+                       destp = stpcpy(destp, " ");
        }
 
-       return result;
+       return str;
 }
 
-char* get_exe_path(pid_t pid)
+bool string_ends_with(const char *string, const char *suffix)
 {
-       char exe_link[PATH_MAX];
-       char buffer[PATH_MAX];
+       const size_t string_len = strlen(string);
+       const size_t suffix_len = strlen(suffix);
 
-       snprintf(exe_link, sizeof(exe_link),
-               "/proc/%d/exe", pid);
+       return (string_len >= suffix_len) && !strcmp(string + string_len - suffix_len, suffix);
+}
 
-       ssize_t ret = readlink(exe_link, buffer, sizeof(buffer) - 1);
+/* what for: kernel.core_pattern's %E replaces / with !, this function does opposite process */
+void kernel_exe_path_normalize(char *path)
+{
+       assert(path);
 
-       if (ret <= 0) {
-               _E("Failed to read link %s: %m", exe_link);
-               return NULL;
+       for (size_t i = 0, len = strlen(path); i < len; i++)
+               path[i] = path[i] == '!' ? '/' : path[i];
+}
+
+bool file_exists(const char *path)
+{
+       struct stat buf;
+       return stat(path, &buf) == 0;
+}
+
+bool file_exists_in_dir(const char *base_dir, const char *file_name)
+{
+       char *path;
+       bool result = false;
+
+       if (asprintf(&path, "%s/%s", base_dir, file_name) == -1) {
+               _E("Failed to asprintf for path: %m");
+       } else {
+               result = file_exists(path);
+               free(path);
        }
-       buffer[ret] = '\0';
 
-       if (access(buffer, F_OK) == -1) {
-               _E("Invalid path %s", buffer);
-               return NULL;
+       return result;
+}
+
+bool write_to_file(const char *content, const char *base_dir, const char *file_name)
+{
+       char *path;
+       bool result = false;
+
+       if (asprintf(&path, "%s/%s", base_dir, file_name) == -1) {
+               _E("Failed to asprintf for path: %m");
+               return false;
        }
 
-       char *result;
-       if (asprintf(&result, "%s", buffer) == -1) {
-               _E("asprintf() error: %m\n");
-               return NULL;
+       int fd = open(path, O_WRONLY | O_CREAT, 0600);
+
+       if (fd < 0) {
+               _E("Failed to open %s: %m", path);
+               goto exit;
+       }
+
+       if (dprintf(fd, "%s", content) < 0) {
+               _E("Failed to write to file %s: %m", path);
+               close(fd);
+               goto exit;
        }
 
+       close(fd);
+exit:
+       free(path);
        return result;
 }
 
-char *nullvec2str(char *const vec[])
+bool is_dotnet_file(const char *path)
 {
-       char command[PATH_MAX] = {0, };
+       static const char *DOTNET_EXTS[] = { ".dll", ".exe", NULL };
+
+       bool result = false;
+       size_t p_len = strlen(path);
+       while (p_len > 0 && path[p_len-1] == '\n')
+               p_len--;
 
-       for (char *const *p = vec; *p; ++p) {
-               strncat(command, *p, sizeof(command)-1);
-               strncat(command, " ", sizeof(command)-1);
+       for (const char **ext = DOTNET_EXTS; *ext != NULL; ext++) {
+               size_t d_len = strlen(*ext);
+
+               if ((p_len >= d_len) &&
+                   (strncasecmp(&path[p_len - d_len], *ext, d_len) == 0)) {
+                       result = true;
+                       break;
+               }
        }
-       command[sizeof(command)-1] = 0;
-       return strdup(command);
+       return result;
 }
+
 /**
  * @}
  */