#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>