]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/receiver.cc
Merge pull request #965 from zeha/remove-unused-vars
[thirdparty/pdns.git] / pdns / receiver.cc
1 /*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002 - 2013 PowerDNS.COM BV
4
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
8
9
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.
14
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
18 */
19 #include "packetcache.hh"
20
21 #include <cstdio>
22 #include <signal.h>
23 #include <cstring>
24 #include <cstdlib>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <iostream>
30 #include <string>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <sys/wait.h>
36 #include <errno.h>
37 #include <pthread.h>
38 #include <unistd.h>
39 #include <sys/mman.h>
40 #include <fcntl.h>
41 #include <fstream>
42 #include <boost/algorithm/string.hpp>
43
44 #include "config.h"
45 #include "dns.hh"
46 #include "dnsbackend.hh"
47 #include "ueberbackend.hh"
48 #include "dnspacket.hh"
49 #include "nameserver.hh"
50 #include "distributor.hh"
51 #include "logger.hh"
52 #include "arguments.hh"
53 #include "packethandler.hh"
54 #include "statbag.hh"
55 #include "tcpreceiver.hh"
56 #include "ws.hh"
57 #include "misc.hh"
58 #include "dynlistener.hh"
59 #include "dynhandler.hh"
60 #include "communicator.hh"
61 #include "dnsproxy.hh"
62 #include "utility.hh"
63 #include "common_startup.hh"
64 #include "dnsrecords.hh"
65 #include "version.hh"
66
67
68 time_t s_starttime;
69
70 string s_programname="pdns"; // used in packethandler.cc
71
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";
81
82
83 // start (sys)logging
84
85 /** \var Logger L
86 \brief All logging is done via L, a Logger instance
87 */
88
89
90 /**
91 \file receiver.cc
92 \brief The main loop of powerdns
93
94 This file is where it all happens - main is here, as are the two pivotal threads qthread() and athread()
95 */
96
97 void daemonize(void)
98 {
99 if(fork())
100 exit(0); // bye bye
101
102 setsid();
103
104 int i=open("/dev/null",O_RDWR); /* open stdin */
105 if(i < 0)
106 L<<Logger::Critical<<"Unable to open /dev/null: "<<stringerror()<<endl;
107 else {
108 dup2(i,0); /* stdin */
109 dup2(i,1); /* stderr */
110 dup2(i,2); /* stderr */
111 close(i);
112 }
113 }
114
115 static int cpid;
116 static void takedown(int i)
117 {
118 if(cpid) {
119 L<<Logger::Error<<"Guardian is killed, taking down children with us"<<endl;
120 kill(cpid,SIGKILL);
121 exit(0);
122 }
123 }
124
125 static void writePid(void)
126 {
127 string fname=::arg()["socket-dir"]+"/"+s_programname+".pid";
128 ofstream of(fname.c_str());
129 if(of)
130 of<<getpid()<<endl;
131 else
132 L<<Logger::Error<<"Requested to write pid for "<<getpid()<<" to "<<fname<<" failed: "<<strerror(errno)<<endl;
133 }
134
135 int g_fd1[2], g_fd2[2];
136 FILE *g_fp;
137 pthread_mutex_t g_guardian_lock = PTHREAD_MUTEX_INITIALIZER;
138
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)
141 {
142 kill(cpid, SIGKILL); // why?
143 kill(cpid, SIGKILL); // why?
144 sleep(1);
145 return "ok";
146 }
147
148 static string DLRestHandler(const vector<string>&parts, pid_t ppid)
149 {
150 string line;
151
152 for(vector<string>::const_iterator i=parts.begin();i!=parts.end();++i) {
153 if(i!=parts.begin())
154 line.append(1,' ');
155 line.append(*i);
156 }
157 line.append(1,'\n');
158
159 Lock l(&g_guardian_lock);
160
161 try {
162 writen2(g_fd1[1],line.c_str(),line.size()+1);
163 }
164 catch(PDNSException &ae) {
165 return "Error communicating with instance: "+ae.reason;
166 }
167 char mesg[512];
168 string response;
169 while(fgets(mesg,sizeof(mesg),g_fp)) {
170 if(*mesg=='\0')
171 break;
172 response+=mesg;
173 }
174 boost::trim_right(response);
175 return response;
176 }
177
178
179
180 static int guardian(int argc, char **argv)
181 {
182 if(isGuarded(argv))
183 return 0;
184
185 int infd=0, outfd=1;
186
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);
193 dlg.go();
194 string progname=argv[0];
195
196 bool first=true;
197 cpid=0;
198
199 pthread_mutex_lock(&g_guardian_lock);
200
201 for(;;) {
202 int pid;
203 setStatus("Launching child");
204
205 if(pipe(g_fd1)<0 || pipe(g_fd2)<0) {
206 L<<Logger::Critical<<"Unable to open pipe for coprocess: "<<strerror(errno)<<endl;
207 exit(1);
208 }
209
210 if(!(g_fp=fdopen(g_fd2[0],"r"))) {
211 L<<Logger::Critical<<"Unable to associate a file pointer with pipe: "<<stringerror()<<endl;
212 exit(1);
213 }
214 setbuf(g_fp,0); // no buffering please, confuses select
215
216 if(!(pid=fork())) { // child
217 signal(SIGTERM, SIG_DFL);
218
219 signal(SIGHUP, SIG_DFL);
220 signal(SIGUSR1, SIG_DFL);
221 signal(SIGUSR2, SIG_DFL);
222
223 char **const newargv=new char*[argc+2];
224 int n;
225
226 if(::arg()["config-name"]!="") {
227 progname+="-"+::arg()["config-name"];
228 L<<Logger::Error<<"Virtual configuration name: "<<::arg()["config-name"]<<endl;
229 }
230
231 newargv[0]=strdup(const_cast<char *>((progname+"-instance").c_str()));
232 for(n=1;n<argc;n++) {
233 newargv[n]=argv[n];
234 }
235 newargv[n]=0;
236
237 L<<Logger::Error<<"Guardian is launching an instance"<<endl;
238 close(g_fd1[1]);
239 fclose(g_fp); // this closes g_fd2[0] for us
240
241 if(g_fd1[0]!= infd) {
242 dup2(g_fd1[0], infd);
243 close(g_fd1[0]);
244 }
245
246 if(g_fd2[1]!= outfd) {
247 dup2(g_fd2[1], outfd);
248 close(g_fd2[1]);
249 }
250 if(execvp(argv[0], newargv)<0) {
251 L<<Logger::Error<<"Unable to execvp '"<<argv[0]<<"': "<<strerror(errno)<<endl;
252 char **p=newargv;
253 while(*p)
254 L<<Logger::Error<<*p++<<endl;
255
256 exit(1);
257 }
258 L<<Logger::Error<<"execvp returned!!"<<endl;
259 // never reached
260 }
261 else if(pid>0) { // parent
262 close(g_fd1[0]);
263 close(g_fd2[1]);
264
265 if(first) {
266 first=false;
267 signal(SIGTERM, takedown);
268
269 signal(SIGHUP, SIG_IGN);
270 signal(SIGUSR1, SIG_IGN);
271 signal(SIGUSR2, SIG_IGN);
272
273 writePid();
274 }
275 pthread_mutex_unlock(&g_guardian_lock);
276 int status;
277 cpid=pid;
278 for(;;) {
279 int ret=waitpid(pid,&status,WNOHANG);
280
281 if(ret<0) {
282 L<<Logger::Error<<"In guardian loop, waitpid returned error: "<<strerror(errno)<<endl;
283 L<<Logger::Error<<"Dying"<<endl;
284 exit(1);
285 }
286 else if(ret) // something exited
287 break;
288 else { // child is alive
289 // execute some kind of ping here
290 if(DLQuitPlease())
291 takedown(1); // needs a parameter..
292 setStatus("Child running on pid "+itoa(pid));
293 sleep(1);
294 }
295 }
296
297 pthread_mutex_lock(&g_guardian_lock);
298 close(g_fd1[1]);
299 fclose(g_fp);
300 g_fp=0;
301
302 if(WIFEXITED(status)) {
303 int ret=WEXITSTATUS(status);
304
305 if(ret==99) {
306 L<<Logger::Error<<"Child requested a stop, exiting"<<endl;
307 exit(1);
308 }
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;
312
313 sleep(1);
314 continue;
315 }
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;
320 #ifdef WCOREDUMP
321 if(WCOREDUMP(status))
322 L<<Logger::Error<<"Dumped core"<<endl;
323 #endif
324
325 L<<Logger::Error<<"Respawning"<<endl;
326 sleep(1);
327 continue;
328 }
329 L<<Logger::Error<<"No clue what happened! Respawning"<<endl;
330 }
331 else {
332 L<<Logger::Error<<"Unable to fork: "<<strerror(errno)<<endl;
333 exit(1);
334 }
335 }
336 }
337
338 static void UNIX_declareArguments()
339 {
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";
347
348 }
349
350 static void loadModules()
351 {
352 if(!::arg()["load-modules"].empty()) {
353 vector<string>modules;
354
355 stringtok(modules,::arg()["load-modules"],",");
356
357 for(vector<string>::const_iterator i=modules.begin();i!=modules.end();++i) {
358 bool res;
359 const string &module=*i;
360
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);
365 else
366 res=UeberBackend::loadmodule(::arg()["module-dir"]+"/"+module);
367
368 if(res==false) {
369 L<<Logger::Error<<"receiver unable to load module "<<module<<endl;
370 exit(1);
371 }
372 }
373 }
374 }
375
376 #ifdef __linux__
377 #include <execinfo.h>
378 static void tbhandler(int num)
379 {
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)
382 size_t size;
383 char **strings;
384 size_t i;
385
386 size = backtrace (array, 20);
387 strings = backtrace_symbols (array, size); //Need -rdynamic gcc (linker) flag for this to work
388
389 for (i = 0; i < size; i++) //skip useless functions
390 L<<Logger::Error<<strings[i]<<endl;
391
392
393 signal(SIGABRT, SIG_DFL);
394 abort();//hopefully will give core
395
396 }
397 #endif
398
399 //! The main function of pdns, the pdns process
400 int main(int argc, char **argv)
401 {
402 versionSetProduct("Authoritative Server");
403 reportAllTypes(); // init MOADNSParser
404
405 s_programname="pdns";
406 s_starttime=time(0);
407
408 #ifdef __linux__
409 signal(SIGSEGV,tbhandler);
410 signal(SIGFPE,tbhandler);
411 signal(SIGABRT,tbhandler);
412 signal(SIGILL,tbhandler);
413 #endif
414
415 #if __GNUC__ >= 3
416 std::ios_base::sync_with_stdio(false);
417 #endif
418
419 L.toConsole(Logger::Warning);
420 try {
421 declareArguments();
422 UNIX_declareArguments();
423
424 ::arg().laxParse(argc,argv); // do a lax parse
425
426 if(::arg().mustDo("version")) {
427 showProductVersion();
428 exit(99);
429 }
430
431 if(::arg()["config-name"]!="")
432 s_programname+="-"+::arg()["config-name"];
433
434 (void)theL(s_programname);
435
436 string configname=::arg()["config-dir"]+"/"+s_programname+".conf";
437 cleanSlashes(configname);
438
439 if(!::arg().mustDo("config") && !::arg().mustDo("no-config")) // "config" == print a configuration file
440 ::arg().laxFile(configname.c_str());
441
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") );
445 if(val >= 0)
446 theL().setFacility(val);
447 else
448 L<<Logger::Error<<"Unknown logging facility "<<::arg().asNum("logging-facility") <<endl;
449 }
450
451 L.setLoglevel((Logger::Urgency)(::arg().asNum("loglevel")));
452 L.toConsole((Logger::Urgency)(::arg().asNum("loglevel")));
453
454 if(::arg().mustDo("help") || ::arg().mustDo("config")) {
455 ::arg().set("daemon")="no";
456 ::arg().set("guardian")="no";
457 }
458
459 if(::arg().mustDo("guardian") && !isGuarded(argv)) {
460 if(::arg().mustDo("daemon")) {
461 L.toConsole(Logger::Critical);
462 daemonize();
463 }
464 guardian(argc, argv);
465 // never get here, guardian will reinvoke process
466 cerr<<"Um, we did get here!"<<endl;
467 }
468
469
470 // we really need to do work - either standalone or as an instance
471
472 #ifdef __linux__
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);
479 }
480 #endif
481
482 seedRandom(::arg()["entropy-source"]);
483
484 loadModules();
485 BackendMakers().launch(::arg()["launch"]); // vrooooom!
486
487 if(!::arg().getCommands().empty()) {
488 cerr<<"Fatal: non-option on the command line, perhaps a '--setting=123' statement missed the '='?"<<endl;
489 exit(99);
490 }
491
492 if(::arg().mustDo("help")) {
493 cerr<<"syntax:"<<endl<<endl;
494 cerr<<::arg().helpstring(::arg()["help"])<<endl;
495 exit(99);
496 }
497
498 if(::arg().mustDo("config")) {
499 cout<<::arg().configstring()<<endl;
500 exit(99);
501 }
502
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)
507 cout<<*i<<endl;
508
509 exit(99);
510 }
511
512 if(::arg().mustDo("fancy-records")) {
513 reportFancyTypes();
514 }
515
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
519 }
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
523 }
524 if(::arg().mustDo("daemon")) {
525 L.toConsole(Logger::None);
526 if(!isGuarded(argv))
527 daemonize();
528 }
529
530 if(isGuarded(argv)) {
531 L<<Logger::Warning<<"This is a guarded instance of pdns"<<endl;
532 dl=new DynListener; // listens on stdin
533 }
534 else {
535 L<<Logger::Warning<<"This is a standalone pdns"<<endl;
536
537 if(::arg().mustDo("control-console"))
538 dl=new DynListener();
539 else
540 dl=new DynListener(s_programname);
541
542 writePid();
543 }
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");
561
562 if(!::arg()["tcp-control-address"].empty()) {
563 DynListener* dlTCP=new DynListener(ComboAddress(::arg()["tcp-control-address"], ::arg().asNum("tcp-control-port")));
564 dlTCP->go();
565 }
566
567 // reparse, with error checking
568 if(!::arg().mustDo("no-config"))
569 ::arg().file(configname.c_str());
570 ::arg().parse(argc,argv);
571
572 if(::arg()["server-id"].empty()) {
573 char tmp[128];
574 gethostname(tmp, sizeof(tmp)-1);
575 ::arg().set("server-id")=tmp;
576 }
577
578 UeberBackend::go();
579 N=new UDPNameserver; // this fails when we are not root, throws exception
580
581 if(!::arg().mustDo("disable-tcp"))
582 TN=new TCPNameserver;
583 }
584 catch(const ArgException &A) {
585 L<<Logger::Error<<"Fatal error: "<<A.reason<<endl;
586 exit(1);
587 }
588
589 declareStats();
590 DLOG(L<<Logger::Warning<<"Verbose logging in effect"<<endl);
591
592 showProductVersion();
593
594 try {
595 mainthread();
596 }
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;
601 }
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;
606 }
607 catch(...) {
608 cerr<<"Uncaught exception of unknown type - sorry"<<endl;
609 }
610
611 exit(1);
612
613 }
614
615