11 #include "cvmfs_config.h"
19 #include <sys/resource.h>
20 #include <sys/types.h>
22 #include <sys/ucontext.h>
41 #if defined(CVMFS_FUSE_MODULE)
52 #if defined(CVMFS_FUSE_MODULE)
78 const bool retrievable =
true;
80 result +=
"failed to re-gain root permissions... still give it a try\n";
91 const bool double_fork =
false;
107 ReadUntilGdbPrompt(fd_stdout);
111 const string gdb_cmd =
"bt all\n" "quit\n";
113 const string gdb_cmd =
"thread apply all bt\n" "quit\n";
117 ssize_t nbytes = write(fd_stdin, gdb_cmd.data(), gdb_cmd.length());
118 if ((nbytes < 0) || (
static_cast<unsigned>(nbytes) != gdb_cmd.length())) {
119 result +=
"failed to start gdb/lldb (" +
StringifyInt(nbytes) +
" bytes "
127 result += ReadUntilGdbPrompt(fd_stdout);
129 result += ReadUntilGdbPrompt(fd_stdout) +
"\n\n";
135 while (read(fd_stderr, &cbuf, 1) == 1)
136 result_err.push_back(cbuf);
137 if (!result_err.empty())
138 result +=
"\nError output:\n" + result_err +
"\n";
146 unsigned int timeout = 15;
148 while (timeout > 0 && waitpid(gdb_pid, &statloc, WNOHANG) != gdb_pid) {
155 result +=
"gdb did not exit as expected. sending SIGKILL... ";
156 result += (kill(gdb_pid, SIGKILL) != 0) ?
"failed\n" :
"okay\n";
164 if (instance_ != NULL) {
165 return instance_->watchdog_pid_;
175 char ctime_buffer[32];
177 if (!crash_dump_path_.empty()) {
178 FILE *fp = fopen(crash_dump_path_.c_str(),
"a");
180 time_t now = time(NULL);
181 msg +=
"\nTimestamp: " + string(ctime_r(&now, ctime_buffer));
182 if (fwrite(&msg[0], 1, msg.length(), fp) != msg.length()) {
184 " (failed to report into crash dump file " + crash_dump_path_ +
")";
186 msg +=
"\n Crash logged also on file: " + crash_dump_path_ +
"\n";
190 msg +=
" (failed to open crash dump file " + crash_dump_path_ +
")";
205 static const string gdb_prompt =
"(lldb)";
207 static const string gdb_prompt =
"\n(gdb) ";
213 unsigned int ring_buffer_pos = 0;
217 chars_io = read(fd_pipe, &mini_buffer, 1);
220 if (chars_io <= 0)
break;
222 result += mini_buffer;
225 if (mini_buffer == gdb_prompt[ring_buffer_pos]) {
227 if (ring_buffer_pos == gdb_prompt.size()) {
244 if (!pipe_watchdog_->TryRead<
CrashData>(&crash_data)) {
245 return "failed to read crash data (" +
StringifyInt(errno) +
")";
248 string debug =
"--\n";
251 debug +=
", version: " + string(VERSION);
253 debug +=
"Executable path: " + exe_path_ +
"\n";
255 debug += GenerateStackTrace(crash_data.
pid);
258 if (kill(crash_data.
pid, SIGKILL) != 0) {
259 debug +=
"Failed to kill cvmfs client! (";
262 debug +=
"invalid signal";
265 debug +=
"permission denied";
268 debug +=
"no such process";
281 int sig, siginfo_t *siginfo,
void * )
284 "watchdog: received unexpected signal %d from PID %d / UID %d",
285 sig, siginfo->si_pid, siginfo->si_uid);
291 int send_errno = errno;
299 (void) sigaction(SIGQUIT, &(Me()->old_signal_handlers_[sig]), NULL);
302 if (!Me()->pipe_watchdog_->Write(ControlFlow::kProduceStacktrace)) {
310 crash_data.
pid = getpid();
311 if (!Me()->pipe_watchdog_->Write<
CrashData>(crash_data)) {
321 if (++counter == 300) {
324 #if defined(CVMFS_FUSE_MODULE)
327 void *addr[kMaxBacktrace];
331 int num_addr = backtrace(addr, kMaxBacktrace);
332 char **symbols = backtrace_symbols(addr, num_addr);
333 string backtrace =
"Backtrace (" +
StringifyInt(num_addr) +
335 for (
int i = 0; i < num_addr; ++i)
336 backtrace +=
string(symbols[i]) +
"\n";
361 SigactionMap::const_iterator i = signal_handlers.begin();
362 SigactionMap::const_iterator iend = signal_handlers.end();
363 for (; i != iend; ++i) {
364 struct sigaction old_signal_handler;
365 if (sigaction(i->first, &i->second, &old_signal_handler) != 0) {
368 old_signal_handlers[i->first] = old_signal_handler;
371 return old_signal_handlers;
385 switch (pid = fork()) {
386 case -1:
PANIC(NULL);
392 pipe_watchdog_->CloseWriteFd();
395 pid_t watchdog_pid = getpid();
396 pipe_pid.
Write(watchdog_pid);
411 std::set<int> preserve_fds;
412 preserve_fds.insert(0);
413 preserve_fds.insert(1);
414 preserve_fds.insert(2);
415 preserve_fds.insert(pipe_watchdog_->GetReadFd());
416 preserve_fds.insert(pipe_listener_->
GetWriteFd());
421 if (WaitForSupervisee())
424 pipe_watchdog_->CloseReadFd();
432 pipe_watchdog_->CloseReadFd();
435 if (waitpid(pid, &statloc, 0) != pid)
PANIC(NULL);
436 if (!WIFEXITED(statloc) || WEXITSTATUS(statloc))
PANIC(NULL);
440 pipe_pid.
Read(&watchdog_pid_);
449 assert(rv_sig != SIG_ERR);
453 memset(&sa, 0,
sizeof(sa));
454 sa.sa_sigaction = ReportSignalAndTerminate;
455 sa.sa_flags = SA_SIGINFO;
456 sigfillset(&sa.sa_mask);
459 signal_handlers[SIGHUP] = sa;
460 signal_handlers[SIGINT] = sa;
461 signal_handlers[SIGQUIT] = sa;
462 signal_handlers[SIGILL] = sa;
463 signal_handlers[SIGABRT] = sa;
464 signal_handlers[SIGBUS] = sa;
465 signal_handlers[SIGFPE] = sa;
466 signal_handlers[SIGUSR1] = sa;
467 signal_handlers[SIGSEGV] = sa;
468 signal_handlers[SIGUSR2] = sa;
469 signal_handlers[SIGTERM] = sa;
470 signal_handlers[SIGXCPU] = sa;
471 signal_handlers[SIGXFSZ] = sa;
472 SetSignalHandlers(signal_handlers);
476 if (!pipe_watchdog_->TryRead(&control_flow)) {
481 switch (control_flow) {
482 case ControlFlow::kQuit:
484 case ControlFlow::kSupervise:
487 LogEmergency(
"Internal error: invalid control flow");
492 pipe_watchdog_->Read(&size);
493 crash_dump_path_.resize(size);
495 pipe_watchdog_->Read(&crash_dump_path_[0], size);
499 LogEmergency(std::string(
"Cannot change to crash dump directory: ") +
515 "failed to allow ptrace() for watchdog (PID: %d). "
516 "Post crash stacktrace might not work",
521 int stack_size = kSignalHandlerStacksize;
522 sighandler_stack_.ss_sp = smalloc(stack_size);
523 sighandler_stack_.ss_size = stack_size;
524 sighandler_stack_.ss_flags = 0;
525 if (sigaltstack(&sighandler_stack_, NULL) != 0)
530 memset(&sa, 0,
sizeof(sa));
531 sa.sa_sigaction = SendTrace;
532 sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
533 sigfillset(&sa.sa_mask);
536 signal_handlers[SIGQUIT] = sa;
537 signal_handlers[SIGILL] = sa;
538 signal_handlers[SIGABRT] = sa;
539 signal_handlers[SIGFPE] = sa;
540 signal_handlers[SIGSEGV] = sa;
541 signal_handlers[SIGBUS] = sa;
542 signal_handlers[SIGPIPE] = sa;
543 signal_handlers[SIGXFSZ] = sa;
544 old_signal_handlers_ = SetSignalHandlers(signal_handlers);
551 pipe_watchdog_->Write(ControlFlow::kSupervise);
552 size_t path_size = crash_dump_path.size();
553 pipe_watchdog_->Write(path_size);
555 pipe_watchdog_->Write(crash_dump_path.data(), path_size);
566 struct pollfd watch_fds[2];
568 watch_fds[0].events = 0;
569 watch_fds[0].revents = 0;
571 watch_fds[1].events = POLLIN | POLLPRI;
572 watch_fds[1].revents = 0;
574 int retval = poll(watch_fds, 2, -1);
580 if (watch_fds[1].revents)
583 if (watch_fds[0].revents) {
584 if ((watch_fds[0].revents & POLLERR) ||
585 (watch_fds[0].revents & POLLHUP) ||
586 (watch_fds[0].revents & POLLNVAL))
589 "watchdog disappeared, disabling stack trace reporting "
590 "(revents: %d / %d|%d|%d)",
591 watch_fds[0].revents, POLLERR, POLLHUP, POLLNVAL);
608 LogEmergency(
"watchdog: unexpected termination (" +
610 if (on_crash_) on_crash_();
612 switch (control_flow) {
613 case ControlFlow::kProduceStacktrace:
614 LogEmergency(ReportStacktrace());
615 if (on_crash_) on_crash_();
618 case ControlFlow::kQuit:
622 LogEmergency(
"watchdog: unexpected error");
633 , on_crash_(on_crash)
644 signal(SIGQUIT, SIG_DFL);
645 signal(SIGILL, SIG_DFL);
646 signal(SIGABRT, SIG_DFL);
647 signal(SIGFPE, SIG_DFL);
648 signal(SIGSEGV, SIG_DFL);
649 signal(SIGBUS, SIG_DFL);
650 signal(SIGPIPE, SIG_DFL);
651 signal(SIGXFSZ, SIG_DFL);
#define LogCvmfs(source, mask,...)
bool Write(const T &data)
std::map< int, struct sigaction > SigactionMap
std::string GenerateStackTrace(pid_t pid)
std::string ReportStacktrace()
UniquePtr< Pipe< kPipeThreadTerminator > > pipe_terminate_
Send the terminate signal to the listener.
NameString GetFileName(const PathString &path)
static void ReportSignalAndTerminate(int sig, siginfo_t *siginfo, void *context)
SigactionMap old_signal_handlers_
static void SendTrace(int sig, siginfo_t *siginfo, void *context)
static void * MainWatchdogListener(void *data)
void LogEmergency(std::string msg)
assert((mem||(size==0))&&"Out Of Memory")
void SetLogMicroSyslog(const std::string &filename)
static Watchdog * instance_
#define SetLogDebugFile(filename)
stack_t sighandler_stack_
static Watchdog * Create(FnOnCrash on_crash)
pthread_t thread_listener_
Watchdog(FnOnCrash on_crash)
#define GetLogDebugFile()
std::string ReadUntilGdbPrompt(int fd_pipe)
static void * MainWatchdogListener(void *data)
SigactionMap SetSignalHandlers(const SigactionMap &signal_handlers)
string StringifyInt(const int64_t value)
UniquePtr< Pipe< kPipeWatchdogSupervisor > > pipe_listener_
The supervisee makes sure its watchdog does not die.
std::string GetLogMicroSyslog()
UniquePtr< Pipe< kPipeWatchdog > > pipe_watchdog_
bool ExecuteBinary(int *fd_stdin, int *fd_stdout, int *fd_stderr, const std::string &binary_path, const std::vector< std::string > &argv, const bool double_fork, pid_t *child_pid)
bool CloseAllFildes(const std::set< int > &preserve_fildes)
bool SwitchCredentials(const uid_t uid, const gid_t gid, const bool temporarily)
PathString GetParentPath(const PathString &path)
void SafeSleepMs(const unsigned ms)
void Spawn(const std::string &crash_dump_path)
void Block2Nonblock(int filedes)
platform_spinlock lock_handler_