/*
**  sweep.c -- Sophos Anti-Virus binary RTLD wrapper
*/

#define CONFIG_OLD "/etc/sav.conf"
#define CONFIG_NEW "@l_prefix@/etc/sav/sav.conf"

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>

#if defined(PATH_MAX)
#define PATH_ADJUST_MAXLEN PATH_MAX
#elif defined(MAXPATHLEN)
#define PATH_ADJUST_MAXLEN MAXPATHLEN
#else
#define PATH_ADJUST_MAXLEN 1024
#endif

static void path_adjust(char *buf, size_t buflen, const char *path)
{
    size_t pathlen;

    /* special case first */
    if (path == NULL) {
        buf[0] = '\0';
        return;
    }

    /* take over path into buffer */
    pathlen = strlen(path);
    if (pathlen > buflen)
        pathlen = buflen;
    strncpy(buf, path, pathlen);
    buf[pathlen] = '\0';

    /* apply path adjustments */
    if (strcmp(buf, CONFIG_OLD) == 0)
        strcpy(buf, CONFIG_NEW);

    return;
}

#if OPENPKG_FREEBSD

#include <dlfcn.h>
#include <fcntl.h>

int open(const char *path, int flags, ...)
{
    static int (*func)(const char *, int, ...) = NULL;
    char path_adjusted[PATH_ADJUST_MAXLEN];
    int rv;
    int mode;
    va_list ap;

    /* initially resolve original open(2) function */
    if (func == NULL)
        func = dlsym(RTLD_NEXT, "open");

    /* adjust path */
    path_adjust(path_adjusted, sizeof(path_adjusted), path);

    /* execute original function */
    if (flags & O_CREAT) {
        va_start(ap, flags);
        mode = (int)va_arg(ap, int);
        rv = func(path_adjusted, flags, mode);
        va_end(ap);
    }
    else
        rv = func(path_adjusted, flags);

    /* pass-through return value */
    return rv;
}

/*  -----------------------------------------------------------------  */

#elif OPENPKG_LINUX

#include <features.h>
#define __USE_GNU
#include <dlfcn.h>
#undef __USE_GNU
#include <fcntl.h>

static int
myopen(
    const char *func_name,
    int (**func_ptr)(const char *, int, ...),
    char *buf,
    size_t buflen,
    const char *path,
    int flags,
    va_list ap)
{
    int rv;
    int mode;

    /* initially resolve original open(2) like function */
    if (*func_ptr == NULL) {
        if ((*func_ptr = dlsym(RTLD_NEXT, func_name)) == NULL) {
            fprintf(stderr, "sweep.so: unable to resolve function \"%s\"\n", func_name);
            abort();
        }
    }

    /* adjust path */
    path_adjust(buf, buflen, path);

    /* execute original function */
    if (flags & O_CREAT) {
        mode = (int)va_arg(ap, int);
        rv = (*func_ptr)(buf, flags, mode);
    }
    else
        rv = (*func_ptr)(buf, flags);

    /* pass-through return value */
    return rv;
}

#define genstub(name) \
int name(const char *path, int flags, ...) \
{ \
    static int (*func)(const char *, int, ...) = NULL; \
    char path_adjusted[PATH_ADJUST_MAXLEN]; \
    va_list ap; \
    int rv; \
\
    va_start(ap, flags); \
    rv = myopen(#name, &func, path_adjusted, sizeof(path_adjusted), path, flags, ap); \
    va_end(ap); \
    return rv; \
}

genstub(open)
genstub(open64)
genstub(__open)
genstub(__open64)
genstub(__libc_open)
genstub(__libc_open64)

#elif OPENPKG_SOLARIS

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>

int open(const char *path, int flags, ...)
{
    static int (*func)(const char *, int, ...) = NULL;
    char path_adjusted[PATH_ADJUST_MAXLEN];
    int rv;
    int mode;
    va_list ap;

    /* initially resolve original open(2) function */
    if (func == NULL)
        func = dlsym(RTLD_NEXT, "open");

    /* adjust path */
    path_adjust(path_adjusted, sizeof(path_adjusted), path);

    /* execute original function */
    if (flags & O_CREAT) {
        va_start(ap, flags);
        mode = (int)va_arg(ap, int);
        rv = func(path_adjusted, flags, mode);
        va_end(ap);
    }
    else
        rv = func(path_adjusted, flags);

    /* pass-through return value */
    return rv;
}

#endif

