g++/final: don't add extra code to check for buffer overflows (-fno-stack-protector)
[senf.git] / senf / Utils / Daemon / Daemon.cc
1 // $Id$
2 //
3 // Copyright (C) 2007
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 //
6 // The contents of this file are subject to the Fraunhofer FOKUS Public License
7 // Version 1.0 (the "License"); you may not use this file except in compliance
8 // with the License. You may obtain a copy of the License at 
9 // http://senf.berlios.de/license.html
10 //
11 // The Fraunhofer FOKUS Public License Version 1.0 is based on, 
12 // but modifies the Mozilla Public License Version 1.1.
13 // See the full license text for the amendments.
14 //
15 // Software distributed under the License is distributed on an "AS IS" basis, 
16 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
17 // for the specific language governing rights and limitations under the License.
18 //
19 // The Original Code is Fraunhofer FOKUS code.
20 //
21 // The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V. 
22 // (registered association), Hansastraße 27 c, 80686 Munich, Germany.
23 // All Rights Reserved.
24 //
25 // Contributor(s):
26 //   Stefan Bund <g0dil@berlios.de>
27
28 /** \file
29     \brief Daemon non-inline non-template implementation */
30
31 #include "Daemon.hh"
32 #include "Daemon.ih"
33
34 // Custom includes
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/wait.h>
38 #include <unistd.h>
39 #include <limits.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <signal.h>
43 #ifdef SENF_BACKTRACE
44    #include <execinfo.h>
45 #endif
46 #include <sstream>
47 #include <algorithm>
48 #include <boost/algorithm/string/predicate.hpp>
49 #include <boost/algorithm/string/trim.hpp>
50 #include <boost/format.hpp>
51 #include <boost/bind.hpp>
52 #include <senf/Utils/Exception.hh>
53 #include <senf/Utils/membind.hh>
54 #include <senf/Utils/Backtrace.hh>
55 #include <senf/Utils/signalnames.hh>
56
57 // #define __USE_GNU
58 #include <ucontext.h>
59
60 //#include "Daemon.mpp"
61 #define prefix_
62 //-/////////////////////////////////////////////////////////////////////////////////////////////////
63
64 #define LIBC_CALL(fn, args) if (fn args < 0) \
65     SENF_THROW_SYSTEM_EXCEPTION(#fn "()")
66
67 #define LIBC_CALL_RV(var, fn, args) \
68     int var (fn args); if (var < 0) SENF_THROW_SYSTEM_EXCEPTION(#fn "()")
69
70 //-/////////////////////////////////////////////////////////////////////////////////////////////////
71 // senf::Daemon
72
73 prefix_ senf::Daemon::~Daemon()
74 {
75     if (pidfileCreated_) {
76         try {
77             LIBC_CALL( ::unlink, (pidfile_.c_str()) );
78         } catch (Exception & e) {
79             // e << "; could not unlink " << pidfile_.c_str();
80             // throw;
81         }
82     }
83 }
84
85 prefix_ void senf::Daemon::daemonize(bool v)
86 {
87     daemonize_ = v;
88 }
89
90 prefix_ bool senf::Daemon::daemon()
91 {
92     return daemonize_;
93 }
94
95 prefix_ int senf::Daemon::argc()
96 {
97     return argc_;
98 }
99
100 prefix_ char const ** senf::Daemon::argv()
101 {
102     return argv_;
103 }
104
105 namespace {
106
107     struct IsDaemonOpt {
108         bool operator()(std::string const & str) const {
109             return str == "--no-daemon"
110                 || boost::starts_with(str, std::string("--pid-file="))
111                 || boost::starts_with(str, std::string("--console-log="));
112         }
113     };
114 }
115
116 prefix_ void senf::Daemon::removeDaemonArgs()
117 {
118     char const ** last (std::remove_if(argv_+1, argv_+argc_, IsDaemonOpt()));
119     *last = 0;
120     argc_ = last - argv_;
121 }
122
123 prefix_ void senf::Daemon::consoleLog(std::string const & path, StdStream which)
124 {
125     switch (which) {
126     case StdOut : stdoutLog_ = path; break;
127     case StdErr : stderrLog_ = path; break;
128     case Both : stdoutLog_ = path; stderrLog_ = path; break;
129     }
130 }
131
132
133 prefix_ void senf::Daemon::openLog()
134 {
135     int fd (-1);
136     if (! stdoutLog_.empty()) {
137         fd = ::open(stdoutLog_.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0666);
138         if (fd < 0)
139             SENF_THROW_SYSTEM_EXCEPTION(
140                   " Could not open \"" + stdoutLog_ + "\" for redirecting stdout.");
141         stdout_ = fd;
142     }
143     if (! stderrLog_.empty()) {
144         if (stderrLog_ == stdoutLog_) {
145             stderr_ = ::dup(fd);
146             if (stderr_ < 0)
147                 SENF_THROW_SYSTEM_EXCEPTION("::dup()");
148         }
149         else {
150             fd = ::open(stdoutLog_.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0666);
151             if (fd < 0)
152                 SENF_THROW_SYSTEM_EXCEPTION(
153                     " Could not open \"" + stderrLog_ + "\" for redirecting stderr.");
154             stderr_ = fd;
155         }
156     }
157 }
158
159 prefix_ void senf::Daemon::logReopen()
160 {
161     if (! stdoutLog_.empty()) {
162         int fd (::open(stdoutLog_.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0666));
163         if (fd < 0)
164             goto error;
165         if (::dup2(fd, 1) < 0)
166             goto error;
167         if (stderrLog_ == stdoutLog_) {
168             if (::dup2(fd, 2) < 0)
169                 goto error;
170             return;
171         }
172     }
173     if (! stderrLog_.empty()) {
174         int fd (::open(stderrLog_.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0666));
175         if (fd < 0)
176             goto error;
177         if (::dup2(fd, 2) < 0)
178             goto error;
179     }
180     return;
181
182  error:
183     SENF_LOG(
184         (senf::log::CRITICAL)
185         ("log-file reopen failed: (" << errno << ") " << ::strerror(errno)) );
186 }
187
188 prefix_ void senf::Daemon::pidFile(std::string const & f)
189 {
190     pidfile_ = f;
191 }
192
193 namespace {
194     bool signaled (false);
195     void waitusr(int) {
196         signaled = true;
197     }
198 }
199
200 prefix_ void senf::Daemon::detach()
201 {
202     if (daemonize_ && ! detached_) {
203         // Wow .. ouch ..
204         // To ensure all data is written to the console log file in the correct order, we suspend
205         // execution here until the parent process tells us to continue via SIGUSR1: We block
206         // SIGUSR1 and install our own signal handler saving the old handler and signal mask. Then
207         // we close stdin/stderr which will send a HUP condition to the parent process. We wait for
208         // SIGUSR1 and reinstall the old signal mask and action.
209         ::sigset_t oldsig;
210         ::sigset_t usrsig;
211         ::sigemptyset(&usrsig);
212         LIBC_CALL( ::sigaddset, (&usrsig, SIGUSR1) );
213         LIBC_CALL( ::sigprocmask, (SIG_BLOCK, &usrsig, &oldsig) );
214         struct ::sigaction oldact;
215         struct ::sigaction usract;
216         ::memset(&usract, 0, sizeof(usract));
217         usract.sa_handler = &waitusr;
218         LIBC_CALL( ::sigaction, (SIGUSR1, &usract, &oldact) );
219         ::sigset_t waitsig (oldsig);
220         LIBC_CALL( ::sigdelset, (&waitsig, SIGUSR1) );
221
222         LIBC_CALL_RV( nul, ::open, ("/dev/null", O_WRONLY) );
223         LIBC_CALL( ::dup2, (stdout_ == -1 ? nul : stdout_, 1) );
224         LIBC_CALL( ::dup2, (stderr_ == -1 ? nul : stderr_, 2) );
225         LIBC_CALL( ::close, (nul) );
226
227         signaled = false;
228         while (! signaled) {
229             ::sigsuspend(&waitsig);
230             if (errno != EINTR)
231                 SENF_THROW_SYSTEM_EXCEPTION("::sigsuspend()");
232         }
233
234         LIBC_CALL( ::sigaction, (SIGUSR1, &oldact, 0) );
235         LIBC_CALL( ::sigprocmask, (SIG_SETMASK, &oldsig, 0) );
236
237         detached_ = true;
238     }
239 }
240
241 namespace {
242     /* Purposely *not* derived from std::exception */
243     struct DaemonExitException {
244         DaemonExitException(unsigned c) : code(c) {}
245         unsigned code;
246     };
247 }
248
249 prefix_ void senf::Daemon::exit(unsigned code)
250 {
251     throw DaemonExitException(code);
252 }
253
254 prefix_ int senf::Daemon::start(int argc, char const ** argv)
255 {
256     argc_ = argc;
257     argv_ = argv;
258
259     try {
260         configure();
261
262         if (daemonize_) {
263             openLog();
264             fork();
265         }
266         installSighandlers();
267         if (! pidfile_.empty()) {
268             if (pidfileCreate())
269                 pidfileCreated_ = true;
270             else {
271                 std::cerr << "PID file '" << pidfile_
272                           << "' creation failed. Daemon running ?" << std::endl;
273                 return 1;
274             }
275         }
276
277         main();
278     }
279     catch (DaemonExitException & e) {
280         return e.code;
281     }
282
283 #ifndef SENF_DEBUG
284
285     catch (std::exception & e) {
286         std::cerr << "\n*** Fatal exception: " << e.what() << "\n" << std::endl;
287         return 1;
288     }
289     catch (...) {
290         std::cerr << "\n*** Fatal exception: (unknown)" << "\n" << std::endl;
291         return 1;
292     }
293
294 #   endif
295
296     return 0;
297 }
298
299 prefix_ senf::Daemon & senf::Daemon::instance()
300 {
301     BOOST_ASSERT( instance_ );
302     return *instance_;
303 }
304
305 //-/////////////////////////////////////////////////////////////////////////////////////////////////
306 // protected members
307
308 prefix_ senf::Daemon::Daemon()
309     : argc_(0), argv_(0), daemonize_(true), stdout_(-1), stderr_(-1), pidfile_(""),
310       pidfileCreated_(false), detached_(false)
311 {
312     BOOST_ASSERT( ! instance_ );
313     instance_ = this;
314 }
315
316 senf::Daemon * senf::Daemon::instance_ (0);
317
318 //-/////////////////////////////////////////////////////////////////////////////////////////////////
319 // private members
320
321 prefix_ void senf::Daemon::configure()
322 {
323     // int i (not unsigned) since argc_ is int ...
324     for (int i (1); i<argc_; ++i) {
325         std::string argv (argv_[i]);
326         if (argv == "--no-daemon")
327             daemonize(false);
328         else if (boost::starts_with(argv, "--console-log=")) {
329             std::string arg (argv.substr(14u));
330             std::string::size_type komma (arg.find(','));
331             if (komma == std::string::npos) {
332                 boost::trim(arg);
333                 if (arg == std::string("none")) consoleLog("");
334                 else if (!arg.empty())          consoleLog(arg);
335             } else {
336                 std::string arg1 (arg,0,komma);
337                 std::string arg2 (arg,komma+1);
338                 boost::trim(arg1);
339                 boost::trim(arg2);
340                 if (arg1 == std::string("none")) consoleLog("",StdOut);
341                 else if (! arg1.empty() )        consoleLog(arg1, StdOut);
342                 if (arg2 == std::string("none")) consoleLog("",StdErr);
343                 else if (! arg2.empty() )        consoleLog(arg2, StdErr);
344             }
345         }
346         else if (boost::starts_with(argv, "--pid-file="))
347             pidFile(std::string(std::string(argv_[i]), 11u));
348     }
349 }
350
351 prefix_ void senf::Daemon::main()
352 {
353     init();
354     detach();
355     run();
356 }
357
358 prefix_ void senf::Daemon::init()
359 {}
360
361 prefix_ void senf::Daemon::run()
362 {}
363
364 prefix_ void senf::Daemon::fork()
365 {
366     int coutpipe[2];
367     int cerrpipe[2];
368
369     LIBC_CALL_RV( nul, ::open, ("/dev/null", O_RDONLY) );
370     LIBC_CALL( ::dup2, (nul, 0) );
371     LIBC_CALL( ::close, (nul) );
372     LIBC_CALL( ::pipe, (coutpipe) );
373     LIBC_CALL( ::pipe, (cerrpipe) );
374
375     // We need to block the SIGCHLD signal here so we don't miss it, if the child
376     // dies immediately
377     ::sigset_t oldsig;
378     ::sigset_t cldsig;
379     ::sigemptyset(&cldsig);
380     LIBC_CALL( ::sigaddset, (&cldsig, SIGCHLD) );
381     LIBC_CALL( ::sigprocmask, (SIG_BLOCK, &cldsig, &oldsig) );
382
383     if (! senf::scheduler::empty() ) {
384         std::cerr <<
385             "\n"
386             "*** WARNING ***\n"
387             "Scheduler not empty before fork(). THIS MUST NOT HAPPEN.\n"
388             "The scheduler will be reinitialized by the fork() and lose all registrations.\n\n";
389         senf::scheduler::detail::EventManager::instance().listEvents(std::cerr);
390         std::cerr <<
391             "\n*** WARNING ***\n"
392             "\n";
393     }
394
395     LIBC_CALL_RV( pid, ::fork, () );
396
397     if (pid == 0) {
398         // Daemon process
399
400         LIBC_CALL( ::dup2, (coutpipe[1],1) );
401         LIBC_CALL( ::dup2, (cerrpipe[1],2) );
402         LIBC_CALL( ::close, (coutpipe[0]) );
403         LIBC_CALL( ::close, (coutpipe[1]) );
404         LIBC_CALL( ::close, (cerrpipe[0]) );
405         LIBC_CALL( ::close, (cerrpipe[1]) );
406         LIBC_CALL( ::setsid, () );
407         LIBC_CALL( ::sigprocmask, (SIG_SETMASK, &oldsig, 0) );
408
409         senf::scheduler::restart();
410         return;
411     }
412
413     // Ouch ... ensure, the daemon watcher does not remove the pidfile ...
414     pidfile_ = "";
415
416     LIBC_CALL( ::close, (coutpipe[1]) );
417     LIBC_CALL( ::close, (cerrpipe[1]) );
418
419     senf::scheduler::restart();
420
421     detail::DaemonWatcher watcher (pid, coutpipe[0], cerrpipe[0], stdout_, stderr_);
422     watcher.run();
423
424     ::_exit(0);
425 }
426
427 prefix_ bool senf::Daemon::pidfileCreate()
428 {
429     // Create temporary file pidfile_.hostname.pid and hard-link it to pidfile_ If the hardlink
430     // fails, the pidfile exists. If the link count of the temporary file is not 2 after this, there
431     // was some race condition, probably over NFS.
432
433     std::string tempname;
434     boost::format linkErrorFormat(" Could not link \"%1%\" to \"%2%\".");
435
436     {
437         char hostname[HOST_NAME_MAX+1];
438         LIBC_CALL( ::gethostname, (hostname, HOST_NAME_MAX+1) );
439         hostname[HOST_NAME_MAX] = 0;
440         std::stringstream tempname_s;
441         tempname_s << pidfile_ << "." << hostname << "." << ::getpid();
442         tempname = tempname_s.str();
443     }
444
445     while (1) {
446         {
447             std::ofstream pidf (tempname.c_str());
448             if (! pidf)
449                 SENF_THROW_SYSTEM_EXCEPTION(" Could not open pidfile \"" + tempname + "\" for output.");
450             pidf << ::getpid() << std::endl;
451             if (! pidf)
452                 SENF_THROW_SYSTEM_EXCEPTION(" Could not write to pidfile \"" + tempname + "\".");
453         }
454
455         if (::link(tempname.c_str(), pidfile_.c_str()) < 0) {
456             if (errno != EEXIST)
457                 SENF_THROW_SYSTEM_EXCEPTION((linkErrorFormat % pidfile_ % tempname).str());
458         }
459         else {
460             struct ::stat s;
461             LIBC_CALL( ::stat, (tempname.c_str(), &s) );
462             LIBC_CALL( ::unlink, (tempname.c_str()) );
463             return s.st_nlink == 2;
464         }
465
466         // pidfile exists. Check, whether the pid in the pidfile still exists.
467         {
468             int old_pid (-1);
469             std::ifstream pidf (pidfile_.c_str());
470             if ( ! (pidf >> old_pid)
471                  || old_pid < 0
472                  || ::kill(old_pid, 0) >= 0
473                  || errno == EPERM ) {
474                 LIBC_CALL( ::unlink, (tempname.c_str()) );
475                 return false;
476             }
477         }
478
479         // If we reach this point, the pid file exists but the process mentioned within the
480         // pid file does *not* exists. We assume, the pid file to be stale.
481
482         // I hope, the following procedure is without race condition: We remove our generated
483         // temporary pid file and recreate it as hard-link to the old pid file. Now we check, that
484         // the hard-link count of this file is 2. If it is not, we terminate, since someone else
485         // must have already created his hardlink. We then truncate the file and write our pid.
486
487         LIBC_CALL( ::unlink, (tempname.c_str() ));
488         if (::link(pidfile_.c_str(), tempname.c_str()) < 0) {
489             if (errno != ENOENT)
490                 SENF_THROW_SYSTEM_EXCEPTION( (linkErrorFormat % tempname % pidfile_).str());
491             // Hmm ... the pidfile mysteriously disappeared ... try again.
492             continue;
493         }
494
495         {
496             struct ::stat s;
497             LIBC_CALL( ::stat, (tempname.c_str(), &s) );
498             if (s.st_nlink != 2) {
499                 LIBC_CALL( ::unlink, (tempname.c_str()) );
500                 return false;
501             }
502         }
503
504         {
505             std::ofstream pidf (tempname.c_str());
506             pidf << ::getpid() << std::endl;
507         }
508
509         LIBC_CALL( ::unlink, (tempname.c_str()) );
510         break;
511     }
512     return true;
513 }
514
515
516 #ifdef SENF_DEBUG
517
518 namespace {
519     void fatalSignalsHandler(int sig, ::siginfo_t * info, void * arg)
520     {
521         // ::ucontext_t * ucontext = static_cast<ucontext_t*>(arg);
522         std::cerr << "\n" << "Signal " << senf::signalName(sig) << '(' << sig << ')'
523                   << " received\n";
524
525         if (sig == SIGSEGV)
526             std::cerr << "Invalid memory access at " << info->si_addr << "\n";
527 #ifdef SENF_BACKTRACE
528         static void * entries[SENF_DEBUG_BACKTRACE_NUMCALLERS];
529         int nEntries( ::backtrace(entries, SENF_DEBUG_BACKTRACE_NUMCALLERS) );
530
531         // Hack the callers address into the backtrace
532         // entries[1] = reinterpret_cast<void *>(ucontext->uc_mcontext.gregs[REG_EIP]);
533
534         std::cerr << "Backtrace:\n";
535         senf::formatBacktrace(std::cerr, entries, nEntries);
536         std::cerr << "-- \n";
537 #endif //SENF_BACKTRACE
538         if (sig != SIGUSR2) {
539             ::signal(sig, SIG_DFL);
540             ::kill(::getpid(), sig);
541         }
542     }
543
544 }
545
546 #endif // SENF_DEBUG
547
548 namespace {
549     void sighupHandler(int sig)
550     {
551         senf::Daemon::instance().logReopen();
552     }
553 }
554
555 prefix_ void senf::Daemon::installSighandlers()
556 {
557     struct ::sigaction sa;
558
559     ::sigemptyset(&sa.sa_mask);
560     sa.sa_handler = &sighupHandler;
561     sa.sa_flags = SA_RESTART;
562
563     ::sigaction(SIGHUP,   &sa, NULL);
564
565     sa.sa_handler = SIG_IGN;
566     ::sigaction(SIGPIPE, &sa, NULL);
567
568 #ifdef SENF_DEBUG
569     sa.sa_sigaction = &fatalSignalsHandler;
570     sa.sa_flags = SA_RESTART | SA_SIGINFO;
571
572     ::sigaction(SIGILL,    &sa, NULL);
573     ::sigaction(SIGTRAP,   &sa, NULL);
574     ::sigaction(SIGABRT,   &sa, NULL);
575     ::sigaction(SIGFPE,    &sa, NULL);
576     ::sigaction(SIGBUS,    &sa, NULL);
577     ::sigaction(SIGSEGV,   &sa, NULL);
578 #ifdef SIGSTKFLT //SIGSTKFLT is used for stack faults on coprocessors. That condition doesn't exist on MIPS
579     ::sigaction(SIGSTKFLT, &sa, NULL);
580 #endif
581     ::sigaction(SIGSYS,    &sa, NULL);
582     ::sigaction(SIGUSR2,   &sa, NULL);
583 #endif
584 }
585
586 //-/////////////////////////////////////////////////////////////////////////////////////////////////
587 // senf::detail::DaemonWatcher
588
589 prefix_ senf::detail::DaemonWatcher::DaemonWatcher(int pid, int coutpipe, int cerrpipe,
590                                                    int stdout, int stderr)
591     : childPid_(pid), coutpipe_(coutpipe), cerrpipe_(cerrpipe), stdout_(stdout),
592       stderr_(stderr), sigChld_(false),
593       cldSignal_ (SIGCHLD, senf::membind(&DaemonWatcher::sigChld, this)),
594       timer_ ("senf::detail::DaemonWatcher::childOk", senf::membind(&DaemonWatcher::childOk, this)),
595       coutForwarder_(coutpipe_, boost::bind(&DaemonWatcher::pipeClosed, this, 1)),
596       cerrForwarder_(cerrpipe_, boost::bind(&DaemonWatcher::pipeClosed, this, 2))
597 {
598     coutForwarder_.addTarget(1);
599     if (stdout_ >= 0)
600         coutForwarder_.addTarget(stdout_);
601     cerrForwarder_.addTarget(2);
602     if (stderr_ >= 0)
603         cerrForwarder_.addTarget(stderr_);
604 }
605
606 prefix_ void senf::detail::DaemonWatcher::run()
607 {
608     scheduler::process();
609 }
610
611 //-/////////////////////////////////////////////////////////////////////////////////////////////////
612 // private members
613
614 prefix_ void senf::detail::DaemonWatcher::pipeClosed(int id)
615 {
616     switch (id) {
617     case 1 : coutpipe_ = -1; break;
618     case 2 : cerrpipe_ = -1; break;
619     }
620
621     if (coutpipe_ == -1 && cerrpipe_ == -1) {
622         if (sigChld_)
623             childDied(); // does not return
624         if (::kill(childPid_, SIGUSR1) < 0 && errno != ESRCH)
625             SENF_THROW_SYSTEM_EXCEPTION("::kill()");
626         timer_.timeout(scheduler::eventTime() + ClockService::seconds(1));
627     }
628 }
629
630 prefix_ void senf::detail::DaemonWatcher::sigChld(siginfo_t const &)
631 {
632     sigChld_ = true;
633     if (coutpipe_ == -1 && cerrpipe_ == -1)
634         childDied(); // does not return
635 }
636
637 prefix_ void senf::detail::DaemonWatcher::childDied()
638 {
639     int status (0);
640     if (::waitpid(childPid_,&status,0) < 0) SENF_THROW_SYSTEM_EXCEPTION("::waitpid()");
641     if (WIFSIGNALED(status)) {
642         ::signal(WTERMSIG(status),SIG_DFL);
643         ::kill(::getpid(), WTERMSIG(status));
644         // should not be reached
645         ::_exit(126);
646     }
647     if (WEXITSTATUS(status) == 0)
648         ::_exit(127);
649     ::_exit(WEXITSTATUS(status));
650 }
651
652 prefix_ void senf::detail::DaemonWatcher::childOk()
653 {
654     scheduler::terminate();
655 }
656
657 //-/////////////////////////////////////////////////////////////////////////////////////////////////
658 // senf::detail::DaemonWatcher::Forwarder
659
660 prefix_ senf::detail::DaemonWatcher::Forwarder::Target::Target(Forwarder & fwd, int fd_)
661     : fd (fd_), offset (0),
662       writeevent ("senf::detail::DaemonWatcher::Forwarder::Target::writeevent",
663                   boost::bind(&Forwarder::writeData, &fwd, _1, this),
664                   fd, scheduler::FdEvent::EV_WRITE, false)
665 {}
666
667 prefix_ senf::detail::DaemonWatcher::Forwarder::Forwarder(int src, Callback cb)
668     : src_(src), cb_(cb),
669       readevent_("senf::detail::DaemonWatcher::Forwarder::readevent", senf::membind(&Forwarder::readData, this),
670                  src_, scheduler::FdEvent::EV_READ)
671 {}
672
673 prefix_ senf::detail::DaemonWatcher::Forwarder::~Forwarder()
674 {
675     targets_.clear_and_destroy(DestroyDelete());
676 }
677
678 prefix_ void senf::detail::DaemonWatcher::Forwarder::addTarget(int fd)
679 {
680     targets_.push_back(*(new Target(*this, fd)));
681 }
682
683 prefix_ void senf::detail::DaemonWatcher::Forwarder::readData(int event)
684 {
685     char buf[1024];
686     int n (0);
687
688     while (1) {
689         n = ::read(src_,buf,1024);
690         if (n<0) {
691             if (errno != EINTR)
692                 SENF_THROW_SYSTEM_EXCEPTION("::read()");
693         }
694         else
695             break;
696     }
697
698     if (n == 0) {
699         if (buffer_.empty())
700             cb_();
701         src_ = -1;
702         readevent_.disable();
703         return;
704     }
705
706     if (targets_.empty())
707         return;
708
709     for (Targets::iterator i (targets_.begin()); i != targets_.end(); ++i)
710         if (i->offset >= buffer_.size())
711             i->writeevent.enable();
712
713     buffer_.insert(buffer_.end(), buf, buf+n);
714 }
715
716 prefix_ void senf::detail::DaemonWatcher::Forwarder::writeData(int event, Target * target)
717 {
718     if (event != scheduler::FdEvent::EV_WRITE) {
719         // Broken pipe while writing data ? Not much, we can do here, we just drop the data
720         targets_.erase_and_destroy(Targets::current(*target),DestroyDelete());
721         if (targets_.empty() && src_ == -1)
722             cb_();
723         return;
724     }
725
726     char buf[1024];
727     int n (buffer_.size() - target->offset > 1024 ? 1024 : buffer_.size() - target->offset);
728     std::copy(buffer_.begin() + target->offset, buffer_.begin() + target->offset + n, buf);
729
730     int w (::write(target->fd, buf, n));
731     if (w < 0) {
732         if (errno != EINTR) SENF_THROW_SYSTEM_EXCEPTION("::write()");
733         return;
734     }
735     target->offset += w;
736
737     n = std::min_element(
738         targets_.begin(), targets_.end(),
739         boost::bind(&Target::offset, _1) < boost::bind(&Target::offset, _2))->offset;
740
741     buffer_.erase(buffer_.begin(), buffer_.begin()+n);
742
743     for (Targets::iterator i (targets_.begin()); i != targets_.end(); ++i)
744         i->offset -= n;
745
746     if (target->offset >= buffer_.size())
747         target->writeevent.disable();
748     if (src_ == -1 && (buffer_.empty() || targets_.empty()))
749         cb_();
750 }
751
752 #undef LIBC_CALL
753 #undef LIBC_CALL_RV
754
755 //-/////////////////////////////////////////////////////////////////////////////////////////////////
756 #undef prefix_
757 //#include "Daemon.mpp"
758
759 \f
760 // Local Variables:
761 // mode: c++
762 // fill-column: 100
763 // comment-column: 40
764 // c-file-style: "senf"
765 // indent-tabs-mode: nil
766 // ispell-local-dictionary: "american"
767 // compile-command: "scons -u test"
768 // End: