1 /** @file
2
3 A brief file description
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 "traffic_crashlog.h"
25 #include "tscore/ink_args.h"
26 #include "tscore/ink_cap.h"
27 #include "tscore/I_Version.h"
28 #include "tscore/I_Layout.h"
29 #include "tscore/ink_syslog.h"
30 #include "records/I_RecProcess.h"
31 #include "RecordsConfig.h"
32 #include "tscore/BaseLogFile.h"
33 #include "tscore/runroot.h"
34
35 static int syslog_mode = false;
36 static int debug_mode = false;
37 static int wait_mode = false;
38 static char *host_triplet = nullptr;
39 static int target_pid = getppid();
40 static char *user = nullptr;
41
42 // If pid_t is not sizeof(int), we will have to jiggle argument parsing.
43 extern char __pid_size_static_assert[sizeof(pid_t) == sizeof(int) ? 0 : -1];
44
45 static AppVersionInfo appVersionInfo;
46 static const ArgumentDescription argument_descriptions[] = {
47 {"target", '-', "Target process ID", "I", &target_pid, nullptr, nullptr},
48 {"host", '-', "Host triplet for the process being logged", "S*", &host_triplet, nullptr, nullptr},
49 {"wait", '-', "Stop until signalled at startup", "F", &wait_mode, nullptr, nullptr},
50 {"syslog", '-', "Syslog after writing a crash log", "F", &syslog_mode, nullptr, nullptr},
51 {"debug", '-', "Enable debugging mode", "F", &debug_mode, nullptr, nullptr},
52 {"user", '-', "Username used to set privileges", "S*", &user, nullptr, nullptr},
53 HELP_ARGUMENT_DESCRIPTION(),
54 VERSION_ARGUMENT_DESCRIPTION(),
55 RUNROOT_ARGUMENT_DESCRIPTION()};
56
57 static struct tm
timestamp()58 timestamp()
59 {
60 time_t now = time(nullptr);
61 struct tm tm;
62
63 localtime_r(&now, &tm);
64 return tm;
65 }
66
67 static char *
crashlog_name()68 crashlog_name()
69 {
70 char filename[64];
71 struct tm now = timestamp();
72 std::string logdir(RecConfigReadLogDir());
73 ats_scoped_str pathname;
74
75 strftime(filename, sizeof(filename), "crash-%Y-%m-%d-%H%M%S.log", &now);
76 pathname = Layout::relative_to(logdir, filename);
77
78 return pathname.release();
79 }
80
81 static FILE *
crashlog_open(const char * path)82 crashlog_open(const char *path)
83 {
84 int fd;
85
86 fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0400);
87 return (fd == -1) ? nullptr : fdopen(fd, "w");
88 }
89
90 static unsigned
max_passwd_size()91 max_passwd_size()
92 {
93 #if defined(_SC_GETPW_R_SIZE_MAX)
94 long val = sysconf(_SC_GETPW_R_SIZE_MAX);
95 if (val > 0) {
96 return static_cast<unsigned>(val);
97 }
98 #endif
99
100 return 4096;
101 }
102
103 static void
change_privileges()104 change_privileges()
105 {
106 struct passwd *pwd;
107 struct passwd pbuf;
108 char buf[max_passwd_size()];
109
110 if (getpwnam_r(user, &pbuf, buf, sizeof(buf), &pwd) != 0) {
111 Error("missing password database entry for username '%s': %s", user, strerror(errno));
112 return;
113 }
114
115 if (pwd == nullptr) {
116 // Password entry not found ...
117 Error("missing password database entry for '%s'", user);
118 return;
119 }
120
121 if (setegid(pwd->pw_gid) != 0) {
122 Error("setegid(%d) failed: %s", pwd->pw_gid, strerror(errno));
123 return;
124 }
125
126 if (setreuid(pwd->pw_uid, 0) != 0) {
127 Error("setreuid(%d, %d) failed: %s", pwd->pw_uid, 0, strerror(errno));
128 return;
129 }
130 }
131
132 int
main(int,const char ** argv)133 main(int /* argc ATS_UNUSED */, const char **argv)
134 {
135 FILE *fp;
136 char *logname;
137 TSMgmtError mgmterr;
138 crashlog_target target;
139 pid_t parent = getppid();
140
141 diags = new Diags("traffic_crashlog", "" /* tags */, "" /* actions */, new BaseLogFile("stderr"));
142
143 appVersionInfo.setup(PACKAGE_NAME, "traffic_crashlog", PACKAGE_VERSION, __DATE__, __TIME__, BUILD_MACHINE, BUILD_PERSON, "");
144
145 // Process command line arguments and dump into variables
146 process_args(&appVersionInfo, argument_descriptions, countof(argument_descriptions), argv);
147
148 // XXX This is a hack. traffic_manager starts traffic_server with the euid of the admin user. We are still
149 // privileged, but won't be able to open files in /proc or ptrace the target. This really should be fixed
150 // in traffic_manager.
151 if (getuid() == 0) {
152 change_privileges();
153 }
154
155 if (wait_mode) {
156 EnableDeathSignal(SIGKILL);
157 kill(getpid(), SIGSTOP);
158 }
159
160 // If our parent changed, then we were woken after traffic_server exited. There's no point trying to
161 // emit a crashlog because traffic_server is gone.
162 if (getppid() != parent) {
163 return 0;
164 }
165
166 runroot_handler(argv);
167 Layout::create();
168 RecProcessInit(RECM_STAND_ALONE, nullptr /* diags */);
169 LibRecordsConfigInit();
170
171 if (syslog_mode) {
172 RecString name;
173 int facility = -1;
174
175 if (REC_ReadConfigStringAlloc(name, "proxy.config.syslog_facility") == REC_ERR_OKAY) {
176 facility = facility_string_to_int(name);
177 ats_free(name);
178 }
179
180 if (facility < 0) {
181 facility = LOG_DAEMON;
182 }
183
184 openlog(appVersionInfo.AppStr, LOG_PID | LOG_NDELAY | LOG_NOWAIT, facility);
185 diags->config.outputs[DL_Debug].to_syslog = true;
186 diags->config.outputs[DL_Status].to_syslog = true;
187 diags->config.outputs[DL_Note].to_syslog = true;
188 diags->config.outputs[DL_Warning].to_syslog = true;
189 diags->config.outputs[DL_Error].to_syslog = true;
190 diags->config.outputs[DL_Fatal].to_syslog = true;
191 diags->config.outputs[DL_Alert].to_syslog = true;
192 diags->config.outputs[DL_Emergency].to_syslog = true;
193 }
194
195 Note("crashlog started, target=%ld, debug=%s syslog=%s, uid=%ld euid=%ld", static_cast<long>(target_pid),
196 debug_mode ? "true" : "false", syslog_mode ? "true" : "false", (long)getuid(), (long)geteuid());
197
198 mgmterr = TSInit(nullptr, (TSInitOptionT)(TS_MGMT_OPT_NO_EVENTS | TS_MGMT_OPT_NO_SOCK_TESTS));
199 if (mgmterr != TS_ERR_OKAY) {
200 char *msg = TSGetErrorMessage(mgmterr);
201 Warning("failed to initialize management API: %s", msg);
202 TSfree(msg);
203 }
204
205 ink_zero(target);
206 target.pid = static_cast<pid_t>(target_pid);
207 target.timestamp = timestamp();
208
209 if (host_triplet && strncmp(host_triplet, "x86_64-unknown-linux", sizeof("x86_64-unknown-linux") - 1) == 0) {
210 ssize_t nbytes;
211 target.flags |= CRASHLOG_HAVE_THREADINFO;
212
213 nbytes = read(STDIN_FILENO, &target.siginfo, sizeof(target.siginfo));
214 if (nbytes < static_cast<ssize_t>(sizeof(target.siginfo))) {
215 Warning("received %zd of %zu expected signal info bytes", nbytes, sizeof(target.siginfo));
216 target.flags &= ~CRASHLOG_HAVE_THREADINFO;
217 }
218
219 nbytes = read(STDIN_FILENO, &target.ucontext, sizeof(target.ucontext));
220 if (nbytes < static_cast<ssize_t>(sizeof(target.ucontext))) {
221 Warning("received %zd of %zu expected thread context bytes", nbytes, sizeof(target.ucontext));
222 target.flags &= ~CRASHLOG_HAVE_THREADINFO;
223 }
224 }
225
226 logname = crashlog_name();
227
228 fp = debug_mode ? stdout : crashlog_open(logname);
229 if (fp == nullptr) {
230 Error("failed to create '%s': %s", logname, strerror(errno));
231 ats_free(logname);
232 return 1;
233 }
234
235 Note("logging to %p", fp);
236
237 crashlog_write_procname(fp, target);
238 crashlog_write_exename(fp, target);
239 fprintf(fp, LABELFMT "Traffic Server %s\n", "Version:", PACKAGE_VERSION);
240 crashlog_write_uname(fp, target);
241 crashlog_write_datime(fp, target);
242
243 fprintf(fp, "\n");
244 crashlog_write_siginfo(fp, target);
245
246 fprintf(fp, "\n");
247 crashlog_write_registers(fp, target);
248
249 fprintf(fp, "\n");
250 crashlog_write_backtrace(fp, target);
251
252 fprintf(fp, "\n");
253 crashlog_write_procstatus(fp, target);
254
255 fprintf(fp, "\n");
256 crashlog_write_proclimits(fp, target);
257
258 fprintf(fp, "\n");
259 crashlog_write_regions(fp, target);
260
261 fprintf(fp, "\n");
262 crashlog_write_records(fp, target);
263
264 Error("wrote crash log to %s", logname);
265
266 ats_free(logname);
267
268 fflush(fp);
269 fclose(fp);
270 return 0;
271 }
272