]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
config file: directory, logfile, pidfile. And they work too.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 23 Feb 2007 13:38:54 +0000 (13:38 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 23 Feb 2007 13:38:54 +0000 (13:38 +0000)
and log_warn.

git-svn-id: file:///svn/unbound/trunk@139 be551aaa-1e26-0410-a405-d3ace91eadb9

15 files changed:
daemon/daemon.c
daemon/daemon.h
daemon/unbound.c
doc/Changelog
doc/example.conf
doc/unbound.8
doc/unbound.conf.5
testcode/testbound.c
testcode/unitmain.c
util/config_file.c
util/config_file.h
util/configlexer.lex
util/configparser.y
util/log.c
util/log.h

index 39c3d1c82d2cd884f1936310bae154607cfcf518..e309ad9eea9644c86e47904ae04ad4fc9af2a5a6 100644 (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);
 }
index 256c668938bf968a025bb1dacb85c40a623557aa..0fb185d70543ee9d2cb4c27f9272720e05f21fff 100644 (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.
index b3d4fab1100bfb937adad73c87d7eb67ecfe356a..a8769ae6b5256db79f7ebcfbe20c85269979ad3b 100644 (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) {
index 5ee25dd2b83ad13ef599bc21e3d2abb53b3245e5..297f2fe5e211c0d7444672f10e66b6eae2fd4422 100644 (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.
index c134538f92b3fe344b6d7313b98109645315b00b..e52f37ad690d205e45250fe3ee0cd1b038b217ed 100644 (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"
index 5b8cba8a62ca83de1d070ad111cfbc98dbe287bb..7993904b71abb56d6e0fb8d30b8209330d8fb5c5 100644 (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
index 2a52544974153c2956ca31c9c7ad56721275b7c1..00e83c495182a6c1269abc2e4fd72a77cb4b7795 100644 (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
index e6dbbd5f72f230aff20e46672be40f517a276f0d..09100bc44618eb92b68814bdfb79853bb1de7979 100644 (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;
index df891de7b9e8b42f29268e088c495503b58b4935..5dd045c8779c040fb5f34e84a351f63dbb0619c1 100644 (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");
index 6d29761ac27d1dbb512769a6f41d3a0ce897a727..3be1bad601fb521e4cf571ac661b15089f59b1a6 100644 (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++)
index 54648663890ee190b70dc3f21437b87b0f4724b4..f755683318f3e8839e9d289bd33f6fa841a7e427 100644 (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;
index 22b9e4df91e0b16f80f8afc4f4fa77a7b9bb6c12..e104a0a593212f17452260911576dff4aa3e9468 100644 (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 */
index ac4c940de103769ce7cd4fbc1707f3f59df1ada5..6d375975050e71dfb4132d9c12a42783af7cde0f 100644 (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 */
index f91f9a1ce6302b07fb571380185b0dce369c0663..4ca4dac97dcb327d158cedfbfacef598b7a5eb3d 100644 (file)
 #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.
index 8fe90934e9cf60b1fc40e80065556eeac505df47..b0a5d1c45b0dce183a8624030262168d113cd315 100644 (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.