From 105ff10f12dce019ca2a455001239967c2e0e856 Mon Sep 17 00:00:00 2001
From: Jakub Filak <jfilak@redhat.com>
Date: Mon, 8 Dec 2014 16:02:11 +0100
Subject: [PATCH] ccpp-hook: move /proc/[pid]/ utils to libreport

Related to #548

Signed-off-by: Jakub Filak <jfilak@redhat.com>
---
 src/hooks/abrt-hook-ccpp.c | 202 +++++++++++++++------------------------------
 1 file changed, 66 insertions(+), 136 deletions(-)

diff --git a/src/hooks/abrt-hook-ccpp.c b/src/hooks/abrt-hook-ccpp.c
index f8f97ad..7fd9520 100644
--- a/src/hooks/abrt-hook-ccpp.c
+++ b/src/hooks/abrt-hook-ccpp.c
@@ -36,20 +36,6 @@
  */
 #define IGNORE_RESULT(func_call) do { if (func_call) /* nothing */; } while (0)
 
-static char* malloc_readlink(const char *linkname)
-{
-    char buf[PATH_MAX + 1];
-    int len;
-
-    len = readlink(linkname, buf, sizeof(buf)-1);
-    if (len >= 0)
-    {
-        buf[len] = '\0';
-        return xstrdup(buf);
-    }
-    return NULL;
-}
-
 /* Custom version of copyfd_xyz,
  * one which is able to write into two descriptors at once.
  */
@@ -171,75 +157,6 @@ static char *core_basename = (char*) "core";
  */
 static char *full_core_basename;
 
