]> git.saurik.com Git - apple/security.git/blob - cdsa/cdsa_utilities/debugging.cpp
a1941e3ebde5956de3b8890dfab481d031a2893b
[apple/security.git] / cdsa / cdsa_utilities / debugging.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 //
20 // debugging - non-trivial debugging support
21 //
22 #include <Security/debugsupport.h>
23 #include <Security/globalizer.h>
24 #include <cstdarg>
25
26 #define SYSLOG_NAMES // compile syslog name tables
27 #include <syslog.h>
28
29 namespace Security {
30 namespace Debug {
31
32
33 #if !defined(NDEBUG)
34
35
36 //
37 // Main debug functions (global and in-scope)
38 //
39 void debug(const char *scope, const char *format, ...)
40 {
41 #if !defined(NDEBUG_STUBS)
42 va_list args;
43 va_start(args, format);
44 Target::get().message(scope, format, args);
45 va_end(args);
46 #endif
47 }
48
49 void vdebug(const char *scope, const char *format, va_list args)
50 {
51 #if !defined(NDEBUG_STUBS)
52 Target::get().message(scope, format, args);
53 #endif
54 }
55
56 void Scope::operator () (const char *format, ...)
57 {
58 #if !defined(NDEBUG_STUBS)
59 va_list args;
60 va_start(args, format);
61 Target::get().message(mScope, format, args);
62 va_end(args);
63 #endif
64 }
65
66 bool debugging(const char *scope)
67 {
68 #if !defined(NDEBUG_STUBS)
69 return Target::get().debugging(scope);
70 #else
71 return false;
72 #endif
73 }
74
75
76 //
77 // Dump facility
78 //
79 bool dumping(const char *scope)
80 {
81 #if defined(NDEBUG_STUBS)
82 return false;
83 #else
84 return Target::get().dump(scope);
85 #endif
86 }
87
88 void dump(const char *format, ...)
89 {
90 #if !defined(NDEBUG_STUBS)
91 va_list args;
92 va_start(args, format);
93 Target::get().dump(format, args);
94 va_end(args);
95 #endif
96 }
97
98 void dumpData(const void *ptr, size_t size)
99 {
100 #if !defined(NDEBUG_STUBS)
101 const char *addr = reinterpret_cast<const char *>(ptr);
102 const char *end = addr + size;
103 bool isText = true;
104 for (const char *p = addr; p < end; p++)
105 if (!isprint(*p)) { isText = false; break; }
106
107 if (isText) {
108 dump("\"");
109 for (const char *p = addr; p < end; p++)
110 dump("%c", *p);
111 dump("\"");
112 } else {
113 dump("0x");
114 for (const char *p = addr; p < end; p++)
115 dump("%2.2x", *p);
116 }
117 #endif //NDEBUG_STUBS
118 }
119
120 void dumpData(const char *title, const void *ptr, size_t size)
121 {
122 #if !defined(NDEBUG_STUBS)
123 dump("%s: ", title);
124 dumpData(ptr, size);
125 dump("\n");
126 #endif //NDEBUG_STUBS
127 }
128
129
130 //
131 // Target initialization
132 //
133 #if !defined(NDEBUG_STUBS)
134
135 Target::Target() : showScope(false), showThread(false), showPid(false), sink(NULL)
136 {
137 // put into singleton slot if first
138 if (singleton == NULL)
139 singleton = this;
140 }
141
142 Target::~Target()
143 {
144 }
145
146
147 //
148 // The core logging function of a Target
149 //
150 void Target::message(const char *scope, const char *format, va_list args)
151 {
152 if (logSelector(scope)) {
153 // note: messageConstructionSize is big enough for all prefixes constructed
154 char buffer[messageConstructionSize]; // building the message here
155 char *bufp = buffer;
156 if (showScope && scope) { // add "scope "
157 if (const char *sep = strchr(scope, ',')) {
158 bufp += sprintf(bufp, "%-*s", Name::maxLength, (const char *)Name(scope, sep));
159 } else { // single scope
160 bufp += sprintf(bufp, "%-*s", Name::maxLength, scope);
161 }
162 }
163 if (showPid) { // add "[Pid] "
164 bufp += sprintf(bufp, "[%d] ", getpid());
165 }
166 if (showThread) { // add "#Tthreadid "
167 *bufp++ = '#';
168 Thread::Identity::current().getIdString(bufp);
169 bufp += strlen(bufp);
170 *bufp++ = ' ';
171 }
172 vsnprintf(bufp, buffer + sizeof(buffer) - bufp, format, args);
173 for (char *p = bufp; *p; p++)
174 if (!isprint(*p))
175 *p = '?';
176 sink->put(buffer, bufp - buffer);
177 }
178 }
179
180 bool Target::debugging(const char *scope)
181 {
182 return logSelector(scope);
183 }
184
185
186 //
187 // The core debug-dump function of a target
188 //
189 void Target::dump(const char *format, va_list args)
190 {
191 sink->dump(format, args);
192 }
193
194 bool Target::dump(const char *scope)
195 {
196 return dumpSelector(scope);
197 }
198
199 //
200 // Selector objects.
201 //
202 Target::Selector::Selector() : useSet(false), negate(false)
203 { }
204
205 void Target::Selector::operator = (const char *scope)
206 {
207 if (scope) {
208 // initial values
209 if (!strcmp(scope, "all")) {
210 useSet = false;
211 negate = true;
212 } else if (!strcmp(scope, "none")) {
213 useSet = negate = false;
214 } else {
215 useSet = true;
216 enableSet.erase(enableSet.begin(), enableSet.end());
217 if (scope[0] == '-') {
218 negate = true;
219 scope++;
220 } else
221 negate = false;
222 while (const char *sep = strchr(scope, ',')) {
223 enableSet.insert(Name(scope, sep));
224 scope = sep + 1;
225 }
226 enableSet.insert(scope);
227 }
228 } else {
229 useSet = negate = false;
230 }
231 }
232
233 bool Target::Selector::operator () (const char *scope) const
234 {
235 // a scope of NULL is a special override; it always qualifies
236 if (scope == NULL)
237 return true;
238
239 if (useSet) {
240 while (const char *sep = strchr(scope, ',')) {
241 if (enableSet.find(Name(scope, sep)) != enableSet.end())
242 return !negate;
243 scope = sep + 1;
244 }
245 return (enableSet.find(scope) != enableSet.end()) != negate;
246 } else {
247 return negate;
248 }
249 }
250
251
252 //
253 // Establish Target state from the environment
254 //
255 void Target::setFromEnvironment()
256 {
257 // set scopes
258 logSelector = getenv("DEBUGSCOPE");
259 dumpSelector = getenv("DEBUGDUMP");
260
261 //
262 // Set and configure destination. Currently available:
263 // /some/where -> that file
264 // LOG_SOMETHING -> syslog facility
265 // >&number -> that (already) open file descriptor
266 // anything else -> try as a filename sight unseen
267 // DEBUGDEST not set -> stderr
268 // anything in error -> stderr (with an error message on it)
269 //
270 if (const char *dest = getenv("DEBUGDEST")) {
271 if (dest[0] == '/') { // full pathname, write to file
272 to(dest);
273 } else if (!strncmp(dest, "LOG_", 4)) { // syslog
274 int facility = LOG_DAEMON;
275 for (CODE *cp = facilitynames; cp->c_name; cp++)
276 if (!strcmp(dest, cp->c_name))
277 facility = cp->c_val;
278 to(facility | LOG_DEBUG);
279 } else if (!strncmp(dest, ">&", 2)) { // to file descriptor
280 int fd = atoi(dest+2);
281 if (FILE *f = fdopen(fd, "a")) {
282 to(f);
283 } else {
284 to(stderr);
285 ::debug(NULL, "cannot log to fd[%d]: %s", fd, strerror(errno));
286 }
287 } else { // if everything else fails, write a file
288 to(dest);
289 }
290 } else { // default destination is stderr
291 to(stderr);
292 }
293 configure();
294 }
295
296
297 void Target::configure()
298 {
299 configure(getenv("DEBUGOPTIONS"));
300 }
301
302 void Target::configure(const char *config)
303 {
304 // configure global options
305 showScope = config && strstr(config, "scope");
306 showThread = config && strstr(config, "thread");
307 showPid = config && strstr(config, "pid");
308
309 // configure sink
310 if (sink)
311 sink->configure(config);
312 }
313
314
315 //
316 // Explicit destination assignments
317 //
318 void Target::to(Sink *s)
319 {
320 delete sink;
321 sink = s;
322 }
323
324 void Target::to(FILE *file)
325 {
326 to(new FileSink(file));
327 }
328
329 void Target::to(const char *filename)
330 {
331 if (FILE *f = fopen(filename, "a")) {
332 to(new FileSink(f));
333 } else {
334 to(stderr);
335 ::debug(NULL, "cannot debug to \"%s\": %s", filename, strerror(errno));
336 }
337 }
338
339 void Target::to(int syslogPriority)
340 {
341 to(new SyslogSink(syslogPriority));
342 }
343
344
345 //
346 // Making and retrieving the default singleton
347 //
348 Target *Target::singleton;
349
350 Target &Target::get()
351 {
352 if (singleton == NULL) {
353 Target *t = new Target;
354 t->setFromEnvironment();
355 }
356 return *singleton;
357 }
358
359
360 //
361 // Standard sink implementations
362 //
363 Target::Sink::~Sink()
364 { }
365
366 void Target::Sink::dump(const char *, va_list)
367 { }
368
369 void Target::Sink::configure(const char *)
370 { }
371
372
373 //
374 // File sinks (write to file via stdio)
375 //
376 void FileSink::put(const char *buffer, unsigned int)
377 {
378 StLock<Mutex> locker(lock, false);
379 if (lockIO)
380 locker.lock();
381 if (addDate) {
382 time_t now = time(NULL);
383 char *date = ctime(&now);
384 date[19] = '\0';
385 fprintf(file, "%s ", date + 4); // Nov 24 18:22:48
386 }
387 fputs(buffer, file);
388 putc('\n', file);
389 }
390
391 void FileSink::dump(const char *format, va_list args)
392 {
393 StLock<Mutex> locker(lock, false);
394 if (lockIO)
395 locker.lock();
396 vfprintf(file, format, args);
397 }
398
399 void FileSink::configure(const char *options)
400 {
401 if (options == NULL || !strstr(options, "noflush"))
402 setlinebuf(file);
403 if (options) {
404 addDate = strstr(options, "date");
405 lockIO = !strstr(options, "nolock");
406 }
407 }
408
409
410 //
411 // Syslog sinks (write to syslog)
412 //
413 void SyslogSink::put(const char *buffer, unsigned int)
414 {
415 syslog(priority, "%s", buffer);
416 }
417
418 void SyslogSink::dump(const char *format, va_list args)
419 {
420 // add to dump buffer
421 vsnprintf(dumpPtr, dumpBuffer + dumpBufferSize - dumpPtr, format, args);
422
423 // take off full lines and submit
424 char *p = dumpBase;
425 while (char *q = strchr(p, '\n')) {
426 *q++ = '\0'; // terminate/break
427 syslog(priority, " @@ %s", p);
428 p = q;
429 }
430
431 if (*p) { // left-over unterminated line segment in buffer
432 dumpPtr = p + strlen(p);
433 if ((dumpBase = p) > dumpBuffer + dumpBufferSize / 2) {
434 // shift buffer down to make room
435 memmove(dumpBuffer, dumpBase, dumpPtr - dumpBase);
436 dumpPtr -= (dumpBase - dumpBuffer);
437 dumpBase = dumpBuffer;
438 }
439 } else { // buffer is empty; reset to start
440 dumpBase = dumpPtr = dumpBuffer;
441 }
442 }
443
444 void SyslogSink::configure(const char *options)
445 {
446 }
447
448 #endif //NDEBUG_STUBS
449
450 #endif // NDEBUG
451
452
453 } // end namespace Debug
454
455 } // end namespace Security