+/* Joins /proc/[pid]/fd/ and /proc/[pid]/fdinfo/ into the following lines:
+ * 0:/dev/pts/23
+ * pos: 0
+ * flags: 0100002
+ *
+ * 1:/dev/pts/23
+ * pos: 0
+ * flags: 0100002
+ *
+ * 2:/dev/pts/23
+ * pos: 0
+ * flags: 0100002
+ * EOF
+ */
+static int compose_open_fds(pid_t pid, char **open_fds) {
+ _cleanup_closedir_ DIR *proc_fd_dir = NULL;
+ _cleanup_close_ int proc_fdinfo_fd = -1;
+ _cleanup_free_ char *buffer = NULL;
+ _cleanup_fclose_ FILE *stream = NULL;
+ const char *fddelim = "", *path;
+ struct dirent *dent = NULL;
+ size_t size = 0;
+ int r = 0;
+
+ assert(pid >= 0);
+ assert(open_fds != NULL);
+
+ path = procfs_file_alloca(pid, "fd");
+ proc_fd_dir = opendir(path);
+ if (!proc_fd_dir)
+ return -errno;
+
+ proc_fdinfo_fd = openat(dirfd(proc_fd_dir), "../fdinfo", O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC|O_PATH);
+ if (proc_fdinfo_fd < 0)
+ return -errno;
+
+ stream = open_memstream(&buffer, &size);
+ if (!stream)
+ return -ENOMEM;
+
+ FOREACH_DIRENT(dent, proc_fd_dir, return -errno) {
+ _cleanup_fclose_ FILE *fdinfo = NULL;
+ _cleanup_free_ char *fdname = NULL;
+ char line[LINE_MAX];
+ int fd;
+
+ r = readlinkat_malloc(dirfd(proc_fd_dir), dent->d_name, &fdname);
+ if (r < 0)
+ return r;
+
+ 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)
+ continue;
+
+ fdinfo = fdopen(fd, "re");
+ if (fdinfo == NULL) {
+ close(fd);
+ continue;
+ }
+
+ FOREACH_LINE(line, fdinfo, break) {
+ fputs(line, stream);
+ if (!endswith(line, "\n"))
+ fputc('\n', stream);
+ }
+ }
+
+ errno = 0;
+ fclose(stream);
+ stream = NULL;
+
+ if (errno != 0)
+ return -errno;
+
+ *open_fds = buffer;
+ buffer = NULL;
+
+ return 0;
+}
+