-
-static char* get_executable(pid_t pid, int *fd_p)
-{
-    char buf[sizeof("/proc/%lu/exe") + sizeof(long)*3];
-
-    sprintf(buf, "/proc/%lu/exe", (long)pid);
-    if (fd_p)
-        *fd_p = open(buf, O_RDONLY); /* might fail and return -1, it's ok */
-    char *executable = malloc_readlink(buf);
-    if (!executable)
-        return NULL;
-    /* find and cut off " (deleted)" from the path */
-    char *deleted = executable + strlen(executable) - strlen(" (deleted)");
-    if (deleted > executable && strcmp(deleted, " (deleted)") == 0)
-    {
-        *deleted = '\0';
-        log_info("File '%s' seems to be deleted", executable);
-    }
-    /* find and cut off prelink suffixes from the path */
-    char *prelink = executable + strlen(executable) - strlen(".#prelink#.XXXXXX");
-    if (prelink > executable && strncmp(prelink, ".#prelink#.", strlen(".#prelink#.")) == 0)
-    {
-        log_info("File '%s' seems to be a prelink temporary file", executable);
-        *prelink = '\0';
-    }
-    return executable;
-}
-
-static char* get_cwd(pid_t pid)
-{
-    char buf[sizeof("/proc/%lu/cwd") + sizeof(long)*3];
-    sprintf(buf, "/proc/%lu/cwd", (long)pid);
-    return malloc_readlink(buf);
-}
-
-static char* get_rootdir(pid_t pid)
-{
-    char buf[sizeof("/proc/%lu/root") + sizeof(long)*3];
-    sprintf(buf, "/proc/%lu/root", (long)pid);
-    return malloc_readlink(buf);
-}
-
-static int get_fsuid(char *proc_pid_status)
-{
-    int real, euid, saved;
-    /* if we fail to parse the uid, then make it root only readable to be safe */
-    int fs_uid = 0;
-
-    char *line = proc_pid_status; /* never NULL */
-    for (;;)
-    {
-        if (strncmp(line, "Uid", 3) == 0)
-        {
-            int n = sscanf(line, "Uid:\t%d\t%d\t%d\t%d\n", &real, &euid, &saved, &fs_uid);
-            if (n != 4)
-            {
-                perror_msg_and_die("Can't parse Uid: line");
-            }
-            break;
-        }
-        line = strchr(line, '\n');
-        if (!line)
-            break;
-        line++;
-    }
-
-    return fs_uid;
-}
-
 static int dump_suid_policy()
 {
     /*
@@ -400,42 +317,6 @@ static int open_user_core(uid_t uid, uid_t fsuid, pid_t pid, char **percent_valu
     return user_core_fd;
 }
 
-static bool dump_fd_info(const char *dest_filename, char *source_filename, int source_base_ofs)
-{
-    FILE *fp = fopen(dest_filename, "w");
-    if (!fp)
-        return false;
-
-    unsigned fd = 0;
-    while (fd <= 99999) /* paranoia check */
-    {
-        sprintf(source_filename + source_base_ofs, "fd/%u", fd);
-        char *name = malloc_readlink(source_filename);
-        if (!name)
-            break;
-        fprintf(fp, "%u:%s\n", fd, name);
-        free(name);
-
-        sprintf(source_filename + source_base_ofs, "fdinfo/%u", fd);
-        fd++;
-        FILE *in = fopen(source_filename, "r");
-        if (!in)
-            continue;
-        char buf[128];
-        while (fgets(buf, sizeof(buf)-1, in))
-        {
-            /* in case the line is not terminated, terminate it */
-            char *eol = strchrnul(buf, '\n');
-            eol[0] = '\n';
-            eol[1] = '\0';
-            fputs(buf, fp);
-        }
-        fclose(in);
-    }
-    fclose(fp);
-    return true;
-}
-
 /* Like xopen, but on error, unlocks and deletes dd and user core */
 static int create_or_die(const char *filename, int user_core_fd)
 {
@@ -513,6 +394,34 @@ static int test_configuration(bool setting_SaveFullCore, bool setting_CreateCore
     return 0;
 }
 
+int save_crashing_binary(pid_t pid, const char *dest_path, uid_t uid, gid_t gid)
+{
+    char buf[sizeof("/proc/%lu/exe") + sizeof(long)*3];
+
+    sprintf(buf, "/proc/%lu/exe", (long)pid);
+    int src_fd_binary = open(buf, O_RDONLY); /* might fail and return -1, it's ok */
+    if (src_fd_binary < 0)
+    {
+        log_notice("Failed to open an image of crashing binary");
+        return 0;
+    }
+
+    int dst_fd = open(dest_path, O_WRONLY | O_CREAT | O_TRUNC, DEFAULT_DUMP_DIR_MODE);
+    if (dst_fd < 0)
+    {
+        log_notice("Failed to create file '%s'", dest_path);
+        close(src_fd_binary);
+        return -1;
+    }
+
+    IGNORE_RESULT(fchown(dst_fd, uid, gid));
+
+    off_t sz = copyfd_eof(src_fd_binary, dst_fd, COPYFD_SPARSE);
+    close(src_fd_binary);
+
+    return fsync(dst_fd) != 0 || close(dst_fd) != 0 || sz < 0;
+}
+
 int main(int argc, char** argv)
 {
     /* Kernel starts us with all fd's closed.
@@ -612,8 +521,7 @@ int main(int argc, char** argv)
 
     char path[PATH_MAX];
 
-    int src_fd_binary = -1;
-    char *executable = get_executable(pid, setting_SaveBinaryImage ? &src_fd_binary : NULL);
+    char *executable = get_executable(pid);
     if (executable && strstr(executable, "/abrt-hook-ccpp"))
     {
         error_msg_and_die("PID %lu is '%s', not dumping it to avoid recursion",
@@ -628,6 +536,9 @@ int main(int argc, char** argv)
 
     uid_t fsuid = uid;
     uid_t tmp_fsuid = get_fsuid(proc_pid_status);
+    if (tmp_fsuid < 0)
+        perror_msg_and_die("Can't parse 'Uid: line' in /proc/%lu/status", (long)pid);
+
     int suid_policy = dump_suid_policy();
     if (tmp_fsuid != uid)
     {
@@ -805,16 +716,16 @@ int main(int argc, char** argv)
 
         dd_save_text(dd, FILENAME_ABRT_VERSION, VERSION);
 
-        if (src_fd_binary > 0)
+        if (setting_SaveBinaryImage)
         {
             strcpy(path + path_len, "/"FILENAME_BINARY);
-            int dst_fd = create_or_die(path, user_core_fd);
-            off_t sz = copyfd_eof(src_fd_binary, dst_fd, COPYFD_SPARSE);
-            if (fsync(dst_fd) != 0 || close(dst_fd) != 0 || sz < 0)
+
+            if (save_crashing_binary(pid, path, dd->dd_uid, dd->dd_gid))
             {
-                dd_delete(dd); error_msg_and_die("Error saving '%s'", path);
+                error_msg("Error saving '%s'", path);
+
+                goto error_exit;
             }
-            close(src_fd_binary);
         }
 
         off_t core_size = 0;
@@ -837,15 +748,12 @@ int main(int argc, char** argv)
             if (fsync(abrt_core_fd) != 0 || close(abrt_core_fd) != 0 || core_size < 0)
             {
                 unlink(path);
-                dd_delete(dd);
-                if (user_core_fd >= 0)
-                {
-                    xchdir(user_pwd);
-                    unlink(core_basename);
-                }
+
                 /* copyfd_sparse logs the error including errno string,
                  * but it does not log file name */
-                error_msg_and_die("Error writing '%s'", path);
+                error_msg("Error writing '%s'", path);
+
+                goto error_exit;
             }
             if (user_core_fd >= 0
                 /* error writing user coredump? */
@@ -865,6 +773,13 @@ int main(int argc, char** argv)
             create_user_core(user_core_fd, pid, ulimit_c);
         }
 
+        /* User core is either written or closed */
+        user_core_fd = -1;
+
+        /*
+         * ! No other errors should cause removal of the user core !
+         */
+
         /* Save JVM crash log if it exists. (JVM's coredump per se
          * is nearly useless for JVM developers)
          */
@@ -891,8 +806,9 @@ int main(int argc, char** argv)
                 off_t sz = copyfd_eof(src_fd, dst_fd, COPYFD_SPARSE);
                 if (close(dst_fd) != 0 || sz < 0)
                 {
-                    dd_delete(dd);
-                    error_msg_and_die("Error saving '%s'", path);
+                    error_msg("Error saving '%s'", path);
+
+                    goto error_exit;
                 }
                 close(src_fd);
             }
@@ -909,6 +825,8 @@ int main(int argc, char** argv)
          * Classic deadlock.
          */
         dd_close(dd);
+        dd = NULL;
+
         path[path_len] = '\0'; /* path now contains only directory name */
         char *newpath = xstrndup(path, path_len - (sizeof(".new")-1));
         if (rename(path, newpath) == 0)
@@ -938,4 +856,16 @@ int main(int argc, char** argv)
 
     /* We didn't create abrt dump, but may need to create compat coredump */
     return create_user_core(user_core_fd, pid, ulimit_c);
+
+error_exit:
+    if (dd)
+        dd_delete(dd);
+
+    if (user_core_fd >= 0)
+    {
+        xchdir(user_pwd);
+        unlink(core_basename);
+    }
+
+    xfunc_die();
 }
-- 
2.1.0

