/*
* 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)
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;
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;
}
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;
}
sfd = open(src, O_RDONLY);
if (sfd < 0) {
- _SE("Failed to open (%s)\n", src);
+ _E("Failed to open (%s)\n", src);
return -1;
}
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) {
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;
}
}
}
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;
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);
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;
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;
}
+
/**
* @}
*/