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
This commit is contained in:
Wouter Wijngaards 2007-02-23 13:38:54 +00:00
parent c4d87f4f5d
commit 7e4700ec5f
15 changed files with 231 additions and 31 deletions

View file

@ -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);
}

View file

@ -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.

View file

@ -44,6 +44,7 @@
#include "util/log.h"
#include "daemon/daemon.h"
#include "util/config_file.h"
#include <signal.h>
#include <fcntl.h>
#include <pwd.h>
@ -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) {

View file

@ -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.

View file

@ -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"

View file

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

View file

@ -79,9 +79,19 @@ If given a chroot is done to the given directory. The default is none ("").
.It \fBusername:\fR <name>
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 <directory>
Sets the working directory for the program.
.It \fBlogfile:\fR <filename>
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 <filename>
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

View file

@ -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;

View file

@ -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");

View file

@ -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; i<cfg->num_ifs; i++)

View file

@ -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;

View file

@ -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 */

View file

@ -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 */

View file

@ -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.

View file

@ -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.