root/trunk/pdns/pdns/receiver.cc @ 2556

Revision 2556, 17.1 KB (checked in by peter, 14 months ago)

Remove ancient vestiges of binary-only powerdns releases

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1/*
2    PowerDNS Versatile Database Driven Nameserver
3    Copyright (C) 2002 - 2011  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
66
67time_t s_starttime;
68
69string s_programname="pdns"; // used in packethandler.cc
70
71const char *funnytext=
72"*****************************************************************************\n"\
73"Ok, you just ran pdns_server through 'strings' hoping to find funny messages.\n"\
74"Well, you found one. \n"\
75"Two ions are flying through their particle accelerator, says the one to the\n"
76"other 'I think I've lost an electron!' \n"\
77"So the other one says, 'Are you sure?'. 'YEAH! I'M POSITIVE!'\n"\
78"                                            the pdns crew - pdns@powerdns.com\n"
79"*****************************************************************************\n";
80
81
82// start (sys)logging
83
84/** \var Logger L
85\brief All logging is done via L, a Logger instance
86*/
87
88
89/**
90\file receiver.cc
91\brief The main loop of powerdns
92
93This file is where it all happens - main is here, as are the two pivotal threads qthread() and athread()
94*/
95
96void daemonize(void)
97{
98  if(fork())
99    exit(0); // bye bye
100 
101  setsid(); 
102
103  int i=open("/dev/null",O_RDWR); /* open stdin */
104  if(i < 0) 
105    L<<Logger::Critical<<"Unable to open /dev/null: "<<stringerror()<<endl;
106  else {
107    dup2(i,0); /* stdin */
108    dup2(i,1); /* stderr */
109    dup2(i,2); /* stderr */
110    close(i);
111  }
112}
113
114static int cpid;
115static void takedown(int i)
116{
117  if(cpid) {
118    L<<Logger::Error<<"Guardian is killed, taking down children with us"<<endl;
119    kill(cpid,SIGKILL);
120    exit(1);
121  }
122}
123
124static void writePid(void)
125{
126  string fname=::arg()["socket-dir"]+"/"+s_programname+".pid";
127  ofstream of(fname.c_str());
128  if(of)
129    of<<getpid()<<endl;
130  else
131    L<<Logger::Error<<"Requested to write pid for "<<getpid()<<" to "<<fname<<" failed: "<<strerror(errno)<<endl;
132}
133
134int g_fd1[2], g_fd2[2];
135FILE *g_fp;
136pthread_mutex_t g_guardian_lock = PTHREAD_MUTEX_INITIALIZER;
137
138static string DLRestHandler(const vector<string>&parts, pid_t ppid)
139{
140  string line;
141 
142  for(vector<string>::const_iterator i=parts.begin();i!=parts.end();++i) {
143    if(i!=parts.begin())
144      line.append(1,' ');
145    line.append(*i);
146  }
147  line.append(1,'\n');
148 
149  Lock l(&g_guardian_lock);
150
151  try {
152    writen2(g_fd1[1],line.c_str(),line.size()+1);
153  }
154  catch(AhuException &ae) {
155    return "Error communicating with instance: "+ae.reason;
156  }
157  char mesg[512];
158  string response;
159  while(fgets(mesg,sizeof(mesg),g_fp)) {
160    if(*mesg=='\n')
161      break;
162    response+=mesg;
163  }
164  boost::trim_right(response);
165  return response;
166}
167
168static string DLCycleHandler(const vector<string>&parts, pid_t ppid)
169{
170  kill(cpid, SIGKILL); // why?
171  kill(cpid, SIGKILL); // why?
172  sleep(1);
173  return "ok";
174}
175
176static int guardian(int argc, char **argv)
177{
178  if(isGuarded(argv))
179    return 0;
180
181  int infd=0, outfd=1;
182
183  DynListener dlg(s_programname);
184  dlg.registerFunc("QUIT",&DLQuitHandler, "quit daemon");
185  dlg.registerFunc("CYCLE",&DLCycleHandler, "restart instance");
186  dlg.registerFunc("PING",&DLPingHandler, "ping guardian");
187  dlg.registerFunc("STATUS",&DLStatusHandler, "get instance status from guardian");
188  dlg.registerRestFunc(&DLRestHandler);
189  dlg.go();
190  string progname=argv[0];
191
192  bool first=true;
193  cpid=0;
194
195  pthread_mutex_lock(&g_guardian_lock);
196
197  for(;;) {
198    int pid;
199    setStatus("Launching child");
200   
201    if(pipe(g_fd1)<0 || pipe(g_fd2)<0) {
202      L<<Logger::Critical<<"Unable to open pipe for coprocess: "<<strerror(errno)<<endl;
203      exit(1);
204    }
205
206    if(!(g_fp=fdopen(g_fd2[0],"r"))) {
207      L<<Logger::Critical<<"Unable to associate a file pointer with pipe: "<<stringerror()<<endl;
208      exit(1);
209    }
210    setbuf(g_fp,0); // no buffering please, confuses select
211
212    if(!(pid=fork())) { // child
213      signal(SIGTERM, SIG_DFL);
214
215      signal(SIGHUP, SIG_DFL);
216      signal(SIGUSR1, SIG_DFL);
217      signal(SIGUSR2, SIG_DFL);
218
219      char **const newargv=new char*[argc+2];
220      int n;
221
222      if(::arg()["config-name"]!="") {
223        progname+="-"+::arg()["config-name"];
224        L<<Logger::Error<<"Virtual configuration name: "<<::arg()["config-name"]<<endl;
225      }
226
227      newargv[0]=strdup(const_cast<char *>((progname+"-instance").c_str()));
228      for(n=1;n<argc;n++) {
229        newargv[n]=argv[n];
230      }
231      newargv[n]=0;
232     
233      L<<Logger::Error<<"Guardian is launching an instance"<<endl;
234      close(g_fd1[1]);
235      fclose(g_fp); // this closes g_fd2[0] for us
236
237      if(g_fd1[0]!= infd) {
238        dup2(g_fd1[0], infd);
239        close(g_fd1[0]);
240      }
241
242      if(g_fd2[1]!= outfd) {
243        dup2(g_fd2[1], outfd);
244        close(g_fd2[1]);
245      }
246      if(execvp(argv[0], newargv)<0) {
247        L<<Logger::Error<<"Unable to execvp '"<<argv[0]<<"': "<<strerror(errno)<<endl;
248        char **p=newargv;
249        while(*p)
250          L<<Logger::Error<<*p++<<endl;
251
252        exit(1);
253      }
254      L<<Logger::Error<<"execvp returned!!"<<endl;
255      // never reached
256    }
257    else if(pid>0) { // parent
258      close(g_fd1[0]);
259      close(g_fd2[1]);
260
261      if(first) {
262        first=false;
263        signal(SIGTERM, takedown);
264
265        signal(SIGHUP, SIG_IGN);
266        signal(SIGUSR1, SIG_IGN);
267        signal(SIGUSR2, SIG_IGN);
268
269        writePid();
270      }
271      pthread_mutex_unlock(&g_guardian_lock); 
272      int status;
273      cpid=pid;
274      for(;;) {
275        int ret=waitpid(pid,&status,WNOHANG);
276
277        if(ret<0) {
278          L<<Logger::Error<<"In guardian loop, waitpid returned error: "<<strerror(errno)<<endl;
279          L<<Logger::Error<<"Dying"<<endl;
280          exit(1);
281        }
282        else if(ret) // something exited
283          break;
284        else { // child is alive
285          // execute some kind of ping here
286          if(DLQuitPlease())
287            takedown(1); // needs a parameter..
288          setStatus("Child running on pid "+itoa(pid));
289          sleep(1);
290        }
291      }
292
293      pthread_mutex_lock(&g_guardian_lock);
294      close(g_fd1[1]);
295      fclose(g_fp);
296      g_fp=0;
297
298      if(WIFEXITED(status)) {
299        int ret=WEXITSTATUS(status);
300
301        if(ret==99) {
302          L<<Logger::Error<<"Child requested a stop, exiting"<<endl;
303          exit(1);
304        }
305        setStatus("Child died with code "+itoa(ret));
306        L<<Logger::Error<<"Our pdns instance exited with code "<<ret<<endl;
307        L<<Logger::Error<<"Respawning"<<endl;
308
309        sleep(1);
310        continue;
311      }
312      if(WIFSIGNALED(status)) {
313        int sig=WTERMSIG(status);
314        setStatus("Child died because of signal "+itoa(sig));
315        L<<Logger::Error<<"Our pdns instance ("<<pid<<") exited after signal "<<sig<<endl;
316#ifdef WCOREDUMP
317        if(WCOREDUMP(status)) 
318          L<<Logger::Error<<"Dumped core"<<endl;
319#endif
320
321        L<<Logger::Error<<"Respawning"<<endl;
322        sleep(1);
323        continue;
324      }
325      L<<Logger::Error<<"No clue what happened! Respawning"<<endl;
326    }
327    else {
328      L<<Logger::Error<<"Unable to fork: "<<strerror(errno)<<endl;
329      exit(1);
330    }
331  }
332}
333
334static void UNIX_declareArguments()
335{
336  ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=SYSCONFDIR;
337  ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")="";
338  ::arg().set("socket-dir","Where the controlsocket will live")=LOCALSTATEDIR;
339  ::arg().set("module-dir","Default directory for modules")=LIBDIR;
340  ::arg().set("chroot","If set, chroot to this directory for more security")="";
341  ::arg().set("logging-facility","Log under a specific facility")="";
342  ::arg().set("daemon","Operate as a daemon")="no";
343
344}
345
346static void loadModules()
347{
348  if(!::arg()["load-modules"].empty()) { 
349    vector<string>modules;
350   
351    stringtok(modules,::arg()["load-modules"],",");
352   
353    for(vector<string>::const_iterator i=modules.begin();i!=modules.end();++i) {
354      bool res;
355      const string &module=*i;
356     
357      if(module.find(".")==string::npos)
358        res=UeberBackend::loadmodule(::arg()["module-dir"]+"/lib"+module+"backend.so");
359      else if(module[0]=='/' || (module[0]=='.' && module[1]=='/') || (module[0]=='.' && module[1]=='.'))    // absolute or current path
360        res=UeberBackend::loadmodule(module);
361      else
362        res=UeberBackend::loadmodule(::arg()["module-dir"]+"/"+module);
363     
364      if(res==false) {
365        L<<Logger::Error<<"receiver unable to load module "<<module<<endl;
366        exit(1);
367      }
368    }
369  }
370}
371
372
373
374#ifdef __linux__
375#include <execinfo.h>
376static void tbhandler(int num)
377{
378  L<<Logger::Critical<<"Got a signal "<<num<<", attempting to print trace: "<<endl;
379  void *array[20]; //only care about last 17 functions (3 taken with tracing support)
380  size_t size;
381  char **strings;
382  size_t i;
383 
384  size = backtrace (array, 20);
385  strings = backtrace_symbols (array, size); //Need -rdynamic gcc (linker) flag for this to work
386 
387  for (i = 0; i < size; i++) //skip useless functions
388    L<<Logger::Error<<strings[i]<<endl;
389 
390 
391  signal(SIGABRT, SIG_DFL);
392  abort();//hopefully will give core
393
394}
395#endif
396
397//! The main function of pdns, the pdns process
398int main(int argc, char **argv)
399{ 
400  reportAllTypes(); // init MOADNSParser
401
402  s_programname="pdns";
403  s_starttime=time(0);
404
405#ifdef __linux__
406  signal(SIGSEGV,tbhandler);
407  signal(SIGFPE,tbhandler);
408  signal(SIGABRT,tbhandler);
409  signal(SIGILL,tbhandler);
410#endif
411
412
413  L.toConsole(Logger::Warning);
414  try {
415    declareArguments();
416    UNIX_declareArguments();
417     
418    ::arg().laxParse(argc,argv); // do a lax parse
419   
420    if(::arg()["config-name"]!="") 
421      s_programname+="-"+::arg()["config-name"];
422   
423    (void)theL(s_programname);
424   
425    string configname=::arg()["config-dir"]+"/"+s_programname+".conf";
426    cleanSlashes(configname);
427
428    if(!::arg().mustDo("config") && !::arg().mustDo("no-config")) // "config" == print a configuration file
429      ::arg().laxFile(configname.c_str());
430   
431    ::arg().laxParse(argc,argv); // reparse so the commandline still wins
432    if(!::arg()["logging-facility"].empty()) {
433      int val=logFacilityToLOG(::arg().asNum("logging-facility") );
434      if(val >= 0)
435        theL().setFacility(val);
436      else
437        L<<Logger::Error<<"Unknown logging facility "<<::arg().asNum("logging-facility") <<endl;
438    }
439
440    L.setLoglevel((Logger::Urgency)(::arg().asNum("loglevel")));
441    L.toConsole((Logger::Urgency)(::arg().asNum("loglevel"))); 
442
443    if(::arg().mustDo("help") || ::arg().mustDo("config")) {
444      ::arg().set("daemon")="no";
445      ::arg().set("guardian")="no";
446    }
447
448    if(::arg().mustDo("guardian") && !isGuarded(argv)) {
449      if(::arg().mustDo("daemon")) {
450        L.toConsole(Logger::Critical);
451        daemonize();
452      }
453      guardian(argc, argv); 
454      // never get here, guardian will reinvoke process
455      cerr<<"Um, we did get here!"<<endl;
456    }
457
458    if(::arg().mustDo("version")) {
459      cerr<<"Version: "VERSION", compiled on "<<__DATE__", "__TIME__;
460#ifdef __GNUC__
461      cerr<<" with gcc version "<<__VERSION__;
462#endif
463      cout<<endl;
464      exit(99);
465    }
466   
467    // we really need to do work - either standalone or as an instance
468   
469    seedRandom(::arg()["entropy-source"]);
470   
471    loadModules();
472    BackendMakers().launch(::arg()["launch"]); // vrooooom!
473
474    if(!::arg().getCommands().empty()) {
475      cerr<<"Fatal: non-option on the command line, perhaps a '--setting=123' statement missed the '='?"<<endl;
476      exit(99);
477    }
478   
479    if(::arg().mustDo("help")) {
480      cerr<<"syntax:"<<endl<<endl;
481      cerr<<::arg().helpstring(::arg()["help"])<<endl;
482      exit(99);
483    }
484   
485    if(::arg().mustDo("config")) {
486      cout<<::arg().configstring()<<endl;
487      exit(99);
488    }
489
490    if(::arg().mustDo("list-modules")) {
491      vector<string>modules=BackendMakers().getModules();
492      cerr<<"Modules available:"<<endl;
493      for(vector<string>::const_iterator i=modules.begin();i!=modules.end();++i)
494        cout<<*i<<endl;
495
496      exit(99);
497    }
498
499    if(::arg().mustDo("fancy-records")) {
500      reportFancyTypes();
501    }
502
503    if(!::arg().asNum("local-port")) {
504      L<<Logger::Error<<"Unable to launch, binding to no port or port 0 makes no sense"<<endl;
505      exit(99); // this isn't going to fix itself either
506    }
507    if(!BackendMakers().numLauncheable()) {
508      L<<Logger::Error<<"Unable to launch, no backends configured for querying"<<endl;
509      exit(99); // this isn't going to fix itself either
510    }   
511    if(::arg().mustDo("daemon")) {
512      L.toConsole(Logger::None);
513      if(!isGuarded(argv))
514        daemonize();
515    }
516
517    if(::arg()["server-id"].empty()) {
518      char tmp[128];
519      gethostname(tmp, sizeof(tmp)-1);
520      ::arg().set("server-id")=tmp;
521    }
522
523    if(isGuarded(argv)) {
524      L<<Logger::Warning<<"This is a guarded instance of pdns"<<endl;
525      dl=new DynListener; // listens on stdin
526    }
527    else {
528      L<<Logger::Warning<<"This is a standalone pdns"<<endl; 
529     
530      if(::arg().mustDo("control-console"))
531        dl=new DynListener();
532      else
533        dl=new DynListener(s_programname);
534     
535      writePid();
536    }
537    DynListener::registerFunc("SHOW",&DLShowHandler, "show a specific statistic or * to get a list", "<statistic>");
538    DynListener::registerFunc("RPING",&DLPingHandler, "ping instance");
539    DynListener::registerFunc("QUIT",&DLRQuitHandler, "quit daemon");
540    DynListener::registerFunc("UPTIME",&DLUptimeHandler, "get instance uptime");
541    DynListener::registerFunc("NOTIFY-HOST",&DLNotifyHostHandler, "notify host for specific domain", "<domain> <host>");
542    DynListener::registerFunc("NOTIFY",&DLNotifyHandler, "queue a notification", "<domain>");
543    DynListener::registerFunc("RELOAD",&DLReloadHandler, "reload all zones");
544    DynListener::registerFunc("REDISCOVER",&DLRediscoverHandler, "discover any new zones");
545    DynListener::registerFunc("VERSION",&DLVersionHandler, "get instance version");
546    DynListener::registerFunc("PURGE",&DLPurgeHandler, "purge entries from packet cache", "[<record>]");
547    DynListener::registerFunc("CCOUNTS",&DLCCHandler, "get cache statistics");
548    DynListener::registerFunc("SET",&DLSettingsHandler, "set config variables", "<var> <value>");
549    DynListener::registerFunc("RETRIEVE",&DLNotifyRetrieveHandler, "retrieve slave domain", "<domain>");
550
551    if(!::arg()["tcp-control-address"].empty()) {
552      DynListener* dlTCP=new DynListener(ComboAddress(::arg()["tcp-control-address"], ::arg().asNum("tcp-control-port")));
553      dlTCP->go();
554    }
555
556    // reparse, with error checking
557    if(!::arg().mustDo("no-config"))
558      ::arg().file(configname.c_str());
559    ::arg().parse(argc,argv);
560    UeberBackend::go();
561    N=new UDPNameserver; // this fails when we are not root, throws exception
562   
563    if(!::arg().mustDo("disable-tcp"))
564      TN=new TCPNameserver; 
565  }
566  catch(const ArgException &A) {
567    L<<Logger::Error<<"Fatal error: "<<A.reason<<endl;
568    exit(1);
569  }
570 
571  declareStats();
572  DLOG(L<<Logger::Warning<<"Verbose logging in effect"<<endl);
573 
574  L<<Logger::Warning<<"PowerDNS "<<VERSION<<" (C) 2001-2012 PowerDNS.COM BV ("<<__DATE__", "__TIME__;
575#ifdef __GNUC__
576  L<<", gcc "__VERSION__;
577#endif // add other compilers here
578  L<<") starting up"<<endl;
579
580  L<<Logger::Warning<<"PowerDNS comes with ABSOLUTELY NO WARRANTY. "
581    "This is free software, and you are welcome to redistribute it "
582    "according to the terms of the GPL version 2."<<endl;
583
584
585  try {
586
587    mainthread();
588  }
589  catch(AhuException &AE) {
590    if(!::arg().mustDo("daemon"))
591      cerr<<"Exiting because: "<<AE.reason<<endl;
592    L<<Logger::Error<<"Exiting because: "<<AE.reason<<endl;
593  }     
594  catch(std::exception &e) {
595    if(!::arg().mustDo("daemon"))
596      cerr<<"Exiting because of STL error: "<<e.what()<<endl;
597    L<<Logger::Error<<"Exiting because of STL error: "<<e.what()<<endl;
598  }
599  catch(...) {
600    cerr<<"Uncaught exception of unknown type - sorry"<<endl;
601  }
602
603  exit(1);
604 
605}
606
607
Note: See TracBrowser for help on using the browser.