1 /** @file
2
3 Crash logging helper support
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 #include "Main.h"
25 #include "tscore/I_Layout.h"
26 #include "tscore/I_Version.h"
27 #include "I_Net.h"
28 #include "tscore/signals.h"
29 #include "tscore/ink_cap.h"
30
31 // ucontext.h is deprecated on Darwin, and we really only need it on Linux, so only
32 // include it if we are planning to use it.
33 #if defined(__linux__)
34 #include <ucontext.h>
35 #endif
36
37 extern AppVersionInfo appVersionInfo;
38
39 static pid_t crash_logger_pid = -1;
40 static int crash_logger_fd = NO_FD;
41
42 static char *
create_logger_path()43 create_logger_path()
44 {
45 RecString name;
46 std::string bindir;
47 ats_scoped_str fullpath;
48
49 if (RecGetRecordString_Xmalloc("proxy.config.crash_log_helper", &name) != REC_ERR_OKAY) {
50 return nullptr;
51 }
52
53 // Take an absolute path as it is ...
54 if (name == nullptr || *name == '/') {
55 return name;
56 }
57
58 // Otherwise locate it relative to $BINDIR.
59 bindir = RecConfigReadBinDir();
60 fullpath = Layout::relative_to(bindir, name);
61
62 ats_free(name);
63 return fullpath.release();
64 }
65
66 static bool
check_logger_path(const char * path)67 check_logger_path(const char *path)
68 {
69 struct stat sbuf;
70
71 if (stat(path, &sbuf) != 0) {
72 Error("failed to access crash log helper '%s': %s", path, strerror(errno));
73 return false;
74 }
75
76 if (!S_ISREG(sbuf.st_mode) || access(path, X_OK) != 0) {
77 Error("crash log helper '%s' is not executable", path);
78 return false;
79 }
80
81 return true;
82 }
83
84 void
crash_logger_init(const char * user)85 crash_logger_init(const char *user)
86 {
87 ats_scoped_str logger(create_logger_path());
88 const char *basename;
89
90 pid_t child;
91 int status;
92 int pipe[2];
93
94 // Do nothing the log helper was set to NULL, or we can't find it.
95 if (!logger || !check_logger_path(logger)) {
96 return;
97 }
98
99 // By this point, we have an absolute path, so we'd better be able to find the basename.
100 basename = strrchr(logger, '/') + 1;
101
102 socketpair(AF_UNIX, SOCK_STREAM, 0, pipe);
103
104 child = fork();
105 switch (child) {
106 case -1:
107 Error("failed to fork crash log helper: %s", strerror(errno));
108 crash_logger_pid = -1;
109 crash_logger_fd = NO_FD;
110 return;
111
112 case 0:
113 // Dupe the child socket to stdin.
114 dup2(pipe[1], STDIN_FILENO);
115 close(pipe[0]);
116 close(pipe[1]);
117
118 // Starting after stderr, keep closing file descriptors until we run out.
119 for (int fd = STDERR_FILENO + 1; fd; ++fd) {
120 if (close(fd) == -1) {
121 break;
122 }
123 }
124
125 ink_release_assert(execl(logger, basename, "--syslog", "--wait", "--host", TS_BUILD_CANONICAL_HOST, "--user", user, NULL) !=
126 -1);
127 return; // not reached.
128 }
129
130 close(pipe[1]);
131 crash_logger_pid = child;
132 crash_logger_fd = pipe[0];
133
134 // Wait for the helper to stop
135 if (waitpid(crash_logger_pid, &status, WUNTRACED) > 0) {
136 Debug("server", "waited on PID %ld, %s", (long)crash_logger_pid, WIFSTOPPED(status) ? "STOPPED" : "???");
137
138 if (WIFEXITED(status)) {
139 Warning("crash logger '%s' unexpectedly exited with status %d", (const char *)logger, WEXITSTATUS(status));
140 close(crash_logger_pid);
141 crash_logger_pid = -1;
142 crash_logger_fd = NO_FD;
143 }
144 }
145 }
146
147 void
crash_logger_invoke(int signo,siginfo_t * info,void * ctx)148 crash_logger_invoke(int signo, siginfo_t *info, void *ctx)
149 {
150 if (crash_logger_pid != -1) {
151 int status;
152
153 // Let the crash logger free ...
154 kill(crash_logger_pid, SIGCONT);
155
156 #if defined(__linux__)
157 // Write the crashing thread information to the crash logger. While the siginfo_t is blesses by POSIX, the
158 // ucontext_t can contain pointers, so it's highly platform dependent. On Linux with glibc, however, it is
159 // a single memory block that we can just puke out.
160 ATS_UNUSED_RETURN(write(crash_logger_fd, info, sizeof(siginfo_t)));
161 ATS_UNUSED_RETURN(write(crash_logger_fd, static_cast<ucontext_t *>(ctx), sizeof(ucontext_t)));
162 #endif
163
164 close(crash_logger_fd);
165 crash_logger_fd = NO_FD;
166
167 // Wait for the logger to finish ...
168 waitpid(crash_logger_pid, &status, 0);
169 }
170
171 // Log the signal, dump a stack trace and core.
172 signal_format_siginfo(signo, info, appVersionInfo.AppStr); // XXX Add timestamp ...
173 signal_crash_handler(signo, info, ctx);
174 }
175