Commit 8e35ff4a authored by Peter Wu's avatar Peter Wu Committed by Christian Persch

spawn: Fix g_spawn deadlock in a multi-threaded program on linux

Ported from glib@f2917459

Issues: glib#945 and glib#1014
parent e229ea34
......@@ -38,6 +38,10 @@
#include <glib-unix.h>
#ifdef __linux__
#include <sys/syscall.h> /* for syscall and SYS_getdents64 */
#endif
#include "vtespawn.hh"
#include "vteutils.h" /* for strchrnul on non-GNU systems */
#include "reaper.hh"
......@@ -410,6 +414,44 @@ set_cloexec (void *data, gint fd)
}
#ifndef HAVE_FDWALK
#ifdef __linux__
struct linux_dirent64
{
guint64 d_ino; /* 64-bit inode number */
guint64 d_off; /* 64-bit offset to next structure */
unsigned short d_reclen; /* Size of this dirent */
unsigned char d_type; /* File type */
char d_name[]; /* Filename (null-terminated) */
};
static gint
filename_to_fd (const char *p)
{
char c;
int fd = 0;
const int cutoff = G_MAXINT / 10;
const int cutlim = G_MAXINT % 10;
if (*p == '\0')
return -1;
while ((c = *p++) != '\0')
{
if (!g_ascii_isdigit (c))
return -1;
c -= '0';
/* Check for overflow. */
if (fd > cutoff || (fd == cutoff && c > cutlim))
return -1;
fd = fd * 10 + c;
}
return fd;
}
#endif
static int
fdwalk (int (*cb)(void *data, int fd), void *data)
{
......@@ -421,45 +463,39 @@ fdwalk (int (*cb)(void *data, int fd), void *data)
struct rlimit rl;
#endif
#ifdef __linux__
DIR *d;
if ((d = opendir("/proc/self/fd"))) {
struct dirent *de;
while ((de = readdir(d))) {
glong l;
gchar *e = NULL;
if (de->d_name[0] == '.')
continue;
errno = 0;
l = strtol(de->d_name, &e, 10);
if (errno != 0 || !e || *e)
continue;
fd = (gint) l;
#ifdef __linux__
/* Avoid use of opendir/closedir since these are not async-signal-safe. */
int dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY);
if (dir_fd >= 0)
{
char buf[4096];
int pos, nread;
struct linux_dirent64 *de;
if ((glong) fd != l)
continue;
while ((nread = syscall (SYS_getdents64, dir_fd, buf, sizeof(buf))) > 0)
{
for (pos = 0; pos < nread; pos += de->d_reclen)
{
de = (struct linux_dirent64 *)(buf + pos);
if (fd == dirfd(d))
continue;
fd = filename_to_fd (de->d_name);
if (fd < 0 || fd == dir_fd)
continue;
if ((res = cb (data, fd)) != 0)
break;
if ((res = cb (data, fd)) != 0)
break;
}
}
closedir(d);
close (dir_fd);
return res;
}
}
/* If /proc is not mounted or not accessible we fall back to the old
* rlimit trick */
#endif
#ifdef HAVE_SYS_RESOURCE_H
if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment