From: Wouter Wijngaards Date: Fri, 23 Feb 2007 13:38:54 +0000 (+0000) Subject: config file: directory, logfile, pidfile. And they work too. X-Git-Tag: release-0.1~32 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7e4700ec5f73e7fee8c2aa827b7518cbaad27b7d;p=thirdparty%2Funbound.git config file: directory, logfile, pidfile. And they work too. and log_warn. git-svn-id: file:///svn/unbound/trunk@139 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/daemon/daemon.c b/daemon/daemon.c index 39c3d1c82..e309ad9ee 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -62,10 +62,9 @@ daemon_init() } int -daemon_open_shared_ports(struct daemon* daemon, struct config_file* cfg) +daemon_open_shared_ports(struct daemon* daemon) { log_assert(daemon); - daemon->cfg = cfg; if(daemon->cfg->port == daemon->listening_port) return 1; listening_ports_free(daemon->ports); @@ -116,5 +115,7 @@ daemon_delete(struct daemon* daemon) return; listening_ports_free(daemon->ports); lock_basic_destroy(&daemon->lock); + free(daemon->cwd); + free(daemon->pidfile); free(daemon); } diff --git a/daemon/daemon.h b/daemon/daemon.h index 256c66893..0fb185d70 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -56,6 +56,10 @@ struct daemon { lock_basic_t lock; /** The config settings */ struct config_file* cfg; + /** current working directory */ + char* cwd; + /** pidfile that is used */ + char* pidfile; /** port number that has ports opened. */ int listening_port; /** listening ports, opened, to be shared by threads */ @@ -76,11 +80,11 @@ struct daemon* daemon_init(); /** * Open shared listening ports (if needed). + * The cfg member pointer must have been set for the daemon. * @param daemon: the daemon. - * @param cfg: the cfg settings. Applied to daemon. * @return: false on error. */ -int daemon_open_shared_ports(struct daemon* daemon, struct config_file* cfg); +int daemon_open_shared_ports(struct daemon* daemon); /** * Fork workers and start service. diff --git a/daemon/unbound.c b/daemon/unbound.c index b3d4fab11..a8769ae6b 100644 --- a/daemon/unbound.c +++ b/daemon/unbound.c @@ -44,6 +44,7 @@ #include "util/log.h" #include "daemon/daemon.h" #include "util/config_file.h" +#include #include #include @@ -60,9 +61,100 @@ static void usage() printf("Report bugs to %s\n", PACKAGE_BUGREPORT); } +/** to changedir, logfile */ +static void +apply_dir(struct daemon* daemon, struct config_file* cfg, int cmdline_verbose) +{ + /* apply changes if they have changed */ + daemon->cfg = cfg; + verbosity = cmdline_verbose + cfg->verbosity; + if(cfg->directory && cfg->directory[0]) { + if(!daemon->cwd || strcmp(daemon->cwd, cfg->directory) != 0) { + if(chdir(cfg->directory)) { + log_err("Could not chdir to %s: %s", + cfg->directory, strerror(errno)); + } + free(daemon->cwd); + if(!(daemon->cwd = strdup(cfg->directory))) + fatal_exit("malloc failed"); + } + } +} + +/** Read existing pid from pidfile. */ +static pid_t +readpid (const char* file) +{ + int fd; + pid_t pid; + char pidbuf[32]; + char* t; + int l; + + if ((fd = open(file, O_RDONLY)) == -1) { + return -1; + } + + if (((l = read(fd, pidbuf, sizeof(pidbuf)))) == -1) { + close(fd); + return -1; + } + + close(fd); + + /* Empty pidfile means no pidfile... */ + if (l == 0) { + errno = ENOENT; + return -1; + } + + pid = strtol(pidbuf, &t, 10); + + if (*t && *t != '\n') { + return -1; + } + return pid; +} + +/** write pid to file. */ +static void +writepid (const char* pidfile, pid_t pid) +{ + FILE* f; + + if ((f = fopen(pidfile, "w")) == NULL ) { + log_err("cannot open pidfile %s: %s", + pidfile, strerror(errno)); + return; + } + if(fprintf(f, "%lu\n", (unsigned long)pid) < 0) { + log_err("cannot write to pidfile %s: %s", + pidfile, strerror(errno)); + } + fclose(f); +} + +/** check old pid file */ +static void +checkoldpid(struct config_file* cfg) +{ + pid_t old; + if((old = readpid(cfg->pidfile)) == -1) { + if(errno != ENOENT) { + log_err("Could not read pidfile %s: %s", + cfg->pidfile, strerror(errno)); + } + } else { + /** see if it is still alive */ + if(kill(old, 0) == 0 || errno == EPERM) + log_warn("unbound is already running as pid %u.", old); + else log_warn("did not exit gracefully last time (%u)", old); + } +} + /** daemonize, drop user priviliges and chroot if needed */ static void -do_chroot(struct config_file* cfg) +do_chroot(struct daemon* daemon, struct config_file* cfg) { log_assert(cfg); @@ -80,6 +172,13 @@ do_chroot(struct config_file* cfg) fatal_exit("unable to set user id: %s", strerror(errno)); endpwent(); } + /* check old pid file before forking */ + if(cfg->pidfile && cfg->pidfile[0]) { + checkoldpid(cfg); + } + + /* init logfile just before fork */ + log_init(cfg->logfile); if(cfg->do_daemonize) { int fd; /* Take off... */ @@ -87,6 +186,7 @@ do_chroot(struct config_file* cfg) case 0: break; case -1: + unlink(cfg->pidfile); fatal_exit("fork failed: %s", strerror(errno)); default: /* exit interactive session */ @@ -103,6 +203,10 @@ do_chroot(struct config_file* cfg) (void)close(fd); } } + if(cfg->pidfile && cfg->pidfile[0]) { + writepid(cfg->pidfile, getpid()); + daemon->pidfile = strdup(cfg->pidfile); + } } /** @@ -129,13 +233,13 @@ static void run_daemon(const char* cfgfile, int cmdline_verbose) fatal_exit("Could not alloc config defaults"); if(!config_read(cfg, cfgfile)) fatal_exit("Could not read config file: %s", cfgfile); - verbosity = cmdline_verbose + cfg->verbosity; + apply_dir(daemon, cfg, cmdline_verbose); /* prepare */ - if(!daemon_open_shared_ports(daemon, cfg)) + if(!daemon_open_shared_ports(daemon)) fatal_exit("could not open ports"); if(!done_chroot) { - do_chroot(cfg); + do_chroot(daemon, cfg); done_chroot = 1; } /* work */ @@ -147,6 +251,8 @@ static void run_daemon(const char* cfgfile, int cmdline_verbose) config_delete(cfg); } verbose(VERB_ALGO, "Exit cleanup."); + if(daemon->pidfile) + unlink(daemon->pidfile); daemon_delete(daemon); } @@ -168,7 +274,7 @@ main(int argc, char* argv[]) const char* cfgfile = NULL; int cmdline_verbose = 0; - log_init(); + log_init(NULL); /* parse the options */ while( (c=getopt(argc, argv, "c:hv")) != -1) { switch(c) { diff --git a/doc/Changelog b/doc/Changelog index 5ee25dd2b..297f2fe5e 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -4,6 +4,7 @@ And everything is started again (and listening ports if needed). - Ports for queries are shared. - config file added interface:, chroot: and username:. + - config file: directory, logfile, pidfile. And they work too. 22 February 2007: Wouter - Have a config file. Removed commandline options, moved to config. diff --git a/doc/example.conf b/doc/example.conf index c134538f9..e52f37ad6 100644 --- a/doc/example.conf +++ b/doc/example.conf @@ -57,8 +57,19 @@ server: # forward-to-port: 53 # if given, a chroot(2) is done to the given directory. + # i.e. you can chroot to the working directory, for example, + # for extra security, but make sure all files are in that directory. # chroot: "/some/directory" # if given, user privileges are dropped (after binding port), # and the given username is assumed. Default is nothing "". # username: "unbound" + + # the working directory. + # directory: "/etc/unbound" + + # the log file, "" means log to stderr. + # logfile: "" + + # the pid file. + # pidfile: "unbound.pid" diff --git a/doc/unbound.8 b/doc/unbound.8 index 5b8cba8a6..7993904b7 100644 --- a/doc/unbound.8 +++ b/doc/unbound.8 @@ -66,7 +66,6 @@ This is in addition to the verbosity (if any) from the config file. .El .Sh SEE ALSO -.Xr resolv.conf 5 , .Xr unbound.conf 5 . .Sh AUTHORS diff --git a/doc/unbound.conf.5 b/doc/unbound.conf.5 index 2a5254497..00e83c495 100644 --- a/doc/unbound.conf.5 +++ b/doc/unbound.conf.5 @@ -79,9 +79,19 @@ If given a chroot is done to the given directory. The default is none (""). .It \fBusername:\fR If given, after binding the port the user privileges are dropped. Default is not to change user, username: "". If this user is not capable of binding the -port, reloads (by signal HUP) will work, however, if you change the port -number in the config file, and that port number requires privileges, then -a reload will fail to bind to the new port number; a restart is needed. +port, reloads (by signal HUP) will still retain the opened ports. +If you change the port number in the config file, and that new port number +requires privileges, then a restart is needed. +.It \fBdirectory:\fR +Sets the working directory for the program. +.It \fBlogfile:\fR +If "" is given, logging goes to stderr, or nowhere once daemonized. +The logfile is appended to, in the following format: +[seconds since 1970] unbound[pid]: type: message. +.It \fBpidfile:\fR +The process id is written to the file. Default is "unbound.pid". So, +kill -HUP `cat /etc/unbound/unbound.pid` will trigger a reload, +kill -QUIT `cat /etc/unbound/unbound.pid` will gracefully terminate. .Sh FILES .Bl -tag -width indent diff --git a/testcode/testbound.c b/testcode/testbound.c index e6dbbd5f7..09100bc44 100644 --- a/testcode/testbound.c +++ b/testcode/testbound.c @@ -151,6 +151,7 @@ main(int argc, char* argv[]) char* init_optarg = optarg; struct replay_scenario* scen = NULL; + log_init(NULL); log_info("Start of %s testbound program.", PACKAGE_STRING); /* determine commandline options for the daemon */ pass_argc = 1; diff --git a/testcode/unitmain.c b/testcode/unitmain.c index df891de7b..5dd045c87 100644 --- a/testcode/unitmain.c +++ b/testcode/unitmain.c @@ -70,6 +70,7 @@ net_test() int main(int argc, char* argv[]) { + log_init(NULL); if(argc != 1) { printf("usage: %s\n", argv[0]); printf("\tperforms unit tests.\n"); diff --git a/util/config_file.c b/util/config_file.c index 6d29761ac..3be1bad60 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -77,18 +77,12 @@ config_create() cfg->do_tcp = 1; cfg->outgoing_base_port = cfg->port + 1000; cfg->outgoing_num_ports = 16; - if(!(cfg->fwd_address = strdup(""))) { - config_delete(cfg); - return NULL; - } - if(!(cfg->username = strdup(""))) { - config_delete(cfg); - return NULL; - } - if(!(cfg->chrootdir = strdup(""))) { - config_delete(cfg); - return NULL; - } + if(!(cfg->fwd_address = strdup(""))) {config_delete(cfg); return NULL;} + if(!(cfg->username = strdup(""))) {config_delete(cfg); return NULL;} + if(!(cfg->chrootdir = strdup(""))) {config_delete(cfg); return NULL;} + if(!(cfg->directory = strdup("/etc/unbound"))) {config_delete(cfg); return NULL;} + if(!(cfg->logfile = strdup(""))) {config_delete(cfg); return NULL;} + if(!(cfg->pidfile = strdup("unbound.pid"))) {config_delete(cfg); return NULL;} cfg->fwd_port = UNBOUND_DNS_PORT; cfg->do_daemonize = 0; cfg->num_ifs = 0; @@ -140,6 +134,9 @@ config_delete(struct config_file* cfg) free(cfg->fwd_address); free(cfg->username); free(cfg->chrootdir); + free(cfg->directory); + free(cfg->logfile); + free(cfg->pidfile); if(cfg->ifs) { int i; for(i=0; inum_ifs; i++) diff --git a/util/config_file.h b/util/config_file.h index 546486638..f75568331 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -83,6 +83,12 @@ struct config_file { char* chrootdir; /** username to change to, if not "". */ char* username; + /** working directory */ + char* directory; + /** filename to log to. */ + char* logfile; + /** pidfile to write pid to. */ + char* pidfile; /** daemonize, i.e. fork into the background. */ int do_daemonize; diff --git a/util/configlexer.lex b/util/configlexer.lex index 22b9e4df9..e104a0a59 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -111,6 +111,9 @@ forward-to-port{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_FORWARD_TO_PORT; interface{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_INTERFACE;} chroot{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CHROOT;} username{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_USERNAME;} +directory{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DIRECTORY;} +logfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_LOGFILE;} +pidfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PIDFILE;} {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} /* Quoted strings. Strip leading and ending quotes */ diff --git a/util/configparser.y b/util/configparser.y index ac4c940de..6d3759750 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -70,7 +70,7 @@ extern struct config_parser_state* cfg_parser; %token VAR_OUTGOING_PORT VAR_OUTGOING_RANGE VAR_INTERFACE %token VAR_DO_IP4 VAR_DO_IP6 VAR_DO_UDP VAR_DO_TCP %token VAR_FORWARD_TO VAR_FORWARD_TO_PORT VAR_CHROOT -%token VAR_USERNAME +%token VAR_USERNAME VAR_DIRECTORY VAR_LOGFILE VAR_PIDFILE %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; @@ -90,7 +90,7 @@ content_server: server_num_threads | server_verbosity | server_port | server_outgoing_port | server_outgoing_range | server_do_ip4 | server_do_ip6 | server_do_udp | server_do_tcp | server_forward_to | server_forward_to_port | server_interface | server_chroot | - server_username; + server_username | server_directory | server_logfile | server_pidfile; server_num_threads: VAR_NUM_THREADS STRING { OUTYY(("P(server_num_threads:%s)\n", $2)); @@ -215,6 +215,27 @@ server_username: VAR_USERNAME STRING cfg_parser->cfg->username = $2; } ; +server_directory: VAR_DIRECTORY STRING + { + OUTYY(("P(server_directory:%s)\n", $2)); + free(cfg_parser->cfg->directory); + cfg_parser->cfg->directory = $2; + } + ; +server_logfile: VAR_LOGFILE STRING + { + OUTYY(("P(server_logfile:%s)\n", $2)); + free(cfg_parser->cfg->logfile); + cfg_parser->cfg->logfile = $2; + } + ; +server_pidfile: VAR_PIDFILE STRING + { + OUTYY(("P(server_pidfile:%s)\n", $2)); + free(cfg_parser->cfg->pidfile); + cfg_parser->cfg->pidfile = $2; + } + ; %% /* parse helper routines could be here */ diff --git a/util/log.c b/util/log.c index f91f9a1ce..4ca4dac97 100644 --- a/util/log.c +++ b/util/log.c @@ -44,10 +44,28 @@ #endif enum verbosity_value verbosity = 0; +static FILE* logfile = 0; void -log_init() +log_init(const char* filename) { + FILE *f; + if(!filename || !filename[0]) { + if(logfile && logfile != stderr) + fclose(logfile); + logfile = stderr; + return; + } + /* open the file for logging */ + f = fopen(filename, "a"); + if(!f) { + log_err("Could not open logfile %s: %s", filename, + strerror(errno)); + return; + } + if(logfile && logfile != stderr) + fclose(logfile); + logfile = f; } void @@ -56,9 +74,9 @@ log_vmsg(const char* type, const char *format, va_list args) char message[MAXSYSLOGMSGLEN]; const char* ident="unbound"; vsnprintf(message, sizeof(message), format, args); - fprintf(stderr, "[%d] %s[%d] %s: %s\n", + fprintf(logfile, "[%d] %s[%d] %s: %s\n", (int)time(NULL), ident, (int)getpid(), type, message); - fflush(stderr); + fflush(logfile); } /** @@ -87,6 +105,19 @@ log_err(const char *format, ...) va_end(args); } +/** + * implementation of log_warn + * @param format: format string printf-style. + */ +void +log_warn(const char *format, ...) +{ + va_list args; + va_start(args, format); + log_vmsg("warning", format, args); + va_end(args); +} + /** * implementation of fatal_exit * @param format: format string printf-style. diff --git a/util/log.h b/util/log.h index 8fe90934e..b0a5d1c45 100644 --- a/util/log.h +++ b/util/log.h @@ -76,8 +76,9 @@ void verbose(enum verbosity_value level, /** * call this to initialize logging services. + * @param filename: if NULL stderr is used. */ -void log_init(); +void log_init(const char* filename); /** * Log informational message. @@ -93,6 +94,13 @@ void log_info(const char* format, ...) ATTR_FORMAT(printf, 1, 2); */ void log_err(const char* format, ...) ATTR_FORMAT(printf, 1, 2); +/** + * Log warning message. + * Pass printf formatted arguments. No trailing newline is needed. + * @param format: printf-style format string. Arguments follow. + */ +void log_warn(const char* format, ...) ATTR_FORMAT(printf, 1, 2); + /** * Log fatal error message, and exit the current process. * Pass printf formatted arguments. No trailing newline is needed.