/*
** $Id: utils.c 2698 2008-08-27 14:03:38Z andre.dig $
** watchcatd - Watchcat Daemon
** See copyright notice in distro's COPYRIGHT file
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <sched.h>
#include <syslog.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include "utils.h"

extern char **environ;

/* You don't have to call _myassert directly, use the myassert macro.
 * The return mean nothing, it only prevents warnings.
 * The library client musts define mysyslog and mysyslog can't call myassert
 * or myassert_sys!
*/
int _myassert(char *exp, char *filename, int line, const char *func)
{
    mysyslog(LOG_ALERT, "FATAL ERROR, source_filename=`%s', "
             "line=%i, function=%s assertion=`%s'", filename, line, func, exp);
    exit(1);
    return 0;
}

/* You don't have to call _myassert_sys directly, use the myassert_sys macro.
 * The return mean nothing, it only prevents warnings.
 * The library client musts define mysyslog and mysyslog can't call myassert
 * or myassert_sys!
 */
int _myassert_sys(char *exp, char *filename, int line, const char *func)
{
    int e = errno;
    mysyslog(LOG_ALERT, "FATAL ERROR, source_filename=`%s', "
             "line=%i, function=%s assertion=`%s' errno=%i, strerror=`%s'",
             filename, line, func, exp, e, strerror(e));
    exit(e);
    return 0;
}

int is_fd_open(int fd)
{
    struct stat dummy;
    return fstat(fd, &dummy) == 0; 
}

struct env_list {
    char *name;
    char *val;
};

void restrict_env(void)
{
    int i;
    char *envp;
    char *allowed_env[] = { "TZ" };
#define NALLOWED (sizeof(allowed_env)/sizeof(char *))

    struct env_list newenv[NALLOWED];

    for (i = 0; i < NALLOWED; i++) {
        envp = getenv(allowed_env[i]);
        newenv[i].name = (envp ? strdup(allowed_env[i]) : NULL);
        newenv[i].val  = (envp ? strdup(envp) : NULL);
    }

    environ = NULL;
    for (i = 0; i < NALLOWED; i++)
        if (newenv[i].name != NULL)
            setenv(newenv[i].name, newenv[i].val, 1);

    setenv("PATH", "/bin/:/usr/bin/", 1);

    free(envp);
    envp = NULL;
}

/* These are all protections inherited across a fork and shared in watchcat's
 * process group.
 * The protect_process_group function must be called when watchcatd master
 * is starting.
 */
void protect_process_group(int nodaemon, int priority)
{
    struct sched_param sp;

    signal(SIGPIPE, SIG_IGN);

    sp.sched_priority = priority;
    myassert_sys(sched_setscheduler(0, SCHED_RR, &sp) == 0);

    restrict_env();

    if (nodaemon) {
        myassert_sys(chdir("/") == 0);
        myassert_sys(setpgid(0, 0) == 0);
        return;
    }
    myassert_sys(daemon(0, 0) == 0);
}

void drop_privileges(char *user)
{
    struct passwd *pw;
    myassert_sys(pw = getpwnam(user));

    /* Chroot */
    myassert_sys(chroot(pw->pw_dir) == 0);
    myassert_sys(chdir("/") == 0);

    /* Drop privileges */
    myassert_sys(initgroups(user, pw->pw_gid) == 0);
    myassert_sys(setgid(pw->pw_gid) == 0);
    myassert_sys(setuid(pw->pw_uid) == 0);
    myassert(seteuid(0) == -1 && setegid(0) == -1);
}

/* These are all protections doesn't inherited across a fork.
 * The protect_daemon function must be called when daemon is starting.
 */
void protect_daemon(int realtime)
{
    if (realtime)
        myassert_sys(mlockall(MCL_CURRENT | MCL_FUTURE) == 0);
}
