Blob Blame History Raw
From c16ef7b59b8327332b4252a9c7e70616c363b868 Mon Sep 17 00:00:00 2001
From: Jakub Filak <jfilak@redhat.com>
Date: Wed, 21 Jan 2015 09:37:30 +0100
Subject: [PATCH] rewrite dump_fd_info()

Now, the function is same as from systemd coredump's function compose_open_fds()

Closes #320

Signed-off-by: Jakub Filak <jfilak@redhat.com>
---
 src/include/internal_libreport.h |   4 +-
 src/lib/get_cmdline.c            | 105 ++++++++++++++++++++++++++++++---------
 src/lib/read_write.c             |   7 ++-
 3 files changed, 90 insertions(+), 26 deletions(-)

diff --git a/src/include/internal_libreport.h b/src/include/internal_libreport.h
index 7cda793..00ff7a1 100644
--- a/src/include/internal_libreport.h
+++ b/src/include/internal_libreport.h
@@ -185,6 +185,8 @@ void* xmalloc_open_read_close(const char *filename, size_t *maxsz_p);
 void* xmalloc_xopen_read_close(const char *filename, size_t *maxsz_p);
 #define malloc_readlink libreport_malloc_readlink
 char* malloc_readlink(const char *linkname);
+#define malloc_readlinkat libreport_malloc_readlinkat
+char* malloc_readlinkat(int dir_fd, const char *linkname);
 
 
 /* Returns malloc'ed block */
@@ -641,7 +643,7 @@ char* get_rootdir(pid_t pid);
 #define get_fsuid libreport_get_fsuid
 int get_fsuid(const char *proc_pid_status);
 #define dump_fd_info libreport_dump_fd_info
-int dump_fd_info(const char *dest_filename, char *source_filename, int source_base_ofs);
+int dump_fd_info(const char *dest_filename, const char *proc_pid_fd_path);
 
 /* Takes ptr to time_t, or NULL if you want to use current time.
  * Returns "YYYY-MM-DD-hh:mm:ss" string.
diff --git a/src/lib/get_cmdline.c b/src/lib/get_cmdline.c
index 7ceba2f..2e362c5 100644
--- a/src/lib/get_cmdline.c
+++ b/src/lib/get_cmdline.c
@@ -213,39 +213,96 @@ int get_fsuid(const char *proc_pid_status)
     return fs_uid;
 }
 
-int dump_fd_info(const char *dest_filename, char *source_filename, int source_base_ofs)
+int dump_fd_info(const char *dest_filename, const char *proc_pid_fd_path)
 {
-    FILE *fp = fopen(dest_filename, "w");
-    if (!fp)
-        return 0;
+    DIR *proc_fd_dir = NULL;
+    int proc_fdinfo_fd = -1;
+    char *buffer = NULL;
+    FILE *stream = NULL;
+    const char *fddelim = "";
+    struct dirent *dent = NULL;
+    int r = 0;
 
-    /*TODO: BUG: there might be holes as programs can close any fd at any time*/
-    unsigned fd = 0;
-    while (fd <= 99999) /* paranoia check */
+    proc_fd_dir = opendir(proc_pid_fd_path);
+    if (!proc_fd_dir)
     {
-        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);
+        r = -errno;
+        goto dumpfd_cleanup;
+    }
 
-        sprintf(source_filename + source_base_ofs, "fdinfo/%u", fd);
-        fd++;
-        FILE *in = fopen(source_filename, "r");
-        if (!in)
+    proc_fdinfo_fd = openat(dirfd(proc_fd_dir), "../fdinfo", O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC|O_PATH);
+    if (proc_fdinfo_fd < 0)
+    {
+        r = -errno;
+        goto dumpfd_cleanup;
+    }
+
+    stream = fopen(dest_filename, "w");
+    if (!stream)
+    {
+        r = -ENOMEM;
+        goto dumpfd_cleanup;
+    }
+
+    while (1)
+    {
+        errno = 0;
+        dent = readdir(proc_fd_dir);
+        if (dent == NULL)
+        {
+            if (errno > 0)
+            {
+                r = -errno;
+                goto dumpfd_cleanup;
+            }
+            break;
+        }
+        else if (dot_or_dotdot(dent->d_name))
             continue;
-        char buf[128];
-        while (fgets(buf, sizeof(buf)-1, in))
+
+        FILE *fdinfo = NULL;
+        char *fdname = NULL;
+        char line[LINE_MAX];
+        int fd;
+
+        fdname = malloc_readlinkat(dirfd(proc_fd_dir), dent->d_name);
+
+        fprintf(stream, "%s%s:%s\n", fddelim, dent->d_name, fdname);
+        fddelim = "\n";
+
+        /* Use the directory entry from /proc/[pid]/fd with /proc/[pid]/fdinfo */
+        fd = openat(proc_fdinfo_fd, dent->d_name, O_NOFOLLOW|O_CLOEXEC|O_RDONLY);
+        if (fd < 0)
+            goto dumpfd_next_fd;
+
+        fdinfo = fdopen(fd, "re");
+        if (fdinfo == NULL)
+            goto dumpfd_next_fd;
+
+        while (fgets(line, sizeof(line)-1, fdinfo))
         {
             /* in case the line is not terminated, terminate it */
-            char *eol = strchrnul(buf, '\n');
+            char *eol = strchrnul(line, '\n');
             eol[0] = '\n';
             eol[1] = '\0';
-            fputs(buf, fp);
+            fputs(line, stream);
         }
-        fclose(in);
+
+dumpfd_next_fd:
+        fclose(fdinfo);
+        free(fdname);
     }
-    fclose(fp);
-    return 1;
+
+dumpfd_cleanup:
+    errno = 0;
+    fclose(stream);
+
+    if (r == 0 && errno != 0)
+        r = -errno;
+
+    closedir(proc_fd_dir);
+    close(proc_fdinfo_fd);
+    free(buffer);
+
+    return r;
 }
diff --git a/src/lib/read_write.c b/src/lib/read_write.c
index 3f3bd1e..657adb0 100644
--- a/src/lib/read_write.c
+++ b/src/lib/read_write.c
@@ -194,10 +194,15 @@ void* xmalloc_xopen_read_close(const char *filename, size_t *maxsz_p)
 
 char* malloc_readlink(const char *linkname)
 {
+    return malloc_readlinkat(AT_FDCWD, linkname);
+}
+
+char* malloc_readlinkat(int dir_fd, const char *linkname)
+{
     char buf[PATH_MAX + 1];
     int len;
 
-    len = readlink(linkname, buf, sizeof(buf)-1);
+    len = readlinkat(dir_fd, linkname, buf, sizeof(buf)-1);
     if (len >= 0)
     {
         buf[len] = '\0';
-- 
2.1.0