2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002 - 2013 PowerDNS.COM BV
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2
7 as published by the Free Software Foundation
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 #include "packetcache.hh"
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
34 #include <sys/types.h>
42 #include <boost/algorithm/string.hpp>
46 #include "dnsbackend.hh"
47 #include "ueberbackend.hh"
48 #include "dnspacket.hh"
49 #include "nameserver.hh"
50 #include "distributor.hh"
52 #include "arguments.hh"
53 #include "packethandler.hh"
55 #include "tcpreceiver.hh"
58 #include "dynlistener.hh"
59 #include "dynhandler.hh"
60 #include "communicator.hh"
61 #include "dnsproxy.hh"
63 #include "common_startup.hh"
64 #include "dnsrecords.hh"
70 string s_programname
="pdns"; // used in packethandler.cc
72 const char *funnytext
=
73 "*****************************************************************************\n"\
74 "Ok, you just ran pdns_server through 'strings' hoping to find funny messages.\n"\
75 "Well, you found one. \n"\
76 "Two ions are flying through their particle accelerator, says the one to the\n"
77 "other 'I think I've lost an electron!' \n"\
78 "So the other one says, 'Are you sure?'. 'YEAH! I'M POSITIVE!'\n"\
79 " the pdns crew - pdns@powerdns.com\n"
80 "*****************************************************************************\n";
86 \brief All logging is done via L, a Logger instance
92 \brief The main loop of powerdns
94 This file is where it all happens - main is here, as are the two pivotal threads qthread() and athread()
104 int i
=open("/dev/null",O_RDWR
); /* open stdin */
106 L
<<Logger::Critical
<<"Unable to open /dev/null: "<<stringerror()<<endl
;
108 dup2(i
,0); /* stdin */
109 dup2(i
,1); /* stderr */
110 dup2(i
,2); /* stderr */
116 static void takedown(int i
)
119 L
<<Logger::Error
<<"Guardian is killed, taking down children with us"<<endl
;
125 static void writePid(void)
127 string fname
=::arg()["socket-dir"]+"/"+s_programname
+".pid";
128 ofstream
of(fname
.c_str());
132 L
<<Logger::Error
<<"Requested to write pid for "<<getpid()<<" to "<<fname
<<" failed: "<<strerror(errno
)<<endl
;
135 int g_fd1
[2], g_fd2
[2];
137 pthread_mutex_t g_guardian_lock
= PTHREAD_MUTEX_INITIALIZER
;
139 // The next two methods are not in dynhandler.cc because they use a few items declared in this file.
140 static string
DLCycleHandler(const vector
<string
>&parts
, pid_t ppid
)
142 kill(cpid
, SIGKILL
); // why?
143 kill(cpid
, SIGKILL
); // why?
148 static string
DLRestHandler(const vector
<string
>&parts
, pid_t ppid
)
152 for(vector
<string
>::const_iterator i
=parts
.begin();i
!=parts
.end();++i
) {
159 Lock
l(&g_guardian_lock
);
162 writen2(g_fd1
[1],line
.c_str(),line
.size()+1);
164 catch(PDNSException
&ae
) {
165 return "Error communicating with instance: "+ae
.reason
;
169 while(fgets(mesg
,sizeof(mesg
),g_fp
)) {
174 boost::trim_right(response
);
180 static int guardian(int argc
, char **argv
)
187 DynListener
dlg(s_programname
);
188 dlg
.registerFunc("QUIT",&DLQuitHandler
, "quit daemon");
189 dlg
.registerFunc("CYCLE",&DLCycleHandler
, "restart instance");
190 dlg
.registerFunc("PING",&DLPingHandler
, "ping guardian");
191 dlg
.registerFunc("STATUS",&DLStatusHandler
, "get instance status from guardian");
192 dlg
.registerRestFunc(&DLRestHandler
);
194 string progname
=argv
[0];
199 pthread_mutex_lock(&g_guardian_lock
);
203 setStatus("Launching child");
205 if(pipe(g_fd1
)<0 || pipe(g_fd2
)<0) {
206 L
<<Logger::Critical
<<"Unable to open pipe for coprocess: "<<strerror(errno
)<<endl
;
210 if(!(g_fp
=fdopen(g_fd2
[0],"r"))) {
211 L
<<Logger::Critical
<<"Unable to associate a file pointer with pipe: "<<stringerror()<<endl
;
214 setbuf(g_fp
,0); // no buffering please, confuses select
216 if(!(pid
=fork())) { // child
217 signal(SIGTERM
, SIG_DFL
);
219 signal(SIGHUP
, SIG_DFL
);
220 signal(SIGUSR1
, SIG_DFL
);
221 signal(SIGUSR2
, SIG_DFL
);
223 char **const newargv
=new char*[argc
+2];
226 if(::arg()["config-name"]!="") {
227 progname
+="-"+::arg()["config-name"];
228 L
<<Logger::Error
<<"Virtual configuration name: "<<::arg()["config-name"]<<endl
;
231 newargv
[0]=strdup(const_cast<char *>((progname
+"-instance").c_str()));
232 for(n
=1;n
<argc
;n
++) {
237 L
<<Logger::Error
<<"Guardian is launching an instance"<<endl
;
239 fclose(g_fp
); // this closes g_fd2[0] for us
241 if(g_fd1
[0]!= infd
) {
242 dup2(g_fd1
[0], infd
);
246 if(g_fd2
[1]!= outfd
) {
247 dup2(g_fd2
[1], outfd
);
250 if(execvp(argv
[0], newargv
)<0) {
251 L
<<Logger::Error
<<"Unable to execvp '"<<argv
[0]<<"': "<<strerror(errno
)<<endl
;
254 L
<<Logger::Error
<<*p
++<<endl
;
258 L
<<Logger::Error
<<"execvp returned!!"<<endl
;
261 else if(pid
>0) { // parent
267 signal(SIGTERM
, takedown
);
269 signal(SIGHUP
, SIG_IGN
);
270 signal(SIGUSR1
, SIG_IGN
);
271 signal(SIGUSR2
, SIG_IGN
);
275 pthread_mutex_unlock(&g_guardian_lock
);
279 int ret
=waitpid(pid
,&status
,WNOHANG
);
282 L
<<Logger::Error
<<"In guardian loop, waitpid returned error: "<<strerror(errno
)<<endl
;
283 L
<<Logger::Error
<<"Dying"<<endl
;
286 else if(ret
) // something exited
288 else { // child is alive
289 // execute some kind of ping here
291 takedown(1); // needs a parameter..
292 setStatus("Child running on pid "+itoa(pid
));
297 pthread_mutex_lock(&g_guardian_lock
);
302 if(WIFEXITED(status
)) {
303 int ret
=WEXITSTATUS(status
);
306 L
<<Logger::Error
<<"Child requested a stop, exiting"<<endl
;
309 setStatus("Child died with code "+itoa(ret
));
310 L
<<Logger::Error
<<"Our pdns instance exited with code "<<ret
<<endl
;
311 L
<<Logger::Error
<<"Respawning"<<endl
;
316 if(WIFSIGNALED(status
)) {
317 int sig
=WTERMSIG(status
);
318 setStatus("Child died because of signal "+itoa(sig
));
319 L
<<Logger::Error
<<"Our pdns instance ("<<pid
<<") exited after signal "<<sig
<<endl
;
321 if(WCOREDUMP(status
))
322 L
<<Logger::Error
<<"Dumped core"<<endl
;
325 L
<<Logger::Error
<<"Respawning"<<endl
;
329 L
<<Logger::Error
<<"No clue what happened! Respawning"<<endl
;
332 L
<<Logger::Error
<<"Unable to fork: "<<strerror(errno
)<<endl
;
338 static void UNIX_declareArguments()
340 ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=SYSCONFDIR
;
341 ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")="";
342 ::arg().set("socket-dir","Where the controlsocket will live")=LOCALSTATEDIR
;
343 ::arg().set("module-dir","Default directory for modules")=LIBDIR
;
344 ::arg().set("chroot","If set, chroot to this directory for more security")="";
345 ::arg().set("logging-facility","Log under a specific facility")="";
346 ::arg().set("daemon","Operate as a daemon")="no";
350 static void loadModules()
352 if(!::arg()["load-modules"].empty()) {
353 vector
<string
>modules
;
355 stringtok(modules
,::arg()["load-modules"],",");
357 for(vector
<string
>::const_iterator i
=modules
.begin();i
!=modules
.end();++i
) {
359 const string
&module
=*i
;
361 if(module
.find(".")==string::npos
)
362 res
=UeberBackend::loadmodule(::arg()["module-dir"]+"/lib"+module
+"backend.so");
363 else if(module
[0]=='/' || (module
[0]=='.' && module
[1]=='/') || (module
[0]=='.' && module
[1]=='.')) // absolute or current path
364 res
=UeberBackend::loadmodule(module
);
366 res
=UeberBackend::loadmodule(::arg()["module-dir"]+"/"+module
);
369 L
<<Logger::Error
<<"receiver unable to load module "<<module
<<endl
;
377 #include <execinfo.h>
378 static void tbhandler(int num
)
380 L
<<Logger::Critical
<<"Got a signal "<<num
<<", attempting to print trace: "<<endl
;
381 void *array
[20]; //only care about last 17 functions (3 taken with tracing support)
386 size
= backtrace (array
, 20);
387 strings
= backtrace_symbols (array
, size
); //Need -rdynamic gcc (linker) flag for this to work
389 for (i
= 0; i
< size
; i
++) //skip useless functions
390 L
<<Logger::Error
<<strings
[i
]<<endl
;
393 signal(SIGABRT
, SIG_DFL
);
394 abort();//hopefully will give core
399 //! The main function of pdns, the pdns process
400 int main(int argc
, char **argv
)
402 versionSetProduct("Authoritative Server");
403 reportAllTypes(); // init MOADNSParser
405 s_programname
="pdns";
409 signal(SIGSEGV
,tbhandler
);
410 signal(SIGFPE
,tbhandler
);
411 signal(SIGABRT
,tbhandler
);
412 signal(SIGILL
,tbhandler
);
416 std::ios_base::sync_with_stdio(false);
419 L
.toConsole(Logger::Warning
);
422 UNIX_declareArguments();
424 ::arg().laxParse(argc
,argv
); // do a lax parse
426 if(::arg().mustDo("version")) {
427 showProductVersion();
431 if(::arg()["config-name"]!="")
432 s_programname
+="-"+::arg()["config-name"];
434 (void)theL(s_programname
);
436 string configname
=::arg()["config-dir"]+"/"+s_programname
+".conf";
437 cleanSlashes(configname
);
439 if(!::arg().mustDo("config") && !::arg().mustDo("no-config")) // "config" == print a configuration file
440 ::arg().laxFile(configname
.c_str());
442 ::arg().laxParse(argc
,argv
); // reparse so the commandline still wins
443 if(!::arg()["logging-facility"].empty()) {
444 int val
=logFacilityToLOG(::arg().asNum("logging-facility") );
446 theL().setFacility(val
);
448 L
<<Logger::Error
<<"Unknown logging facility "<<::arg().asNum("logging-facility") <<endl
;
451 L
.setLoglevel((Logger::Urgency
)(::arg().asNum("loglevel")));
452 L
.toConsole((Logger::Urgency
)(::arg().asNum("loglevel")));
454 if(::arg().mustDo("help") || ::arg().mustDo("config")) {
455 ::arg().set("daemon")="no";
456 ::arg().set("guardian")="no";
459 if(::arg().mustDo("guardian") && !isGuarded(argv
)) {
460 if(::arg().mustDo("daemon")) {
461 L
.toConsole(Logger::Critical
);
464 guardian(argc
, argv
);
465 // never get here, guardian will reinvoke process
466 cerr
<<"Um, we did get here!"<<endl
;
470 // we really need to do work - either standalone or as an instance
473 if(!::arg().mustDo("traceback-handler")) {
474 L
<<Logger::Warning
<<"Disabling traceback handler"<<endl
;
475 signal(SIGSEGV
,SIG_DFL
);
476 signal(SIGFPE
,SIG_DFL
);
477 signal(SIGABRT
,SIG_DFL
);
478 signal(SIGILL
,SIG_DFL
);
482 seedRandom(::arg()["entropy-source"]);
485 BackendMakers().launch(::arg()["launch"]); // vrooooom!
487 if(!::arg().getCommands().empty()) {
488 cerr
<<"Fatal: non-option on the command line, perhaps a '--setting=123' statement missed the '='?"<<endl
;
492 if(::arg().mustDo("help")) {
493 cerr
<<"syntax:"<<endl
<<endl
;
494 cerr
<<::arg().helpstring(::arg()["help"])<<endl
;
498 if(::arg().mustDo("config")) {
499 cout
<<::arg().configstring()<<endl
;
503 if(::arg().mustDo("list-modules")) {
504 vector
<string
>modules
=BackendMakers().getModules();
505 cerr
<<"Modules available:"<<endl
;
506 for(vector
<string
>::const_iterator i
=modules
.begin();i
!=modules
.end();++i
)
512 if(::arg().mustDo("fancy-records")) {
516 if(!::arg().asNum("local-port")) {
517 L
<<Logger::Error
<<"Unable to launch, binding to no port or port 0 makes no sense"<<endl
;
518 exit(99); // this isn't going to fix itself either
520 if(!BackendMakers().numLauncheable()) {
521 L
<<Logger::Error
<<"Unable to launch, no backends configured for querying"<<endl
;
522 exit(99); // this isn't going to fix itself either
524 if(::arg().mustDo("daemon")) {
525 L
.toConsole(Logger::None
);
530 if(isGuarded(argv
)) {
531 L
<<Logger::Warning
<<"This is a guarded instance of pdns"<<endl
;
532 dl
=new DynListener
; // listens on stdin
535 L
<<Logger::Warning
<<"This is a standalone pdns"<<endl
;
537 if(::arg().mustDo("control-console"))
538 dl
=new DynListener();
540 dl
=new DynListener(s_programname
);
544 DynListener::registerFunc("SHOW",&DLShowHandler
, "show a specific statistic or * to get a list", "<statistic>");
545 DynListener::registerFunc("RPING",&DLPingHandler
, "ping instance");
546 DynListener::registerFunc("QUIT",&DLRQuitHandler
, "quit daemon");
547 DynListener::registerFunc("UPTIME",&DLUptimeHandler
, "get instance uptime");
548 DynListener::registerFunc("NOTIFY-HOST",&DLNotifyHostHandler
, "notify host for specific domain", "<domain> <host>");
549 DynListener::registerFunc("NOTIFY",&DLNotifyHandler
, "queue a notification", "<domain>");
550 DynListener::registerFunc("RELOAD",&DLReloadHandler
, "reload all zones");
551 DynListener::registerFunc("REDISCOVER",&DLRediscoverHandler
, "discover any new zones");
552 DynListener::registerFunc("VERSION",&DLVersionHandler
, "get instance version");
553 DynListener::registerFunc("PURGE",&DLPurgeHandler
, "purge entries from packet cache", "[<record>]");
554 DynListener::registerFunc("CCOUNTS",&DLCCHandler
, "get cache statistics");
555 DynListener::registerFunc("QTYPES", &DLQTypesHandler
, "get QType statistics");
556 DynListener::registerFunc("RESPSIZES", &DLRSizesHandler
, "get histogram of response sizes");
557 DynListener::registerFunc("REMOTES", &DLRemotesHandler
, "get top remotes");
558 DynListener::registerFunc("SET",&DLSettingsHandler
, "set config variables", "<var> <value>");
559 DynListener::registerFunc("RETRIEVE",&DLNotifyRetrieveHandler
, "retrieve slave domain", "<domain>");
560 DynListener::registerFunc("CURRENT-CONFIG",&DLCurrentConfigHandler
, "Retrieve the current configuration");
562 if(!::arg()["tcp-control-address"].empty()) {
563 DynListener
* dlTCP
=new DynListener(ComboAddress(::arg()["tcp-control-address"], ::arg().asNum("tcp-control-port")));
567 // reparse, with error checking
568 if(!::arg().mustDo("no-config"))
569 ::arg().file(configname
.c_str());
570 ::arg().parse(argc
,argv
);
572 if(::arg()["server-id"].empty()) {
574 gethostname(tmp
, sizeof(tmp
)-1);
575 ::arg().set("server-id")=tmp
;
579 N
=new UDPNameserver
; // this fails when we are not root, throws exception
581 if(!::arg().mustDo("disable-tcp"))
582 TN
=new TCPNameserver
;
584 catch(const ArgException
&A
) {
585 L
<<Logger::Error
<<"Fatal error: "<<A
.reason
<<endl
;
590 DLOG(L
<<Logger::Warning
<<"Verbose logging in effect"<<endl
);
592 showProductVersion();
597 catch(PDNSException
&AE
) {
598 if(!::arg().mustDo("daemon"))
599 cerr
<<"Exiting because: "<<AE
.reason
<<endl
;
600 L
<<Logger::Error
<<"Exiting because: "<<AE
.reason
<<endl
;
602 catch(std::exception
&e
) {
603 if(!::arg().mustDo("daemon"))
604 cerr
<<"Exiting because of STL error: "<<e
.what()<<endl
;
605 L
<<Logger::Error
<<"Exiting because of STL error: "<<e
.what()<<endl
;
608 cerr
<<"Uncaught exception of unknown type - sorry"<<endl
;