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

Revision 2608, 17.1 KB (checked in by peter, 13 months ago)

make sure pdns_server --version works even if guardian or daemon are enabled. Fixes #456, reported by several.

  • 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().mustDo("version")) {
421      cerr<<"Version: "VERSION", compiled on "<<__DATE__", "__TIME__;
422#ifdef __GNUC__
423      cerr<<" with gcc version "<<__VERSION__;
424#endif
425      cout<<endl;
426      exit(99);
427    }
428
429    if(::arg()["config-name"]!="") 
430      s_programname+="-"+::arg()["config-name"];
431   
432    (void)theL(s_programname);
433   
434    string configname=::arg()["config-dir"]+"/"+s_programname+".conf";
435    cleanSlashes(configname);
436
437    if(!::arg().mustDo("config") && !::arg().mustDo("no-config")) // "config" == print a configuration file
438      ::arg().laxFile(configname.c_str());
439   
440    ::arg().laxParse(argc,argv); // reparse so the commandline still wins
441    if(!::arg()["logging-facility"].empty()) {
442      int val=logFacilityToLOG(::arg().asNum("logging-facility") );
443      if(val >= 0)
444        theL().setFacility(val);
445      else
446        L<<Logger::Error<<"Unknown logging facility "<<::arg().asNum("logging-facility") <<endl;
447    }
448
449    L.setLoglevel((Logger::Urgency)(::arg().asNum("loglevel")));
450    L.toConsole((Logger::Urgency)(::arg().asNum("loglevel"))); 
451
452    if(::arg().mustDo("help") || ::arg().mustDo("config")) {
453      ::arg().set("daemon")="no";
454      ::arg().set("guardian")="no";
455    }
456
457    if(::arg().mustDo("guardian") && !isGuarded(argv)) {
458      if(::arg().mustDo("daemon")) {
459        L.toConsole(Logger::Critical);
460        daemonize();
461      }
462      guardian(argc, argv); 
463      // never get here, guardian will reinvoke process
464      cerr<<"Um, we did get here!"<<endl;
465    }
466
467   
468    // we really need to do work - either standalone or as an instance
469   
470    seedRandom(::arg()["entropy-source"]);
471   
472    loadModules();
473    BackendMakers().launch(::arg()["launch"]); // vrooooom!
474
475    if(!::arg().getCommands().empty()) {
476      cerr<<"Fatal: non-option on the command line, perhaps a '--setting=123' statement missed the '='?"<<endl;
477      exit(99);
478    }
479   
480    if(::arg().mustDo("help")) {
481      cerr<<"syntax:"<<endl<<endl;
482      cerr<<::arg().helpstring(::arg()["help"])<<endl;
483      exit(99);
484    }
485   
486    if(::arg().mustDo("config")) {
487      cout<<::arg().configstring()<<endl;
488      exit(99);
489    }
490
491    if(::arg().mustDo("list-modules")) {
492      vector<string>modules=BackendMakers().getModules();
493      cerr<<"Modules available:"<<endl;
494      for(vector<string>::const_iterator i=modules.begin();i!=modules.end();++i)
495        cout<<*i<<endl;
496
497      exit(99);
498    }
499
500    if(::arg().mustDo("fancy-records")) {
501      reportFancyTypes();
502    }
503
504    if(!::arg().asNum("local-port")) {
505      L<<Logger::Error<<"Unable to launch, binding to no port or port 0 makes no sense"<<endl;
506      exit(99); // this isn't going to fix itself either
507    }
508    if(!BackendMakers().numLauncheable()) {
509      L<<Logger::Error<<"Unable to launch, no backends configured for querying"<<endl;
510      exit(99); // this isn't going to fix itself either
511    }   
512    if(::arg().mustDo("daemon")) {
513      L.toConsole(Logger::None);
514      if(!isGuarded(argv))
515        daemonize();
516    }
517
518    if(::arg()["server-id"].empty()) {
519      char tmp[128];
520      gethostname(tmp, sizeof(tmp)-1);
521      ::arg().set("server-id")=tmp;
522    }
523
524    if(isGuarded(argv)) {
525      L<<Logger::Warning<<"This is a guarded instance of pdns"<<endl;
526      dl=new DynListener; // listens on stdin
527    }
528    else {
529      L<<Logger::Warning<<"This is a standalone pdns"<<endl; 
530     
531      if(::arg().mustDo("control-console"))
532        dl=new DynListener();
533      else
534        dl=new DynListener(s_programname);
535     
536      writePid();
537    }
538    DynListener::registerFunc("SHOW",&DLShowHandler, "show a specific statistic or * to get a list", "<statistic>");
539    DynListener::registerFunc("RPING",&DLPingHandler, "ping instance");
540    DynListener::registerFunc("QUIT",&DLRQuitHandler, "quit daemon");
541    DynListener::registerFunc("UPTIME",&DLUptimeHandler, "get instance uptime");
542    DynListener::registerFunc("NOTIFY-HOST",&DLNotifyHostHandler, "notify host for specific domain", "<domain> <host>");
543    DynListener::registerFunc("NOTIFY",&DLNotifyHandler, "queue a notification", "<domain>");
544    DynListener::registerFunc("RELOAD",&DLReloadHandler, "reload all zones");
545    DynListener::registerFunc("REDISCOVER",&DLRediscoverHandler, "discover any new zones");
546    DynListener::registerFunc("VERSION",&DLVersionHandler, "get instance version");
547    DynListener::registerFunc("PURGE",&DLPurgeHandler, "purge entries from packet cache", "[<record>]");
548    DynListener::registerFunc("CCOUNTS",&DLCCHandler, "get cache statistics");
549    DynListener::registerFunc("SET",&DLSettingsHandler, "set config variables", "<var> <value>");
550    DynListener::registerFunc("RETRIEVE",&DLNotifyRetrieveHandler, "retrieve slave domain", "<domain>");
551
552    if(!::arg()["tcp-control-address"].empty()) {
553      DynListener* dlTCP=new DynListener(ComboAddress(::arg()["tcp-control-address"], ::arg().asNum("tcp-control-port")));
554      dlTCP->go();
555    }
556
557    // reparse, with error checking
558    if(!::arg().mustDo("no-config"))
559      ::arg().file(configname.c_str());
560    ::arg().parse(argc,argv);
561    UeberBackend::go();
562    N=new UDPNameserver; // this fails when we are not root, throws exception
563   
564    if(!::arg().mustDo("disable-tcp"))
565      TN=new TCPNameserver; 
566  }
567  catch(const ArgException &A) {
568    L<<Logger::Error<<"Fatal error: "<<A.reason<<endl;
569    exit(1);
570  }
571 
572  declareStats();
573  DLOG(L<<Logger::Warning<<"Verbose logging in effect"<<endl);
574 
575  L<<Logger::Warning<<"PowerDNS "<<VERSION<<" (C) 2001-2012 PowerDNS.COM BV ("<<__DATE__", "__TIME__;
576#ifdef __GNUC__
577  L<<", gcc "__VERSION__;
578#endif // add other compilers here
579  L<<") starting up"<<endl;
580
581  L<<Logger::Warning<<"PowerDNS comes with ABSOLUTELY NO WARRANTY. "
582    "This is free software, and you are welcome to redistribute it "
583    "according to the terms of the GPL version 2."<<endl;
584
585
586  try {
587
588    mainthread();
589  }
590  catch(AhuException &AE) {
591    if(!::arg().mustDo("daemon"))
592      cerr<<"Exiting because: "<<AE.reason<<endl;
593    L<<Logger::Error<<"Exiting because: "<<AE.reason<<endl;
594  }     
595  catch(std::exception &e) {
596    if(!::arg().mustDo("daemon"))
597      cerr<<"Exiting because of STL error: "<<e.what()<<endl;
598    L<<Logger::Error<<"Exiting because of STL error: "<<e.what()<<endl;
599  }
600  catch(...) {
601    cerr<<"Uncaught exception of unknown type - sorry"<<endl;
602  }
603
604  exit(1);
605 
606}
607
608
Note: See TracBrowser for help on using the browser.