summaryrefslogtreecommitdiffstats
path: root/src/lib/restartable-syscall-wrappers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/restartable-syscall-wrappers.c')
-rw-r--r--src/lib/restartable-syscall-wrappers.c287
1 files changed, 287 insertions, 0 deletions
diff --git a/src/lib/restartable-syscall-wrappers.c b/src/lib/restartable-syscall-wrappers.c
new file mode 100644
index 0000000..9f593a5
--- /dev/null
+++ b/src/lib/restartable-syscall-wrappers.c
@@ -0,0 +1,287 @@
+/* $Id: restartable-syscall-wrappers.c 2851 2016-08-31 17:30:52Z bird $ */
+/** @file
+ * restartable-syscall-wrappers.c - Workaround for annoying S11 "features".
+ *
+ * The symptoms are that open or mkdir occationally fails with EINTR when
+ * receiving SIGCHLD at the wrong time. With a enough cores, this start
+ * happening on a regular basis.
+ *
+ * The workaround here is to create our own wrappers for these syscalls which
+ * will restart the syscall when appropriate. This depends on the libc
+ * providing alternative names for the syscall entry points.
+ */
+
+/*
+ * Copyright (c) 2011-2016 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Alternatively, the content of this file may be used under the terms of the
+ * GPL version 2 or later, or LGPL version 2.1 or later.
+ */
+
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <sys/types.h>
+#ifdef KBUILD_OS_SOLARIS
+# include <string.h> /* Try drag in feature_tests.h. */
+# include <ctype.h>
+# undef _RESTRICT_KYWD
+# define _RESTRICT_KYWD
+# undef __PRAGMA_REDEFINE_EXTNAME
+#endif
+#include <sys/stat.h>
+#include <utime.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** Mangle a syscall name to it's weak alias. */
+#ifdef KBUILD_OS_SOLARIS
+# define WRAP(a_name) _##a_name
+#elif defined(KBUILD_OS_LINUX)
+# define WRAP(a_name) __##a_name
+#else
+# error "Port Me"
+#endif
+
+/** Mangle a syscall name with optional '64' suffix. */
+#if !defined(_LP64) && _FILE_OFFSET_BITS == 64
+# define WRAP64(a_name) WRAP(a_name)##64
+#else
+# define WRAP64(a_name) WRAP(a_name)
+#endif
+
+/** Check whether errno indicates restart. */
+#ifdef ERESTART
+# define SHOULD_RESTART() (errno == EINTR || errno == ERESTART)
+#else
+# define SHOULD_RESTART() (errno == EINTR)
+#endif
+
+/** Used by XSTR. */
+#define XSTR_INNER(x) #x
+/** Returns the expanded argument as a string. */
+#define XSTR(x) XSTR_INNER(x)
+
+
+static int dlsym_libc(const char *pszSymbol, void **ppvSym)
+{
+ static void *s_pvLibc = NULL;
+ void *pvLibc;
+ void *pvSym;
+
+ /*
+ * Use the RTLD_NEXT dl feature if present, it's designed for doing
+ * exactly what we want here.
+ */
+#ifdef RTLD_NEXT
+ pvSym = dlsym(RTLD_NEXT, pszSymbol);
+ if (pvSym)
+ {
+ *ppvSym = pvSym;
+ return 0;
+ }
+#endif
+
+ /*
+ * Open libc.
+ */
+ pvLibc = s_pvLibc;
+ if (!pvLibc)
+ {
+#ifdef RTLD_NOLOAD
+ unsigned fFlags = RTLD_NOLOAD | RTLD_NOW;
+#else
+ unsigned fFlags = RTLD_GLOBAL | RTLD_NOW;
+#endif
+#ifdef KBUILD_OS_LINUX
+ pvLibc = dlopen("/lib/libc.so.6", fFlags);
+#else
+ pvLibc = dlopen("/lib/libc.so", fFlags);
+#endif
+ if (!pvLibc)
+ {
+ fprintf(stderr, "restartable-syscall-wrappers: failed to dlopen libc for resolving %s: %s\n",
+ pszSymbol, dlerror());
+ errno = ENOSYS;
+ return -1;
+ }
+ /** @todo check standard symbol? */
+ }
+
+ /*
+ * Resolve the symbol.
+ */
+ pvSym = dlsym(pvLibc, pszSymbol);
+ if (!pvSym)
+ {
+ fprintf(stderr, "restartable-syscall-wrappers: failed to resolve %s: %s\n",
+ pszSymbol, dlerror());
+ errno = ENOSYS;
+ return -1;
+ }
+
+ *ppvSym = pvSym;
+ return 0;
+}
+
+
+#undef open
+int open(const char *pszPath, int fFlags, ...)
+{
+ mode_t fMode;
+ va_list va;
+ int fd;
+ static union
+ {
+ int (* pfnReal)(const char *, int, ...);
+ void *pvSym;
+ } s_u;
+
+ if ( !s_u.pfnReal
+ && dlsym_libc("open", &s_u.pvSym) != 0)
+ return -1;
+
+ va_start(va, fFlags);
+ fMode = va_arg(va, mode_t);
+ va_end(va);
+
+ do
+ fd = s_u.pfnReal(pszPath, fFlags, fMode);
+ while (fd == -1 && SHOULD_RESTART());
+ return fd;
+}
+
+#undef open64
+int open64(const char *pszPath, int fFlags, ...)
+{
+ mode_t fMode;
+ va_list va;
+ int fd;
+ static union
+ {
+ int (* pfnReal)(const char *, int, ...);
+ void *pvSym;
+ } s_u;
+
+ if ( !s_u.pfnReal
+ && dlsym_libc("open64", &s_u.pvSym) != 0)
+ return -1;
+
+ va_start(va, fFlags);
+ fMode = va_arg(va, mode_t);
+ va_end(va);
+
+ do
+ fd = s_u.pfnReal(pszPath, fFlags, fMode);
+ while (fd == -1 && SHOULD_RESTART());
+ return fd;
+}
+
+#define WRAP_FN(a_Name, a_ParamsWithTypes, a_ParamsNoType, a_RetType, a_RetFailed) \
+ a_RetType a_Name a_ParamsWithTypes \
+ { \
+ static union \
+ { \
+ a_RetType (* pfnReal) a_ParamsWithTypes; \
+ void *pvSym; \
+ } s_u; \
+ a_RetType rc; \
+ \
+ if ( !s_u.pfnReal \
+ && dlsym_libc(#a_Name, &s_u.pvSym) != 0) \
+ return a_RetFailed; \
+ \
+ do \
+ rc = s_u.pfnReal a_ParamsNoType; \
+ while (rc == a_RetFailed && SHOULD_RESTART()); \
+ return rc; \
+ } typedef int ignore_semi_colon_##a_Name
+
+#undef mkdir
+WRAP_FN(mkdir, (const char *pszPath, mode_t fMode), (pszPath, fMode), int, -1);
+
+#undef rmdir
+WRAP_FN(rmdir, (const char *pszPath, mode_t fMode), (pszPath, fMode), int, -1);
+
+#undef unlink
+WRAP_FN(unlink, (const char *pszPath), (pszPath), int, -1);
+
+#undef remove
+WRAP_FN(remove, (const char *pszPath), (pszPath), int, -1);
+
+#undef symlink
+WRAP_FN(symlink, (const char *pszFrom, const char *pszTo), (pszFrom, pszTo), int, -1);
+
+#undef link
+WRAP_FN(link, (const char *pszFrom, const char *pszTo), (pszFrom, pszTo), int, -1);
+
+#undef stat
+WRAP_FN(stat, (const char *pszPath, struct stat *pStBuf), (pszPath, pStBuf), int, -1);
+#undef lstat
+WRAP_FN(lstat, (const char *pszPath, struct stat *pStBuf), (pszPath, pStBuf), int, -1);
+
+#undef stat64
+WRAP_FN(stat64, (const char *pszPath, struct stat64 *pStBuf), (pszPath, pStBuf), int, -1);
+#undef lstat64
+WRAP_FN(lstat64, (const char *pszPath, struct stat64 *pStBuf), (pszPath, pStBuf), int, -1);
+
+#undef read
+WRAP_FN(read, (int fd, void *pvBuf, size_t cbBuf), (fd, pvBuf, cbBuf), ssize_t, -1);
+
+#undef write
+WRAP_FN(write, (int fd, void *pvBuf, size_t cbBuf), (fd, pvBuf, cbBuf), ssize_t, -1);
+
+#undef fopen
+WRAP_FN(fopen, (const char *pszPath, const char *pszMode), (pszPath, pszMode), FILE *, NULL);
+#undef fopen64
+WRAP_FN(fopen64, (const char *pszPath, const char *pszMode), (pszPath, pszMode), FILE *, NULL);
+
+#undef chmod
+WRAP_FN(chmod, (const char *pszPath, mode_t fMode), (pszPath, fMode), int, -1);
+#undef lchmod
+WRAP_FN(lchmod, (const char *pszPath, mode_t fMode), (pszPath, fMode), int, -1);
+
+#undef chown
+WRAP_FN(chown, (const char *pszPath, uid_t uid, gid_t gid), (pszPath, uid, gid), int, -1);
+#undef lchown
+WRAP_FN(lchown, (const char *pszPath, uid_t uid, gid_t gid), (pszPath, uid, gid), int, -1);
+
+#undef utime
+WRAP_FN(utime, (const char *pszPath, const struct utimbuf *pTimes), (pszPath, pTimes), int, -1);
+
+#undef utimes
+WRAP_FN(utimes, (const char *pszPath, const struct timeval *paTimes), (pszPath, paTimes), int, -1);
+
+#undef pathconf
+WRAP_FN(pathconf, (const char *pszPath, int iCfgNm), (pszPath, iCfgNm), long, -1);
+
+#undef readlink
+WRAP_FN(readlink, (const char *pszPath, char *pszBuf, size_t cbBuf), (pszPath, pszBuf, cbBuf), ssize_t, -1);
+