/* $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 * * 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 #ifdef KBUILD_OS_SOLARIS # include /* Try drag in feature_tests.h. */ # include # undef _RESTRICT_KYWD # define _RESTRICT_KYWD # undef __PRAGMA_REDEFINE_EXTNAME #endif #include #include #include #include #include #include #include #include /******************************************************************************* * 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);