diff --git a/src/main.c b/src/main.c index 2fd02c1..0ac06a2 100644 --- a/src/main.c +++ b/src/main.c @@ -7,6 +7,23 @@ #include #include +#include +#include + +struct { + bool daemon; + uid_t uid; + gid_t gid; + char config_path[PATH_MAX]; + char logfile_path[PATH_MAX]; + char pidfile_path[PATH_MAX]; +} opts = { + true, + 0, 0, + "/etc/f2b/f2b.conf", + "/var/log/f2b.log", + "/var/run/f2b.pid", +}; bool run = true; bool rcfg = false; @@ -31,23 +48,76 @@ void sa_hup(int signum) { } void usage(int exitcode) { - fprintf(stderr, "Usage: f2b -c \n"); + fprintf(stderr, "Usage: f2b [-c ] [-d] [-h]\n"); exit(exitcode); } +void +update_opts_from_config(f2b_config_section_t *section) { + f2b_config_param_t *pa, *pb; + + if (!section) + return; + + /* set uid & gid. note: set only once if root */ + if (opts.uid == 0 && (pa = f2b_config_param_find(section->param, "user")) != NULL) { + struct passwd *pw; + if ((pw = getpwnam(pa->value)) != NULL) + opts.uid = pw->pw_uid, opts.gid = pw->pw_gid; + } + if (opts.gid == 0 && (pa = f2b_config_param_find(section->param, "group")) != NULL) { + struct group *grp; + if ((grp = getgrnam(pa->value)) != NULL) + opts.gid = grp->gr_gid; + } + + if (opts.daemon == false && (pa = f2b_config_param_find(section->param, "daemon")) != NULL) { + if (strcmp(pa->value, "yes") == 0) + opts.daemon = true; + } + + /* setup logging */ + if ((pa = f2b_config_param_find(section->param, "loglevel")) != NULL) + f2b_log_set_level(pa->value); + + pa = f2b_config_param_find(section->param, "logdest"); + pb = f2b_config_param_find(section->param, "logfile"); + if (pa) { + if (!opts.daemon && strcmp(pa->value, "stderr") == 0) { + f2b_log_to_stderr(); + } else if (strcmp(pa->value, "file") == 0) { + if (pb && *pb->value != '\0') { + size_t len = sizeof(opts.logfile_path); + strncpy(opts.logfile_path, pb->value, len - 1); + opts.logfile_path[len - 1] = '\0'; + f2b_log_to_file(opts.logfile_path); + } else { + f2b_log_msg(log_warn, "you must set 'logfile' option with 'logdest = file'"); + f2b_log_to_syslog(); + } + } else { + f2b_log_to_syslog(); + } + } + + /* TODO: */ +} + int main(int argc, char *argv[]) { struct sigaction act; f2b_config_t config; f2b_config_section_t *section = NULL; f2b_jail_t *jails = NULL; f2b_jail_t *jail = NULL; - char *config_file = NULL; char opt = '\0'; - while ((opt = getopt(argc, argv, "c:h")) != -1) { + while ((opt = getopt(argc, argv, "c:dh")) != -1) { switch (opt) { case 'c': - config_file = optarg; + strncpy(opts.config_path, optarg, sizeof(opts.config_path)); + break; + case 'd': + opts.daemon = true; break; case 'h': usage(EXIT_SUCCESS); @@ -61,13 +131,41 @@ int main(int argc, char *argv[]) { SA_REGISTER(SIGTERM, &sa_term); SA_REGISTER(SIGHUP, &sa_hup); - if (!config_file) + if (opts.config_path[0] == '\0') usage(EXIT_FAILURE); memset(&config, 0x0, sizeof(config)); - if (f2b_config_load(&config, config_file, true) != true) { - f2b_log_msg(log_error, "can't load config from '%s'", config_file); + if (f2b_config_load(&config, opts.config_path, true) != true) { + f2b_log_msg(log_error, "can't load config from '%s'", opts.config_path); return EXIT_FAILURE; } + update_opts_from_config(config.main); + + if (opts.daemon) { + pid_t pid = fork(); + if (pid > 0) + exit(EXIT_SUCCESS); + /* parent */ + if (pid < 0) { + /* parent */ + perror("child: fork() failed"); + exit(EXIT_FAILURE); + } + /* child */ + setsid(); + if (getuid() == 0 && + (setuid(opts.uid) != 0 || + setgid(opts.gid) != 0)) { + perror("child: setuid()/setgid() failed"); + exit(EXIT_FAILURE); + } + if (chdir("/") != 0 || + freopen("/dev/null", "r", stdin) == NULL || + freopen("/dev/null", "w", stdout) == NULL || + freopen("/dev/null", "w", stderr) == NULL) { + perror("child: freopen() failed"); + exit(EXIT_FAILURE); + } + } if (config.defaults) f2b_jail_set_defaults(config.defaults);