#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 \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; }