]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/debugging_internal.cpp
Security-58286.1.32.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / debugging_internal.cpp
1 /*
2 * Copyright (c) 2000-2004,2011-2012,2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 //
26 // debugging - non-trivial debugging support
27 //
28 #include <security_utilities/debugging_internal.h>
29 #include <security_utilities/debugsupport.h>
30 #include <security_utilities/globalizer.h>
31 #include <cstdarg>
32 #include <ctype.h>
33
34 #define SYSLOG_NAMES // compile syslog name tables
35 #include <syslog.h>
36
37 #include <cxxabi.h> // for name demangling
38 #include <mach-o/dyld.h> // for _NSGetExecutablePath
39 #include <limits.h>
40
41 // enable kernel tracing
42 #define ENABLE_SECTRACE 1
43
44
45 namespace Security {
46 namespace Debug {
47
48
49 //
50 // Dump facility
51 //
52 bool dumping(const char *scope)
53 {
54 #if defined(NDEBUG_STUBS)
55 return false;
56 #else
57 return Target::get().dumping(scope);
58 #endif
59 }
60
61 void dump(const char *format, ...)
62 {
63 #if !defined(NDEBUG_CODE)
64 va_list args;
65 va_start(args, format);
66 Target::get().dump(format, args);
67 va_end(args);
68 #endif
69 }
70
71 void dumpData(const void *ptr, size_t size)
72 {
73 #if !defined(NDEBUG_CODE)
74 const char *addr = reinterpret_cast<const char *>(ptr);
75 const char *end = addr + size;
76 bool isText = true;
77 for (const char *p = addr; p < end; p++)
78 if (!isprint(*p)) { isText = false; break; }
79
80 if (isText) {
81 dump("\"");
82 for (const char *p = addr; p < end; p++)
83 dump("%c", *p);
84 dump("\"");
85 } else {
86 dump("0x");
87 for (const char *p = addr; p < end; p++)
88 dump("%2.2x", static_cast<unsigned char>(*p));
89 }
90 #endif //NDEBUG_STUBS
91 }
92
93 void dumpData(const char *title, const void *ptr, size_t size)
94 {
95 #if !defined(NDEBUG_CODE)
96 dump("%s: ", title);
97 dumpData(ptr, size);
98 dump("\n");
99 #endif //NDEBUG_STUBS
100 }
101
102
103 //
104 // Turn a C++ typeid into a nice type name.
105 // This uses the C++ ABI where available.
106 // We're stripping out a few C++ prefixes; they're pretty redundant (and obvious).
107 //
108 string makeTypeName(const type_info &type)
109 {
110 int status;
111 char *cname = abi::__cxa_demangle(type.name(), NULL, NULL, &status);
112 string name = !strncmp(cname, "Security::", 10) ? (cname + 10) :
113 !strncmp(cname, "std::", 5) ? (cname + 5) :
114 cname;
115 ::free(cname); // yes, really (ABI rules)
116 return name;
117 }
118
119
120 //
121 // Target initialization.
122 // This where we should do all "first time" initializations.
123 //
124 #if !defined(NDEBUG_CODE)
125
126 char Target::progName[maxProgNameLength + 1];
127 unsigned int Target::PerThread::lastUsed;
128
129 Target::Target()
130 : showScope(false), showThread(false), showProc(false), showDate(false),
131 sink(NULL)
132 {
133 // put into singleton slot if first
134 if (singleton == NULL)
135 singleton = this;
136
137 // insert terminate handler
138 if (!previousTerminator) // first time we do this
139 previousTerminator = set_terminate(terminator);
140
141 // get program name
142 char execPath[PATH_MAX];
143 uint32_t length = sizeof(execPath);
144 if (_NSGetExecutablePath(execPath, &length)) {
145 strcpy(progName, "unknown");
146 } else {
147 const char *p = strrchr(execPath, '/');
148 if (p)
149 p++;
150 else
151 p = execPath;
152 size_t plen = strlen(p);
153 if (plen > maxProgNameLength) // too long
154 p += plen - maxProgNameLength; // take rear
155 strcpy(progName, p);
156 }
157 }
158
159 Target::~Target()
160 {
161 }
162
163
164 static void addScope(char *&bufp, const char *scope)
165 {
166 if (const char *sep = strchr(scope, ',')) {
167 bufp += sprintf(bufp, "%-*s", Name::maxLength, (const char *)Name(scope, sep));
168 } else { // single scope
169 bufp += sprintf(bufp, "%-*s", Name::maxLength, scope);
170 }
171 }
172
173
174 //
175 // The core logging function of a Target
176 //
177 void Target::message(const char *scope, const char *format, va_list args)
178 {
179 if (logSelector(scope)) {
180 // note: messageConstructionSize is big enough for all prefixes constructed
181 char buffer[messageConstructionSize]; // building the message here
182 char *bufp = buffer;
183
184 // date option
185 if (showDate && sink->needsDate) {
186 time_t now = time(NULL);
187 char *date = ctime(&now);
188 date[19] = '\0';
189 bufp += sprintf(bufp, "%s ", date + 4); // Nov 24 18:22:48
190 }
191
192 // leading scope
193 if (showScope && scope)
194 addScope(bufp, scope);
195
196 if (showProc || showThread) {
197 char sub[maxProgNameLength + 20];
198 unsigned plen = 0;
199 if (showProc && showThread)
200 plen = sprintf(sub, "%s[%d]", progName, getpid());
201 else if (showProc)
202 plen = sprintf(sub, "%s", progName);
203 else
204 plen = sprintf(sub, "[%d]", getpid());
205 unsigned int id = perThread().id;
206 if (id > 1)
207 plen += sprintf(sub + plen, ":%d", id);
208 if (plen <= procLength)
209 bufp += sprintf(bufp, "%-*s ", int(procLength), sub);
210 else
211 bufp += sprintf(bufp, "%s ", sub + plen - procLength);
212 }
213
214 // scope after proc/thread/pid
215 if (showScopeRight && scope)
216 addScope(bufp, scope);
217
218 // now stuff the message body in, slightly roasted
219 size_t left = buffer + sizeof(buffer) - bufp - 1; // reserve one
220 size_t written = vsnprintf(bufp, left, format, args);
221 for (char *p = bufp; *p; p++)
222 if (!isprint(*p))
223 *p = '?';
224 if (written >= left) { // snprintf overflowed
225 bufp += left;
226 strcpy(bufp - 3, "...");
227 } else
228 bufp += written;
229
230 // now append a newline and a null
231 bufp[0] = '\n';
232 bufp[1] = '\0';
233
234 // submit to sink (do not count newline and null in count)
235 sink->put(buffer, (unsigned int)(bufp - buffer));
236 }
237 }
238
239 bool Target::debugging(const char *scope)
240 {
241 return logSelector(scope);
242 }
243
244
245 //
246 // The core debug-dump function of a target
247 //
248 void Target::dump(const char *format, va_list args)
249 {
250 char buffer[messageConstructionSize]; // building the message here
251 vsnprintf(buffer, sizeof(buffer), format, args);
252 for (char *p = buffer; *p; p++)
253 if ((!isprint(*p) && !isspace(*p)) || *p == '\r')
254 *p = '?';
255 sink->dump(buffer);
256 }
257
258 bool Target::dumping(const char *scope)
259 {
260 return dumpSelector(scope);
261 }
262
263
264 //
265 // Selector objects.
266 //
267 Target::Selector::Selector() : useSet(false), negate(false)
268 { }
269
270 void Target::Selector::operator = (const char *scope)
271 {
272 if (scope) {
273 // initial values
274 if (!strcmp(scope, "all")) {
275 useSet = false;
276 negate = true;
277 } else if (!strcmp(scope, "none")) {
278 useSet = negate = false;
279 } else {
280 useSet = true;
281 enableSet.erase(enableSet.begin(), enableSet.end());
282 if (scope[0] == '-') {
283 negate = true;
284 scope++;
285 } else
286 negate = false;
287 while (const char *sep = strchr(scope, ',')) {
288 enableSet.insert(Name(scope, sep));
289 scope = sep + 1;
290 }
291 enableSet.insert(scope);
292 }
293 } else {
294 useSet = negate = false;
295 }
296 }
297
298 bool Target::Selector::operator () (const char *scope) const
299 {
300 // a scope of NULL is a special override; it always qualifies
301 if (scope == NULL)
302 return true;
303
304 if (useSet) {
305 while (const char *sep = strchr(scope, ',')) {
306 if (enableSet.find(Name(scope, sep)) != enableSet.end())
307 return !negate;
308 scope = sep + 1;
309 }
310 return (enableSet.find(scope) != enableSet.end()) != negate;
311 } else {
312 return negate;
313 }
314 }
315
316
317 //
318 // Establish Target state from the environment
319 //
320 void Target::setFromEnvironment()
321 {
322 // set scopes
323 logSelector = getenv("DEBUGSCOPE");
324 dumpSelector = getenv("DEBUGDUMP");
325
326 //
327 // Set and configure destination. Currently available:
328 // /some/where -> that file
329 // LOG_SOMETHING -> syslog facility
330 // >&number -> that (already) open (for write or append) file descriptor
331 // anything else -> try as a filename sight unseen [may change]
332 // DEBUGDEST not set -> stderr
333 // anything in error -> stderr (with an error message on it)
334 //
335 if (const char *dest = getenv("DEBUGDEST")) {
336 if (dest[0] == '/') { // full pathname, write to file
337 to(dest);
338 } else if (!strncmp(dest, "LOG_", 4)) { // syslog
339 int facility = LOG_DAEMON;
340 for (CODE *cp = facilitynames; cp->c_name; cp++)
341 if (!strcmp(dest, cp->c_name))
342 facility = cp->c_val;
343 to(facility | LOG_DEBUG);
344 } else if (!strncmp(dest, ">&", 2)) { // to file descriptor
345 int fd = atoi(dest+2);
346 if (FILE *f = fdopen(fd, "a")) {
347 to(f);
348 } else {
349 to(stderr);
350 secinfo("", "cannot log to fd[%d]: %s", fd, strerror(errno));
351 }
352 } else { // if everything else fails, write a file
353 to(dest);
354 }
355 } else { // default destination is stderr
356 to(stderr);
357 }
358 configure();
359 }
360
361
362 void Target::configure()
363 {
364 configure(getenv("DEBUGOPTIONS"));
365 }
366
367 void Target::configure(const char *config)
368 {
369 // configure global options
370 showScopeRight = config && strstr(config, "rscope");
371 showScope = !showScopeRight && config && strstr(config, "scope");
372 showThread = config && (strstr(config, "thread") || strstr(config, "pid")); // (legacy)
373 showProc = config && strstr(config, "proc");
374 showDate = config && strstr(config, "date");
375
376 // configure sink
377 if (sink)
378 sink->configure(config);
379 }
380
381
382 //
383 // Explicit destination assignments
384 //
385 void Target::to(Sink *s)
386 {
387 delete sink;
388 sink = s;
389 }
390
391 void Target::to(FILE *file)
392 {
393 to(new FileSink(file));
394 }
395
396 void Target::to(const char *filename)
397 {
398 if (FILE *f = fopen(filename, "a")) {
399 to(new FileSink(f));
400 } else {
401 to(stderr);
402 secinfo("", "cannot debug to \"%s\": %s", filename, strerror(errno));
403 }
404 }
405
406 void Target::to(int syslogPriority)
407 {
408 to(new SyslogSink(syslogPriority));
409 }
410
411
412 //
413 // Making and retrieving the default singleton
414 //
415 Target *Target::singleton;
416
417 Target &Target::get()
418 {
419 if (singleton == NULL) {
420 Target *t = new Target;
421 t->setFromEnvironment();
422
423 // The Target constructor attempts to set singleton to the object. If it didn't succeed, we don't need T anymore.
424 if(singleton != t) {
425 delete t;
426 }
427 }
428 return *singleton;
429 }
430
431
432 //
433 // Standard sink implementations
434 //
435 Target::Sink::~Sink()
436 { }
437
438 void Target::Sink::dump(const char *)
439 { }
440
441 void Target::Sink::configure(const char *)
442 { }
443
444
445 //
446 // The terminate handler installed when a Target is created
447 //
448 terminate_handler Target::previousTerminator;
449
450 void Target::terminator()
451 {
452 secinfo("exception", "uncaught exception terminates program");
453 previousTerminator();
454 secinfo("exception", "prior termination handler failed to abort; forcing abort");
455 abort();
456 }
457
458
459 //
460 // File sinks (write to file via stdio)
461 //
462 void FileSink::put(const char *inbuf, unsigned int length)
463 {
464 fwrite(inbuf, 1, length + 1, file); // do pick up the trailing newline
465 }
466
467 void FileSink::dump(const char *text)
468 {
469 fputs(text, file);
470 }
471
472 void FileSink::configure(const char *options)
473 {
474 if (options == NULL || !strstr(options, "noflush")) {
475 // we mean "if the file isn't unbuffered", but what's the portable way to say that?
476 if (file != stderr)
477 setlinebuf(file);
478 }
479 }
480
481
482 //
483 // Syslog sinks (write to syslog)
484 //
485 void SyslogSink::put(const char *buffer, unsigned int length)
486 {
487 syslog(priority, "%1.*s", length, buffer); // don't pick up trailing newline
488 }
489
490 void SyslogSink::dump(const char *text)
491 {
492 // add to dump buffer
493 snprintf(dumpPtr, dumpBuffer + dumpBufferSize - dumpPtr, "%s", text);
494
495 // take off full lines and submit
496 char *p = dumpBase;
497 while (char *q = strchr(p, '\n')) {
498 *q++ = '\0'; // terminate/break
499 syslog(priority, " @@ %s", p);
500 p = q;
501 }
502
503 if (*p) { // left-over unterminated line segment in buffer
504 dumpPtr = p + strlen(p);
505 if ((dumpBase = p) > dumpBuffer + dumpBufferSize / 2) {
506 // shift buffer down to make room
507 memmove(dumpBuffer, dumpBase, dumpPtr - dumpBase);
508 dumpPtr -= (dumpBase - dumpBuffer);
509 dumpBase = dumpBuffer;
510 }
511 } else { // buffer is empty; reset to start
512 dumpBase = dumpPtr = dumpBuffer;
513 }
514 }
515
516 void SyslogSink::configure(const char *options)
517 {
518 }
519
520 #endif //NDEBUG_CODE
521
522
523 } // end namespace Debug
524 } // end namespace Security
525
526
527 void secdebug_internal(const char* scope, const char* format, ...) {
528 // no-op.
529 }
530 void secdebugfunc_internal(const char* scope, const char* functionname, const char* format, ...) {
531 // no-op.
532 }