File:  [ELWIX - Embedded LightWeight unIX -] / tftpd / src / tftpd.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Fri Feb 14 15:38:37 2014 UTC (10 years, 3 months ago) by misho
Branches: MAIN
CVS tags: HEAD
Initial revision

#include "global.h"
#include "srv.h"


intptr_t Kill;
struct tagCli cli;
cfg_root_t cfg;
sched_root_task_t *root;
char szCfgName[PATH_MAX] = DEFAULT_CFGNAME;
struct timespec timeout = { DEFAULT_TIMEOUT, 0 };
extern char compiled[], compiledby[], compilehost[];

const struct tagErr errs[9] = {
	{ 0, "Not defined" }, 
	{ 1, "File not found" }, 
	{ 2, "Access violation" }, 
	{ 3, "Disk full or allocation exceeded" }, 
	{ 4, "Illegal TFTP operation" }, 
	{ 5, "Unknown transfer ID" }, 
	{ 6, "File already exists" }, 
	{ 7, "No such user" }, 
	{ 8, "Option not supported" }, 
};

static void
Usage()
{
	printf(	" -= TFTPd =- ELWIX extended TFTP service\n"
		"=== %s === %s@%s ===\n\n"
		" Syntax: TFTPd [options]\n\n"
		"\t-c <config>\tConfig file [default=/etc/tftpd.conf]\n"
		"\t-w\t\tSwitch to read-write mode [default=read-only]\n"
		"\t-b\t\tRun into batch mode (default is daemon mode)\n"
		"\t-v\t\tVerbose (more -v, more verbosity ...)\n"
		"\t-h\t\tThis help screen!\n"
		"\n", compiled, compiledby, compilehost);
}

static void *
sigHandler(sched_task_t *task)
{
	int stat;
	const char *str;

	switch (TASK_VAL(task)) {
		case SIGHUP:
			cfgUnloadConfig(&cfg);
			if (cfgLoadConfig(szCfgName, &cfg)) {
				ELIBERR(cfg);
				Kill++;
			} else
				EVERBOSE(1, "Reloading config ...");

			str = cfg_getAttribute(&cfg, "tftpd", "timeout");
			if (str)
				timeout.tv_sec = strtol(str, NULL, 10);
			break;
		case SIGINT:
		case SIGTERM:
			EVERBOSE(1, "Terminate service ...");
			Kill++;
			break;
		case SIGCHLD:
			while (waitpid(-1, &stat, WNOHANG) > 0);
			break;
		default:
			EERROR(EINVAL, "Unknown signal #%lu?", TASK_VAL(task));
			break;
	}

	schedSignalSelf(task);
	taskExit(task, NULL);
}

int
main(int argc, char **argv)
{
	char ch, m = 0, b = 0;
	const char *str;
	int fd, uid = 0, ret = 0;
	struct passwd *pass;
	pid_t pid;
	sockaddr_t sa;
	rpack_t *pkt = NULL;

	while ((ch = getopt(argc, argv, "hvbwc:")) != -1)
		switch (ch) {
			case 'c':
				strlcpy(szCfgName, optarg, sizeof szCfgName);
				break;
			case 'w':
				m = 42;	/* rw mode */
				break;
			case 'v':
				e_incVerbose;
				break;
			case 'b':
				b = 42;
				break;
			case 'h':
			default:
				Usage();
				return 1;
		}
	argc -= optind;
	argv += optind;

	openlog("TFTPd", LOG_PID | LOG_CONS, LOG_FTP);
	if (cfgLoadConfig(szCfgName, &cfg)) {
		ELIBERR(cfg);
		return 2;
	}

	str = cfg_getAttribute(&cfg, "tftpd", "user");
	if (str) {
		pass = getpwnam(str);
		if (!pass) {
			EERROR(EINVAL, "User %s not found!\n", str);
			ret = 2;
			goto end;
		} else
			uid = pass->pw_uid;
		endpwent();
	}

	if (!b)	/* service mode */
		switch ((pid = fork())) {
			case -1:
				ret = 3;
				goto end;
			case 0:
				setsid();
				chdir("/");

				fd = open(_PATH_DEVNULL, O_RDWR);
				if (fd > 2) {
					dup2(fd, STDIN_FILENO);
					dup2(fd, STDOUT_FILENO);
					dup2(fd, STDERR_FILENO);
					close(fd);
				}
				EVERBOSE(1, "Welcome to the shadow-land!\n");
				break;
			default:
				EVERBOSE(1, "Starting service with pid=%d ...\n", pid);
				goto end;
		}

	root = schedBegin();
	if (!root) {
		ELIBERR(sched);
		ret = 3;
		goto end;
	}

	str = cfg_getAttribute(&cfg, "tftpd", "timeout");
	if (str)
		timeout.tv_sec = strtol(str, NULL, 10);

	str = cfg_getAttribute(&cfg, "tftpd", "port");
	if (str)
		fd = strtol(str, NULL, 10);
	else
		fd = 69;
	str = cfg_getAttribute(&cfg, "tftpd", "bind");
	e_gethostbyname(str ? str : "0.0.0.0", fd, &sa);
	fd = socket(sa.sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
	if (fd == -1) {
		ESYSERR(0);
		ret = 4;
		goto end;
	}
	if (bind(fd, &sa.sa, sa.sa.sa_len) == -1) {
		ESYSERR(0);
		close(fd);
		ret = 4;
		goto end;
	}

	str = cfg_getAttribute(&cfg, "tftpd", "chroot");
	if (str && chroot(str) == -1) {
		ESYSERR(0);
		close(fd);
		ret = 3;
		goto end;
	} else {
		setuid(uid);
		str = cfg_getAttribute(&cfg, "tftpd", "dir");
		if (str)
			chdir(str);
	}

	if (!(pkt = rpack_create(NULL, 0))) {
		ELIBERR(elwix);
		close(fd);
		ret = 5;
		goto end;
	}
	if (rpack_attach(pkt, TFTP_PKT_MAX)) {
		ELIBERR(elwix);
		close(fd);
		ret = 5;
		goto end;
	}

	memset(&cli, 0, sizeof cli);
	schedSignal(root, sigHandler, NULL, SIGHUP, NULL, 0);
	schedSignal(root, sigHandler, NULL, SIGTERM, NULL, 0);
	schedSignal(root, sigHandler, NULL, SIGINT, NULL, 0);
	schedSignal(root, sigHandler, NULL, SIGCHLD, NULL, 0);
	schedRead(root, rxPkt, NULL, fd, pkt, sizeof(rpack_t));
	schedRun(root, &Kill);

	close(fd);
end:
	schedEnd(&root);
	rpack_detach(pkt);
	rpack_destroy(&pkt);
	cfgUnloadConfig(&cfg);
	closelog();
	return ret;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>