X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=bin%2Foverride.c;h=ac9e14cddd5bf98be6ad55e8726dc007a53868fd;hb=12ade3e3bc975d5426ba4af155b7372c31093b31;hp=1eabda0135b5efa9edec3a727d3f349f68993794;hpb=2eb05547c95e390ecab9f047beb7c4098f656ff2;p=vlc diff --git a/bin/override.c b/bin/override.c index 1eabda0135..ac9e14cddd 100644 --- a/bin/override.c +++ b/bin/override.c @@ -23,42 +23,71 @@ #endif #include +#define MAX_ERRORS 5 void vlc_enable_override (void); -static bool override = false; - -void vlc_enable_override (void) -{ - override = true; -} - -#if defined (__GNUC__) /* typeof and statement-expression */ \ +#if defined (__GNUC__) \ && (defined (__ELF__) && !defined (__sun__)) /* Solaris crashes on printf("%s", NULL); which is legal, but annoying. */ #include #include #include +#include #include #include +#ifdef HAVE_EXECINFO_H +# include +#endif +#ifdef NDEBUG +# undef HAVE_BACKTRACE +#endif + +static bool override = false; + +static void vlc_reset_override (void) +{ + override = false; +} + +void vlc_enable_override (void) +{ + override = true; + pthread_atfork (NULL, NULL, vlc_reset_override); +} -static void vlogbug (const char *level, const char *func, const char *fmt, - va_list ap) +static void vlogbug (unsigned *pc, const char *level, const char *func, + const char *fmt, va_list ap) { +#ifdef HAVE_BACKTRACE + const size_t framec = 5; + void *framev[framec]; + + backtrace (framev, framec); +#endif flockfile (stderr); - fprintf (stderr, "%s: call to %s(", level, func); - vfprintf (stderr, fmt, ap); - fputs (")\n", stderr); + if (*pc < MAX_ERRORS) + { + (*pc)++; + fprintf (stderr, "%s: call to %s(", level, func); + vfprintf (stderr, fmt, ap); + fputs (")\n", stderr); + fflush (stderr); +#ifdef HAVE_BACKTRACE + backtrace_symbols_fd (framev + 2, framec - 2, fileno (stderr)); +#endif + } funlockfile (stderr); } -static void logbug (const char *level, const char *func, const char *fmt, ...) +static void logbug (unsigned *pc, const char *level, const char *func, + const char *fmt, ...) { va_list ap; va_start (ap, fmt); - vlogbug (level, func, fmt, ap); + vlogbug (pc, level, func, fmt, ap); va_end (ap); } @@ -67,15 +96,36 @@ static void *getsym (const char *name) void *sym = dlsym (RTLD_NEXT, name); if (sym == NULL) { - fprintf (stderr, "Cannot resolve symbol %s!\n", name); + fprintf (stderr, "Cannot resolve symbol %s: %s\n", name, + dlerror ()); abort (); } return sym; } -#define LOG(level, ...) logbug(level, __func__, __VA_ARGS__) +#define LOG(level, ...) \ + do { \ + static unsigned counter = 0; \ + logbug(&counter, level, __func__, __VA_ARGS__); \ + } while (0) + +/* Evil non-standard GNU C macro ;) + * typeof keyword, + * statement-expression, + * nested function... + */ #define CALL(func, ...) \ - ({ typeof (func) *sym = getsym ( # func); sym (__VA_ARGS__); }) +({ \ + static typeof (func) *sym = NULL; \ + static pthread_once_t once = PTHREAD_ONCE_INIT; \ + auto void getsym_once (void); \ + void getsym_once (void) \ + { \ + sym = getsym ( # func); \ + } \ + pthread_once (&once, getsym_once); \ + sym (__VA_ARGS__); \ +}) /*** Environment *** @@ -125,52 +175,158 @@ int unsetenv (const char *name) * preserve reproducibility of the number sequence (which usually does not * matter). **/ -static pthread_mutex_t prng_lock = PTHREAD_MUTEX_INITIALIZER; +static struct +{ + pthread_mutex_t lock; + unsigned int seed; +} prng = { PTHREAD_MUTEX_INITIALIZER, 0, }; void srand (unsigned int seed) { - pthread_mutex_lock (&prng_lock); + pthread_mutex_lock (&prng.lock); LOG("Warning", "%d", seed); - CALL(srand, seed); - pthread_mutex_unlock (&prng_lock); + prng.seed = seed; + pthread_mutex_unlock (&prng.lock); } int rand (void) { int ret; - pthread_mutex_lock (&prng_lock); + pthread_mutex_lock (&prng.lock); LOG("Warning", ""); - ret = CALL(rand); - pthread_mutex_unlock (&prng_lock); + ret = rand_r (&prng.seed); + pthread_mutex_unlock (&prng.lock); return ret; } +#if 0 /** Signals **/ #include +static bool blocked_signal (int num) +{ + switch (num) + { + case SIGINT: + case SIGHUP: + case SIGQUIT: + case SIGTERM: + case SIGPIPE: + case SIGCHLD: + return true; + } + return false; +} + void (*signal (int signum, void (*handler) (int))) (int) { if (override) { - const char *msg = "Error"; - - if ((signum == SIGPIPE && handler == SIG_IGN) - || (signum != SIGPIPE && handler == SIG_DFL)) - /* Same settings we already use */ - msg = "Warning"; - LOG(msg, "%d, %p", signum, handler); + if (handler != SIG_IGN && handler != SIG_DFL) + goto error; + if (!blocked_signal (signum)) + goto error; + /* For our blocked signals, the handler won't matter much... */ + if (handler == SIG_DFL) + LOG("Warning", "%d, SIG_DFL", signum, handler); } return CALL(signal, signum, handler); +error: + LOG("Blocked", "%d, %p", signum, handler); + return SIG_DFL; } int sigaction (int signum, const struct sigaction *act, struct sigaction *old) { - if (act != NULL) - LOG("Error", "%d, %p, %p", signum, act, old); + if (override && act != NULL) + { + if ((act->sa_flags & SA_SIGINFO) + || (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL)) + goto error; + if (act->sa_handler == SIG_DFL) + LOG("Warning", "%d, %p, SIG_DFL", signum, act); + } return CALL(sigaction, signum, act, old); +error: + LOG("Blocked", "%d, %p, %p", signum, act, old); + return -1; } +#endif + + +/*** Locales *** + * setlocale() is not thread-safe and has a tendency to crash other threads as + * quite many libc and libintl calls depend on the locale. + * Use uselocale() instead for thread-safety. + */ +#include + +char *setlocale (int cat, const char *locale) +{ + if (override && locale != NULL) + { + LOG("Blocked", "%d, \"%s\"", cat, locale); + return NULL; + } + return CALL(setlocale, cat, locale); +} + + +/* strerror() is not thread-safe in theory (POSIX), nor in practice (glibc). + * This caused quite nasty crashes in the history of VLC/Linux. */ +char *strerror (int val) +{ + if (override) + { + static const char msg[] = + "Error message unavailable (use strerror_r instead of strerror)!"; + LOG("Blocked", "%d", val); + return (char *)msg; + } + return CALL(strerror, val); +} + +/*** Xlib ****/ +#ifdef HAVE_X11_XLIB_H +# include + +static pthread_mutex_t xlib_lock = PTHREAD_MUTEX_INITIALIZER; +int (*XSetErrorHandler (int (*handler) (Display *, XErrorEvent *))) + (Display *, XErrorEvent *) +{ + if (override) + { + int (*ret) (Display *, XErrorEvent *); -#endif /* __ELF__ */ + pthread_mutex_lock (&xlib_lock); + LOG("Error", "%p", handler); + ret = CALL(XSetErrorHandler, handler); + pthread_mutex_unlock (&xlib_lock); + return ret; + } + return CALL(XSetErrorHandler, handler); +} + +int (*XSetIOErrorHandler (int (*handler) (Display *))) (Display *) +{ + if (override) + { + int (*ret) (Display *); + + pthread_mutex_lock (&xlib_lock); + LOG("Error", "%p", handler); + ret = CALL(XSetIOErrorHandler, handler); + pthread_mutex_unlock (&xlib_lock); + return ret; + } + return CALL(XSetIOErrorHandler, handler); +} +#endif +#else +void vlc_enable_override (void) +{ +} +#endif