]>
Commit | Line | Data |
---|---|---|
12c86877 BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
8d022964 | 3 | Copyright (C) 2003 PowerDNS.COM BV |
12c86877 BH |
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 as published by | |
7 | the Free Software Foundation; either version 2 of the License, or | |
8 | (at your option) any later version. | |
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | */ | |
8d022964 | 19 | // $Id: receiver.cc,v 1.7 2003/03/04 18:33:39 ahu Exp $ |
12c86877 BH |
20 | #include <cstdio> |
21 | #include <signal.h> | |
22 | #include <cstring> | |
23 | #include <cstdlib> | |
24 | #include <sys/types.h> | |
25 | #include <sys/socket.h> | |
26 | #include <netinet/in.h> | |
27 | #include <arpa/inet.h> | |
28 | #include <iostream> | |
29 | #include <string> | |
30 | #include <sys/stat.h> | |
31 | #include <unistd.h> | |
32 | #include <sys/time.h> | |
33 | #include <sys/types.h> | |
34 | #include <sys/wait.h> | |
35 | #include <errno.h> | |
36 | #include <pthread.h> | |
37 | #include <unistd.h> | |
38 | #include <sys/mman.h> | |
39 | #include <fcntl.h> | |
40 | #include <fstream> | |
41 | ||
42 | #include "config.h" | |
43 | #include "dns.hh" | |
44 | #include "dnsbackend.hh" | |
45 | #include "ueberbackend.hh" | |
46 | #include "dnspacket.hh" | |
47 | #include "nameserver.hh" | |
48 | #include "distributor.hh" | |
49 | #include "logger.hh" | |
50 | #include "arguments.hh" | |
51 | #include "packethandler.hh" | |
52 | #include "statbag.hh" | |
53 | #include "tcpreceiver.hh" | |
54 | #include "packetcache.hh" | |
55 | #include "ws.hh" | |
56 | #include "misc.hh" | |
57 | #include "dynlistener.hh" | |
58 | #include "dynhandler.hh" | |
59 | #include "communicator.hh" | |
60 | #include "dnsproxy.hh" | |
731f58b8 | 61 | #include "utility.hh" |
12c86877 BH |
62 | #include "common_startup.hh" |
63 | ||
64 | time_t s_starttime; | |
65 | ||
66 | string s_programname="pdns"; // used in packethandler.cc | |
67 | ||
68 | char *funnytext= | |
69 | "*****************************************************************************\n"\ | |
70 | "Ok, you just ran pdns_server through 'strings' hoping to find funny messages.\n"\ | |
71 | "Well, you found one. \n"\ | |
72 | "Two ions are flying through their particle accelerator, says the one to the\n" | |
73 | "other 'I think I've lost an electron!' \n"\ | |
74 | "So the other one says, 'Are you sure?'. 'YEAH! I'M POSITIVE!'\n"\ | |
75 | " the pdns crew - pdns@powerdns.com\n" | |
76 | "*****************************************************************************\n"; | |
77 | ||
78 | ||
79 | // start (sys)logging | |
80 | ||
81 | /** \var Logger L | |
82 | \brief All logging is done via L, a Logger instance | |
83 | */ | |
84 | ||
85 | ||
86 | /** | |
87 | \file receiver.cc | |
88 | \brief The main loop of powerdns | |
89 | ||
90 | This file is where it all happens - main is here, as are the two pivotal threads qthread() and athread() | |
91 | */ | |
92 | ||
93 | void daemonize(void) | |
94 | { | |
95 | if(fork()) | |
96 | exit(0); // bye bye | |
97 | ||
98 | setsid(); | |
99 | ||
100 | // cleanup open fds, but skip sockets | |
101 | close(0); | |
102 | close(1); | |
103 | close(2); | |
104 | ||
105 | } | |
106 | ||
107 | ||
108 | static int cpid; | |
109 | ||
110 | static void takedown(int i) | |
111 | { | |
112 | if(cpid) { | |
113 | L<<Logger::Error<<"Guardian is killed, taking down children with us"<<endl; | |
114 | kill(cpid,SIGKILL); | |
115 | exit(1); | |
116 | } | |
117 | } | |
118 | ||
119 | static void writePid(void) | |
120 | { | |
121 | string fname=arg()["socket-dir"]+"/"+s_programname+".pid"; | |
122 | ofstream of(fname.c_str()); | |
123 | if(of) | |
124 | of<<getpid()<<endl; | |
125 | else | |
126 | L<<Logger::Error<<"Requested to write pid for "<<getpid()<<" to "<<fname<<" failed: "<<strerror(errno)<<endl; | |
127 | } | |
128 | ||
129 | int d_fd1[2], d_fd2[2]; | |
130 | FILE *d_fp; | |
131 | ||
132 | static string DLRestHandler(const vector<string>&parts, pid_t ppid) | |
133 | { | |
134 | string line; | |
135 | ||
136 | for(vector<string>::const_iterator i=parts.begin();i!=parts.end();++i) { | |
137 | if(i!=parts.begin()) | |
138 | line.append(1,' '); | |
139 | line.append(*i); | |
140 | } | |
141 | line.append(1,'\n'); | |
142 | ||
143 | write(d_fd1[1],line.c_str(),line.size()+1); | |
144 | char mesg[512]; | |
20ca8e7d BH |
145 | string response; |
146 | while(fgets(mesg,sizeof(mesg),d_fp)) { | |
147 | if(*mesg=='\n') | |
148 | break; | |
149 | response+=mesg; | |
150 | } | |
151 | chomp(response,"\n"); | |
152 | return response; | |
12c86877 BH |
153 | } |
154 | ||
155 | static string DLCycleHandler(const vector<string>&parts, pid_t ppid) | |
156 | { | |
36d772ab | 157 | kill(cpid,SIGKILL); // why? |
12c86877 BH |
158 | return "ok"; |
159 | } | |
160 | ||
161 | static int guardian(int argc, char **argv) | |
162 | { | |
163 | if(isGuarded(argv)) | |
164 | return 0; | |
165 | ||
166 | int infd=0, outfd=1; | |
167 | ||
168 | DynListener dlg(s_programname); | |
169 | dlg.registerFunc("QUIT",&DLQuitHandler); | |
170 | dlg.registerFunc("CYCLE",&DLCycleHandler); | |
171 | dlg.registerFunc("PING",&DLPingHandler); | |
172 | dlg.registerFunc("STATUS",&DLStatusHandler); | |
173 | dlg.registerRestFunc(&DLRestHandler); | |
174 | dlg.go(); | |
175 | string progname=argv[0]; | |
176 | ||
177 | bool first=true; | |
178 | cpid=0; | |
179 | ||
180 | for(;;) { | |
181 | int pid; | |
182 | setStatus("Launching child"); | |
183 | ||
184 | if(pipe(d_fd1)<0 || pipe(d_fd2)<0) { | |
185 | L<<Logger::Critical<<"Unable to open pipe for coprocess: "<<strerror(errno)<<endl; | |
186 | exit(1); | |
187 | } | |
188 | ||
189 | if(!(pid=fork())) { // child | |
190 | signal(SIGTERM, SIG_DFL); | |
191 | ||
192 | signal(SIGHUP, SIG_DFL); | |
193 | signal(SIGUSR1, SIG_DFL); | |
194 | signal(SIGUSR2, SIG_DFL); | |
195 | ||
196 | char **const newargv=new char*[argc+2]; | |
197 | int n; | |
198 | ||
199 | if(arg()["config-name"]!="") { | |
200 | progname+="-"+arg()["config-name"]; | |
201 | L<<Logger::Error<<"Virtual configuration name: "<<arg()["config-name"]<<endl; | |
202 | } | |
203 | ||
204 | newargv[0]=strdup(const_cast<char *>((progname+"-instance").c_str())); | |
205 | for(n=1;n<argc;n++) { | |
206 | newargv[n]=argv[n]; | |
207 | } | |
208 | newargv[n]=0; | |
209 | ||
210 | L<<Logger::Error<<"Guardian is launching an instance"<<endl; | |
211 | close(d_fd1[1]); | |
212 | close(d_fd2[0]); | |
213 | ||
214 | if(d_fd1[0]!= infd) { | |
215 | dup2(d_fd1[0], infd); | |
216 | close(d_fd1[0]); | |
217 | } | |
218 | ||
219 | if(d_fd2[1]!= outfd) { | |
220 | dup2(d_fd2[1], outfd); | |
221 | close(d_fd2[1]); | |
222 | } | |
223 | if(execv(argv[0], newargv)<0) { | |
224 | L<<Logger::Error<<"Unable to execv '"<<argv[0]<<"': "<<strerror(errno)<<endl; | |
225 | char **p=newargv; | |
226 | while(*p) | |
227 | L<<Logger::Error<<*p++<<endl; | |
228 | ||
229 | exit(1); | |
230 | } | |
231 | L<<Logger::Error<<"execve returned!!"<<endl; | |
232 | // never reached | |
233 | } | |
234 | else if(pid>0) { // parent | |
235 | close(d_fd1[0]); | |
236 | close(d_fd2[1]); | |
237 | if(!(d_fp=fdopen(d_fd2[0],"r"))) { | |
238 | L<<Logger::Critical<<"Unable to associate a file pointer with pipe: "<<stringerror()<<endl; | |
239 | exit(1); | |
240 | } | |
241 | setbuf(d_fp,0); // no buffering please, confuses select | |
242 | ||
243 | if(first) { | |
244 | first=false; | |
245 | signal(SIGTERM, takedown); | |
246 | ||
247 | signal(SIGHUP, SIG_IGN); | |
248 | signal(SIGUSR1, SIG_IGN); | |
249 | signal(SIGUSR2, SIG_IGN); | |
250 | ||
251 | writePid(); | |
252 | } | |
253 | ||
254 | int status; | |
255 | cpid=pid; | |
256 | for(;;) { | |
257 | int ret=waitpid(pid,&status,WNOHANG); | |
258 | ||
259 | if(ret<0) { | |
260 | L<<Logger::Error<<"In guardian loop, waitpid returned error: "<<strerror(errno)<<endl; | |
261 | L<<Logger::Error<<"Dying"<<endl; | |
262 | exit(1); | |
263 | } | |
264 | else if(ret) // something exited | |
265 | break; | |
266 | else { // child is alive | |
267 | // execute some kind of ping here | |
268 | if(DLQuitPlease()) | |
269 | takedown(1); | |
270 | setStatus("Child running on pid "+itoa(pid)); | |
271 | sleep(1); | |
272 | } | |
273 | } | |
274 | close(d_fd1[1]); | |
275 | fclose(d_fp); | |
276 | ||
277 | if(WIFEXITED(status)) { | |
278 | int ret=WEXITSTATUS(status); | |
279 | ||
280 | if(ret==99) { | |
281 | L<<Logger::Error<<"Child requested a stop, exiting"<<endl; | |
282 | exit(1); | |
283 | } | |
284 | setStatus("Child died with code "+itoa(ret)); | |
285 | L<<Logger::Error<<"Our pdns instance exited with code "<<ret<<endl; | |
286 | L<<Logger::Error<<"Respawning"<<endl; | |
287 | ||
288 | sleep(1); | |
289 | continue; | |
290 | } | |
291 | if(WIFSIGNALED(status)) { | |
292 | int sig=WTERMSIG(status); | |
293 | setStatus("Child died because of signal "+itoa(sig)); | |
294 | L<<Logger::Error<<"Our pdns instance ("<<pid<<") exited after signal "<<sig<<endl; | |
295 | #ifdef WCOREDUMP | |
296 | if(WCOREDUMP(status)) | |
297 | L<<Logger::Error<<"Dumped core"<<endl; | |
298 | #endif | |
299 | ||
300 | L<<Logger::Error<<"Respawning"<<endl; | |
301 | sleep(1); | |
302 | continue; | |
303 | } | |
304 | L<<Logger::Error<<"No clue what happened! Respawning"<<endl; | |
305 | } | |
306 | else { | |
307 | L<<Logger::Error<<"Unable to fork: "<<strerror(errno)<<endl; | |
308 | exit(1); | |
309 | } | |
310 | } | |
311 | } | |
312 | ||
313 | static void UNIX_declareArguments() | |
314 | { | |
315 | static char pietje[128]="!@@SYSCONFDIR@@:"; | |
316 | arg().set("config-dir","Location of configuration directory (pdns.conf)")= | |
317 | strcmp(pietje+1,"@@SYSCONFDIR@@:") ? pietje+strlen("@@SYSCONFDIR@@:")+1 : SYSCONFDIR; | |
318 | ||
319 | arg().set("config-name","Name of this virtual configuration - will rename the binary image")=""; | |
320 | arg().set("socket-dir","Where the controlsocket will live")=LOCALSTATEDIR; | |
731f58b8 | 321 | arg().set("module-dir","Default directory for modules")=LIBDIR; |
12c86877 | 322 | arg().set("chroot","If set, chroot to this directory for more security")=""; |
12c86877 BH |
323 | arg().set("logging-facility","Log under a specific facility")=""; |
324 | arg().set("daemon","Operate as a daemon")="no"; | |
325 | ||
326 | } | |
327 | ||
328 | static void loadModules() | |
329 | { | |
330 | if(!arg()["load-modules"].empty()) { | |
331 | vector<string>modules; | |
332 | ||
333 | stringtok(modules,arg()["load-modules"],","); | |
334 | ||
335 | for(vector<string>::const_iterator i=modules.begin();i!=modules.end();++i) { | |
336 | bool res; | |
337 | const string &module=*i; | |
338 | ||
339 | if(module.find(".")==string::npos) | |
340 | res=UeberBackend::loadmodule(arg()["module-dir"]+"/lib"+module+"backend.so"); | |
341 | else if(module[0]=='/' || (module[0]=='.' && module[1]=='/') || (module[0]=='.' && module[1]=='.')) // absolute or current path | |
342 | res=UeberBackend::loadmodule(module); | |
343 | else | |
344 | res=UeberBackend::loadmodule(arg()["module-dir"]+"/"+module); | |
345 | ||
346 | if(res==false) { | |
731f58b8 | 347 | L<<Logger::Error<<"receiver unable to load module "<<module<<endl; |
12c86877 BH |
348 | exit(1); |
349 | } | |
350 | } | |
351 | } | |
352 | } | |
353 | ||
354 | ||
355 | ||
356 | #ifdef __linux__ | |
357 | #include <execinfo.h> | |
358 | static void tbhandler(int num) | |
359 | { | |
360 | L<<Logger::Critical<<"Got a signal "<<num<<", attempting to print trace: "<<endl; | |
361 | void *array[20]; //only care about last 17 functions (3 taken with tracing support) | |
362 | size_t size; | |
363 | char **strings; | |
364 | size_t i; | |
365 | ||
366 | size = backtrace (array, 20); | |
367 | strings = backtrace_symbols (array, size); //Need -rdynamic gcc (linker) flag for this to work | |
368 | ||
369 | for (i = 0; i < size; i++) //skip useless functions | |
370 | L<<Logger::Error<<strings[i]<<endl; | |
371 | ||
372 | ||
373 | signal(SIGABRT, SIG_DFL); | |
374 | abort();//hopefully will give core | |
375 | ||
376 | } | |
377 | #endif | |
378 | ||
379 | //! The main function of pdns, the pdns process | |
380 | int main(int argc, char **argv) | |
381 | { | |
382 | s_programname="pdns"; | |
383 | s_starttime=time(0); | |
384 | ||
385 | #ifdef __linux__ | |
386 | signal(SIGSEGV,tbhandler); | |
387 | signal(SIGFPE,tbhandler); | |
388 | signal(SIGABRT,tbhandler); | |
389 | signal(SIGILL,tbhandler); | |
390 | #endif | |
391 | ||
392 | ||
393 | L.toConsole(Logger::Warning); | |
394 | try { | |
395 | declareArguments(); | |
396 | UNIX_declareArguments(); | |
397 | ||
398 | arg().laxParse(argc,argv); // do a lax parse | |
399 | ||
400 | if(arg()["config-name"]!="") | |
401 | s_programname+="-"+arg()["config-name"]; | |
402 | ||
403 | (void)theL(s_programname); | |
404 | ||
405 | string configname=arg()["config-dir"]+"/"+s_programname+".conf"; | |
406 | cleanSlashes(configname); | |
407 | ||
408 | if(!arg().mustDo("config") && !arg().mustDo("no-config")) // "config" == print a configuration file | |
409 | arg().laxFile(configname.c_str()); | |
410 | ||
411 | arg().laxParse(argc,argv); // reparse so the commandline still wins | |
412 | if(!arg()["logging-facility"].empty()) { | |
413 | int facility=arg().asNum("logging-facility"); | |
414 | switch(facility) { | |
415 | case 0: | |
416 | theL().setFacility(LOG_LOCAL0); | |
417 | break; | |
418 | case 1: | |
419 | theL().setFacility(LOG_LOCAL1); | |
420 | break; | |
421 | case 2: | |
422 | theL().setFacility(LOG_LOCAL2); | |
423 | break; | |
424 | case 3: | |
425 | theL().setFacility(LOG_LOCAL3); | |
426 | break; | |
427 | case 4: | |
428 | theL().setFacility(LOG_LOCAL4); | |
429 | break; | |
430 | case 5: | |
431 | theL().setFacility(LOG_LOCAL5); | |
432 | break; | |
433 | case 6: | |
434 | theL().setFacility(LOG_LOCAL6); | |
435 | break; | |
436 | case 7: | |
437 | theL().setFacility(LOG_LOCAL7); | |
438 | break; | |
439 | default: | |
440 | L<<Logger::Error<<"Unknown logging facility level"<<facility<<endl; | |
441 | break; | |
442 | } | |
443 | } | |
444 | ||
445 | L.toConsole((Logger::Urgency)(arg().asNum("loglevel"))); | |
446 | ||
447 | if(arg().mustDo("help") || arg().mustDo("config")) { | |
448 | arg().set("daemon")="no"; | |
449 | arg().set("guardian")="no"; | |
450 | } | |
451 | ||
452 | if(arg().mustDo("guardian") && !isGuarded(argv)) { | |
453 | if(arg().mustDo("daemon")) { | |
454 | L.toConsole(Logger::Critical); | |
455 | daemonize(); | |
456 | } | |
457 | guardian(argc, argv); | |
458 | // never get here, guardian will reinvoke process | |
459 | cerr<<"Um, we did get here!"<<endl; | |
460 | } | |
461 | ||
462 | // we really need to do work - either standalone or as an instance | |
463 | ||
464 | loadModules(); | |
465 | BackendMakers().launch(arg()["launch"]); // vrooooom! | |
466 | ||
467 | if(arg().mustDo("help")) { | |
468 | cerr<<"syntax:"<<endl<<endl; | |
469 | cerr<<arg().helpstring(arg()["help"])<<endl; | |
470 | exit(99); | |
471 | } | |
472 | ||
473 | if(arg().mustDo("config")) { | |
474 | cout<<arg().configstring()<<endl; | |
475 | exit(99); | |
476 | } | |
477 | ||
478 | if(arg().mustDo("list-modules")) { | |
479 | vector<string>modules=BackendMakers().getModules(); | |
480 | cerr<<"Modules available:"<<endl; | |
481 | for(vector<string>::const_iterator i=modules.begin();i!=modules.end();++i) | |
482 | cout<<*i<<endl; | |
483 | ||
484 | exit(99); | |
485 | } | |
486 | if(!BackendMakers().numLauncheable()) { | |
487 | L<<Logger::Error<<"Unable to launch, no backends configured for querying"<<endl; | |
488 | exit(99); // this isn't going to fix itself either | |
489 | } | |
490 | if(arg().mustDo("daemon")) { | |
491 | L.toConsole(Logger::None); | |
492 | if(!isGuarded(argv)) | |
493 | daemonize(); | |
494 | } | |
495 | ||
496 | if(isGuarded(argv)) { | |
497 | L<<Logger::Warning<<"This is a guarded instance of pdns"<<endl; | |
498 | dl=new DynListener; // listens on stdin | |
499 | } | |
500 | else { | |
501 | L<<Logger::Warning<<"This is a standalone pdns"<<endl; | |
502 | ||
503 | if(arg().mustDo("control-console")) | |
504 | dl=new DynListener(); | |
505 | else | |
506 | dl=new DynListener(s_programname); | |
507 | ||
508 | writePid(); | |
509 | } | |
510 | dl->registerFunc("SHOW",&DLShowHandler); | |
511 | dl->registerFunc("RPING",&DLPingHandler); | |
512 | dl->registerFunc("QUIT",&DLRQuitHandler); | |
513 | dl->registerFunc("UPTIME",&DLUptimeHandler); | |
514 | dl->registerFunc("NOTIFY-HOST",&DLNotifyHostHandler); | |
515 | dl->registerFunc("NOTIFY",&DLNotifyHandler); | |
516 | dl->registerFunc("RELOAD",&DLReloadHandler); | |
517 | dl->registerFunc("REDISCOVER",&DLRediscoverHandler); | |
518 | dl->registerFunc("VERSION",&DLVersionHandler); | |
519 | dl->registerFunc("PURGE",&DLPurgeHandler); | |
520 | dl->registerFunc("CCOUNTS",&DLCCHandler); | |
521 | dl->registerFunc("SET",&DLSettingsHandler); | |
ef1d2f44 | 522 | dl->registerFunc("RETRIEVE",&DLNotifyRetrieveHandler); |
12c86877 BH |
523 | |
524 | ||
525 | // reparse, with error checking | |
526 | if(!arg().mustDo("no-config")) | |
527 | arg().file(configname.c_str()); | |
528 | arg().parse(argc,argv); | |
529 | UeberBackend::go(); | |
530 | N=new UDPNameserver; // this fails when we are not root, throws exception | |
531 | ||
532 | if(!arg().mustDo("disable-tcp")) | |
533 | TN=new TCPNameserver; | |
534 | } | |
535 | catch(const ArgException &A) { | |
536 | L<<Logger::Error<<"Fatal error: "<<A.reason<<endl; | |
537 | exit(1); | |
538 | } | |
539 | ||
540 | declareStats(); | |
541 | DLOG(L<<Logger::Warning<<"Verbose logging in effect"<<endl); | |
542 | ||
8d022964 | 543 | L<<Logger::Warning<<"PowerDNS "<<VERSION<<" (C) 2001-2003 PowerDNS.COM BV ("<<__DATE__", "__TIME__<<") starting up"<<endl; |
12c86877 BH |
544 | |
545 | L<<Logger::Warning<<"PowerDNS comes with ABSOLUTELY NO WARRANTY. " | |
546 | "This is free software, and you are welcome to redistribute it " | |
547 | "according to the terms of the GPL version 2."<<endl; | |
548 | ||
549 | ||
550 | try { | |
551 | ||
552 | mainthread(); | |
553 | } | |
554 | catch(AhuException &AE) { | |
555 | if(!arg().mustDo("daemon")) | |
556 | cerr<<"Exiting because: "<<AE.reason<<endl; | |
557 | L<<Logger::Error<<"Exiting because: "<<AE.reason<<endl; | |
558 | } | |
559 | catch(exception &e) { | |
560 | if(!arg().mustDo("daemon")) | |
561 | cerr<<"Exiting because of STL error: "<<e.what()<<endl; | |
562 | L<<Logger::Error<<"Exiting because of STL error: "<<e.what()<<endl; | |
563 | } | |
564 | catch(...) { | |
565 | cerr<<"Uncaught exception of unknown type - sorry"<<endl; | |
566 | } | |
567 | ||
568 | exit(1); | |
569 | ||
570 | } | |
571 | ||
572 |