/*
 * sb-exportfs version 0.1
 * 
 * Copyright (c) 2003, 2004 Nokia
 *
 * sb-exportfs is licensed under GPL.
 * You can read the full license at http://www.gnu.org/licenses/gpl.txt
 * 
 */

#define _GNU_SOURCE

#include "types.h"
#include "common.h"
#include "config.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pwd.h>


#define SB_USERS_DIR    "/scratchbox/users"
#define SB_HOME_DIR     "/scratchbox/users/%s/home"
#define SB_TARGET_DIR   "/scratchbox/users/%s/targets/%s"
#define SB_TARGETS_DIR  "/scratchbox/users/%s/targets"
#define MOUNT_OPTS      "rw,all_squash,anonuid=%i,anongid=%i"


/*
 * We only allow export that are SB_HOME_DIR, SB_TARGET_DIR or SB_TARGETS_DIR.
 * In the last two we also check that those are not links to anywhere but
 * they are really directories.
 */
static int check_path(const char *path, const char *user, const char *target)
{
	char *homedir;
	char *targetdir;
	char *targetsdir;
	char *mypath;
	char *tmp;
	struct stat filestat;

	homedir = alloca(strlen(SB_HOME_DIR) + strlen(user) + 2);
	targetdir = alloca(strlen(SB_TARGET_DIR) + strlen(user) + strlen(target) + 2);
	targetsdir = alloca(strlen(SB_TARGETS_DIR) + strlen(user) + 2);

	sprintf(homedir, SB_HOME_DIR, user);
	sprintf(targetdir, SB_TARGET_DIR, user, target);
	sprintf(targetsdir, SB_TARGETS_DIR, user);

	mypath = strdupa(path);
	while (mypath[strlen(mypath)] == '/')
		mypath[strlen(mypath)] = '\0';

	/* if sb is istalled correctly then this should always be directory */
	if (strcmp(mypath, homedir) == 0)
		return 0;

	/* we want to test this a bit */
	if (strcmp(mypath, targetdir) == 0) {
		stat(mypath, &filestat);
		if (!S_ISDIR(filestat.st_mode))
			goto _not_dir;

		tmp = strrchr(mypath, '/');
		if (tmp == NULL) {
			fprintf(stderr, "Error: Bad path: %s\n", path);
			return -1337;
		}
		*tmp = '\0';

		stat(tmp, &filestat);
		if (!S_ISDIR(filestat.st_mode))
			goto _not_dir;

		return 0;
	}

	if (strcmp(mypath, targetsdir) == 0) {
		stat(mypath, &filestat);
		if (!S_ISDIR(filestat.st_mode))
			goto _not_dir;

		return 0;
	}

	fprintf(stderr, "Error: Not allowed to export %s\n", path);
	printf("homedir: %s\n", homedir);
	printf("targetdir: %s\n", targetdir);
	printf("targetsdir: %s\n", targetsdir);
	return -2;

_not_dir:
	fprintf(stderr, "Error: Not allowed to export %s (not a directory)\n", path);
	return -1;
}

int main(int argc,
	 char **argv)
{
	DIR *dir;
	struct dirent *file;
	config_t *cfg;
	pid_t pid;
	int status;

	dir = opendir(SB_USERS_DIR);
	if (dir == NULL) {
		perror("Error: Can't open directory");
		exit(1);
	}

	pid = fork();
	if (pid == 0) {
		execlp("exportfs", "exportfs", "-r", NULL);
		perror("Error: Can't exec exportfs");
	}
	wait(&status);

	while ((file = readdir(dir)) != NULL) {
		char **targets;
		char **p, **q;
		char *sbrsh_config;
		struct passwd *pwds = NULL;

		if (strchr(file->d_name, '.') == file->d_name)
			continue;

		pwds = getpwnam(file->d_name);
		if (pwds == NULL)
			continue;

		/* check that .sbrsh file exists */
		sbrsh_config = malloc(strlen(SB_USERS_DIR) +
				      strlen("/home/") +
				      strlen(file->d_name) * 2 +
				      strlen("/.sbrsh") + 4);
		sprintf(sbrsh_config, "%s/%s/home/%s/.sbrsh", SB_USERS_DIR,
			file->d_name, file->d_name);

		targets = get_targets(sbrsh_config);
		if (!targets) {
			fprintf(stderr, "Warning: No targets found from %s\n", sbrsh_config);
			free(sbrsh_config);
			continue;
		}

		p = targets;
		while (*p != NULL) {
			cfg = config_alloc();
			config_read(cfg, sbrsh_config, *p);

			q = cfg->opts;
			while (*q != NULL) {
				if (strncmp(*q, "nfs", 3) == 0) {
					char *path, *tmp;
					char *cp, *opts;

					path = strchr(*q, ':');
					if (path == NULL) {
						fprintf(stderr, "Error: Bad config file: %s\n",
								sbrsh_config);
						++q;
						continue;
					}
					++path;

					tmp = find_space(path);
					if (tmp == NULL) {
						fprintf(stderr, "Error: Bad config file: %s\n",
								sbrsh_config);
						++q;
						continue;
					}
					*tmp++ = '\0';

					/* check the path */
					if (check_path(path, file->d_name, *p) < 0) {
						++q;
						continue;
					}

					cp = malloc(strlen(cfg->host) + strlen(path) + 4);
					sprintf(cp, "%s:/%s", cfg->host, path);

					opts = malloc(strlen(MOUNT_OPTS) + 11);
					sprintf(opts, MOUNT_OPTS, pwds->pw_uid, pwds->pw_gid);

					pid = fork();
					if (pid == 0) {
						execlp("exportfs", "exportfs", "-i", "-o", opts, cp,
						       NULL);
						perror("Error: Can't exec exportfs");
					}
					wait(&status);

					free(cp);
					free(opts);
				}

				++q;
			}

			config_free(cfg);
			++p;
		}

		free_vec((void **) targets, NULL);
		free(sbrsh_config);
	}

	closedir(dir);

	return 0;
}

