2 * Copyright (c) 1997-2020 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 #include "heim_threads.h"
39 #include "heimbase-svc.h"
44 struct heim_log_facility_internal {
47 heim_log_log_func_t log_func;
48 heim_log_close_func_t close_func;
52 struct heim_log_facility_s {
56 struct heim_log_facility_internal *val;
59 typedef struct heim_pcontext_s *heim_pcontext;
60 typedef struct heim_pconfig *heim_pconfig;
61 struct heim_svc_req_desc_common_s {
62 HEIM_SVC_REQUEST_DESC_COMMON_ELEMENTS;
65 static struct heim_log_facility_internal *
66 log_realloc(heim_log_facility *f)
68 struct heim_log_facility_internal *fp;
69 fp = realloc(f->val, (f->len + 1) * sizeof(*f->val));
83 #define L(X) { #X, LOG_ ## X }
85 static struct s2i syslogvals[] = {
129 find_value(const char *s, struct s2i *table)
131 while (table->s && strcasecmp(table->s, s) != 0)
137 heim_initlog(heim_context context,
139 heim_log_facility **fac)
141 heim_log_facility *f = calloc(1, sizeof(*f));
143 return heim_enomem(context);
145 f->program = strdup(program);
146 if (f->program == NULL) {
148 return heim_enomem(context);
155 heim_log_ref(heim_log_facility *fac)
163 heim_addlog_func(heim_context context,
164 heim_log_facility *fac,
167 heim_log_log_func_t log_func,
168 heim_log_close_func_t close_func,
171 struct heim_log_facility_internal *fp = log_realloc(fac);
173 return heim_enomem(context);
176 fp->log_func = log_func;
177 fp->close_func = close_func;
183 struct _heimdal_syslog_data{
187 static void HEIM_CALLCONV
188 log_syslog(heim_context context, const char *timestr,
189 const char *msg, void *data)
191 struct _heimdal_syslog_data *s = data;
192 syslog(s->priority, "%s", msg);
195 static void HEIM_CALLCONV
196 close_syslog(void *data)
202 static heim_error_code
203 open_syslog(heim_context context,
204 heim_log_facility *facility, int min, int max,
205 const char *sev, const char *fac)
207 struct _heimdal_syslog_data *sd = malloc(sizeof(*sd));
211 return heim_enomem(context);
212 i = find_value(sev, syslogvals);
216 i = find_value(fac, syslogvals);
220 roken_openlog(facility->program, LOG_PID | LOG_NDELAY, i);
221 return heim_addlog_func(context, facility, min, max,
222 log_syslog, close_syslog, sd);
231 #define FILEDISP_KEEPOPEN 0x1
232 #define FILEDISP_REOPEN 0x2
233 #define FILEDISP_IFEXISTS 0x4
240 static void HEIM_CALLCONV
241 log_file(heim_context context, const char *timestr, const char *msg, void *data)
244 struct file_data *f = data;
250 if (logf == NULL || (f->disp & FILEDISP_REOPEN)) {
251 int flags = O_WRONLY|O_APPEND;
254 if (f->mode[0] == 'e') {
258 if (f->mode[i] == 'w')
260 if (f->mode[i + 1] == '+')
263 if (f->disp & FILEDISP_IFEXISTS) {
264 /* Cache failure for 1s */
265 gettimeofday(&tv, NULL);
266 if (tv.tv_sec == f->tv.tv_sec)
272 fd = open(f->filename, flags, 0666); /* umask best be set */
274 if (f->disp & FILEDISP_IFEXISTS)
275 gettimeofday(&f->tv, NULL);
279 logf = fdopen(fd, f->mode);
281 if (f->fd == NULL && (f->disp & FILEDISP_KEEPOPEN))
286 * make sure the log doesn't contain special chars:
287 * we used to use strvisx(3) to encode the log, but this is
288 * inconsistent with our syslog(3) code which does not do this.
289 * It also makes it inelegant to write data which has already
290 * been quoted such as what krb5_unparse_principal() gives us.
291 * So, we change here to eat the special characters, instead.
293 if (msg && (msgclean = strdup(msg))) {
294 for (i = 0, j = 0; msg[i]; i++)
295 if (msg[i] >= 32 || msg[i] == '\t')
296 msgclean[j++] = msg[i];
297 fprintf(logf, "%s %s\n", timestr ? timestr : "", msgclean);
304 static void HEIM_CALLCONV
305 close_file(void *data)
307 struct file_data *f = data;
308 if (f->fd && f->fd != stdout && f->fd != stderr)
314 static heim_error_code
315 open_file(heim_context context, heim_log_facility *fac, int min, int max,
316 const char *filename, const char *mode, FILE *f, int disp,
319 heim_error_code ret = 0;
320 struct file_data *fd;
322 if ((fd = calloc(1, sizeof(*fd))) == NULL)
323 return heim_enomem(context);
332 ret = heim_expand_path_tokens(context, filename, 1, &fd->filename, NULL);
333 else if ((fd->filename = strdup(filename)) == NULL)
334 ret = heim_enomem(context);
337 ret = heim_addlog_func(context, fac, min, max, log_file, close_file, fd);
342 if (disp & FILEDISP_KEEPOPEN)
343 log_file(context, NULL, NULL, fd);
348 heim_addlog_dest(heim_context context, heim_log_facility *f, const char *orig)
350 heim_error_code ret = 0;
351 int min = 0, max = 3, n;
353 const char *p = orig;
358 n = sscanf(p, "%d%c%d/", &min, &c, &max);
373 q = strrchr(p, '\\');
380 heim_set_error_message(context, EINVAL /*XXX HEIM_ERR_LOG_PARSE*/,
381 N_("failed to parse \"%s\"", ""), orig);
382 return EINVAL /*XXX HEIM_ERR_LOG_PARSE*/;
386 if (strcmp(p, "STDERR") == 0) {
387 ret = open_file(context, f, min, max, NULL, NULL, stderr,
388 FILEDISP_KEEPOPEN, 0);
389 } else if (strcmp(p, "CONSOLE") == 0) {
391 ret = open_file(context, f, min, max, "/dev/console", "w", NULL,
392 FILEDISP_KEEPOPEN, 0);
393 } else if (strncmp(p, "EFILE:", 5) == 0) {
394 ret = open_file(context, f, min, max, p + sizeof("EFILE:") - 1, "a",
395 NULL, FILEDISP_IFEXISTS | FILEDISP_REOPEN, 1);
396 } else if (strncmp(p, "EFILE=", 5) == 0) {
397 ret = open_file(context, f, min, max, p + sizeof("EFILE=") - 1, "a",
398 NULL, FILEDISP_IFEXISTS | FILEDISP_KEEPOPEN, 1);
399 } else if (strncmp(p, "FILE:", sizeof("FILE:") - 1) == 0) {
400 ret = open_file(context, f, min, max, p + sizeof("FILE:") - 1, "a",
401 NULL, FILEDISP_REOPEN, 1);
402 } else if (strncmp(p, "FILE=", sizeof("FILE=") - 1) == 0) {
403 ret = open_file(context, f, min, max, p + sizeof("FILE=") - 1, "a",
404 NULL, FILEDISP_KEEPOPEN, 1);
405 } else if (strncmp(p, "DEVICE:", sizeof("DEVICE:") - 1) == 0) {
406 ret = open_file(context, f, min, max, p + sizeof("DEVICE:") - 1, "a",
407 NULL, FILEDISP_REOPEN, 0);
408 } else if (strncmp(p, "DEVICE=", sizeof("DEVICE=") - 1) == 0) {
409 ret = open_file(context, f, min, max, p + sizeof("DEVICE=") - 1, "a",
410 NULL, FILEDISP_KEEPOPEN, 0);
411 } else if (strncmp(p, "SYSLOG", 6) == 0 && (p[6] == '\0' || p[6] == ':')) {
412 char severity[128] = "";
413 char facility[128] = "";
417 if (strsep_copy(&p, ":", severity, sizeof(severity)) != -1)
418 strsep_copy(&p, ":", facility, sizeof(facility));
419 if (*severity == '\0')
420 strlcpy(severity, "ERR", sizeof(severity));
421 if (*facility == '\0')
422 strlcpy(facility, "AUTH", sizeof(facility));
423 ret = open_syslog(context, f, min, max, severity, facility);
425 ret = EINVAL; /*XXX HEIM_ERR_LOG_PARSE*/
426 heim_set_error_message(context, ret,
427 N_("unknown log type: %s", ""), p);
433 heim_openlog(heim_context context,
436 heim_log_facility **fac)
440 ret = heim_initlog(context, program, fac);
446 for (i = 0; specs[i] && ret == 0; i++)
447 ret = heim_addlog_dest(context, *fac, specs[i]);
449 ret = heim_addlog_dest(context, *fac, "SYSLOG");
455 heim_closelog(heim_context context, heim_log_facility *fac)
459 if (!fac || --(fac->refs))
461 for (i = 0; i < fac->len; i++)
462 (*fac->val[i].close_func)(fac->val[i].data);
473 format_time(heim_context context, time_t t, char *s, size_t len)
475 struct tm *tm = heim_context_get_log_utc(context) ?
476 gmtime(&t) : localtime(&t);
477 if (tm && strftime(s, len, heim_context_get_time_fmt(context), tm))
479 snprintf(s, len, "%ld", (long)t);
483 #define __attribute__(X)
486 heim_vlog_msg(heim_context context,
487 heim_log_facility *fac,
492 __attribute__ ((__format__ (__printf__, 5, 0)))
496 const char *actual = NULL;
502 fac = context->log_dest;
503 for (i = 0; fac && i < fac->len; i++)
504 if (fac->val[i].min <= level &&
505 (fac->val[i].max < 0 || fac->val[i].max >= level)) {
508 format_time(context, t, buf, sizeof(buf));
510 if (actual == NULL) {
511 int ret = vasprintf(&msg, fmt, ap);
512 if (ret < 0 || msg == NULL)
517 (*fac->val[i].log_func)(context, buf, actual, fac->val[i].data);
527 heim_vlog(heim_context context,
528 heim_log_facility *fac,
532 __attribute__ ((__format__ (__printf__, 4, 0)))
534 return heim_vlog_msg(context, fac, NULL, level, fmt, ap);
538 heim_log_msg(heim_context context,
539 heim_log_facility *fac,
544 __attribute__ ((__format__ (__printf__, 5, 6)))
550 ret = heim_vlog_msg(context, fac, reply, level, fmt, ap);
557 heim_log(heim_context context,
558 heim_log_facility *fac,
562 __attribute__ ((__format__ (__printf__, 4, 5)))
568 ret = heim_vlog(context, fac, level, fmt, ap);
574 heim_debug(heim_context context,
578 __attribute__ ((__format__ (__printf__, 3, 4)))
580 heim_log_facility *fac;
583 if (context == NULL ||
584 (fac = heim_get_debug_dest(context)) == NULL)
588 heim_vlog(context, fac, level, fmt, ap);
593 heim_vdebug(heim_context context,
597 __attribute__ ((__format__ (__printf__, 3, 0)))
599 heim_log_facility *fac;
601 if (context == NULL ||
602 (fac = heim_get_debug_dest(context)) == NULL)
605 heim_vlog(context, fac, level, fmt, ap);
609 heim_have_debug(heim_context context, int level)
611 heim_log_facility *fac;
613 return (context != NULL &&
614 (fac = heim_get_debug_dest(context)) != NULL);
618 heim_add_warn_dest(heim_context context, const char *program,
619 const char *log_spec)
621 heim_log_facility *fac;
625 if ((fac = heim_get_warn_dest(context)) == NULL) {
626 ret = heim_initlog(context, program, &fac);
629 heim_set_warn_dest(context, fac);
632 ret = heim_addlog_dest(context, fac, log_spec);
639 heim_add_debug_dest(heim_context context, const char *program,
640 const char *log_spec)
642 heim_log_facility *fac;
645 if ((fac = heim_get_debug_dest(context)) == NULL) {
646 ret = heim_initlog(context, program, &fac);
649 heim_set_debug_dest(context, fac);
652 ret = heim_addlog_dest(context, fac, log_spec);
659 fmtkv(int flags, const char *k, const char *fmt, va_list ap)
660 __attribute__ ((__format__ (__printf__, 3, 0)))
668 int ret = vasprintf(&buf1, fmt, ap);
669 if (ret < 0 || !buf1)
672 j = asprintf(&buf2, "%s=%s", k, buf1);
677 /* We optionally eat the whitespace. */
679 if (flags & HEIM_SVC_AUDIT_EATWHITE) {
680 for (i=0, j=0; buf2[i]; i++)
681 if (buf2[i] != ' ' && buf2[i] != '\t')
686 if (flags & (HEIM_SVC_AUDIT_VIS | HEIM_SVC_AUDIT_VISLAST)) {
687 int vis_flags = VIS_CSTYLE | VIS_OCTAL | VIS_NL;
689 if (flags & HEIM_SVC_AUDIT_VIS)
690 vis_flags |= VIS_WHITE;
691 buf3 = malloc((j + 1) * 4 + 1);
693 strvisx(buf3, buf2, j, vis_flags);
700 str = heim_string_create(buf3);
706 heim_audit_vaddreason(heim_svc_req_desc r, const char *fmt, va_list ap)
707 __attribute__ ((__format__ (__printf__, 2, 0)))
711 str = fmtkv(HEIM_SVC_AUDIT_VISLAST, "reason", fmt, ap);
713 heim_log(r->hcontext, r->logf, 1, "heim_audit_vaddreason: "
714 "failed to add reason (out of memory)");
718 heim_log(r->hcontext, r->logf, 7, "heim_audit_vaddreason(): "
719 "adding reason %s", heim_string_get_utf8(str));
723 str2 = heim_string_create_with_format("%s: %s",
724 heim_string_get_utf8(str),
725 heim_string_get_utf8(r->reason));
731 heim_release(r->reason);
736 heim_audit_addreason(heim_svc_req_desc r, const char *fmt, ...)
737 __attribute__ ((__format__ (__printf__, 2, 3)))
742 heim_audit_vaddreason(r, fmt, ap);
747 * append_token adds a token which is optionally a kv-pair and it
748 * also optionally eats the whitespace. If k == NULL, then it's
753 heim_audit_vaddkv(heim_svc_req_desc r, int flags, const char *k,
754 const char *fmt, va_list ap)
755 __attribute__ ((__format__ (__printf__, 4, 0)))
759 str = fmtkv(flags, k, fmt, ap);
761 heim_log(r->hcontext, r->logf, 1, "heim_audit_vaddkv: "
762 "failed to add kv pair (out of memory)");
766 heim_log(r->hcontext, r->logf, 7, "heim_audit_vaddkv(): "
767 "adding kv pair %s", heim_string_get_utf8(str));
768 heim_array_append_value(r->kv, str);
773 heim_audit_addkv(heim_svc_req_desc r, int flags, const char *k,
774 const char *fmt, ...)
775 __attribute__ ((__format__ (__printf__, 4, 5)))
780 heim_audit_vaddkv(r, flags, k, fmt, ap);
785 heim_audit_addkv_timediff(heim_svc_req_desc r, const char *k,
786 const struct timeval *start,
787 const struct timeval *end)
791 const char *sign = "";
793 if (end->tv_sec > start->tv_sec ||
794 (end->tv_sec == start->tv_sec && end->tv_usec >= start->tv_usec)) {
795 sec = end->tv_sec - start->tv_sec;
796 usec = end->tv_usec - start->tv_usec;
798 sec = start->tv_sec - end->tv_sec;
799 usec = start->tv_usec - end->tv_usec;
808 heim_audit_addkv(r, 0, k, "%s%ld.%06d", sign, sec, usec);
812 heim_audit_trail(heim_svc_req_desc r, heim_error_code ret, const char *retname)
816 char retvalbuf[30]; /* Enough for UNKNOWN-%d */
820 #define CASE(x) case x : retval = #x; break
823 } else switch (ret ? ret : r->ret) {
831 /* Wish we had a com_err number->symbolic name function */
832 (void) snprintf(retvalbuf, sizeof(retvalbuf), "UNKNOWN-%d", ret);
837 heim_audit_addkv_timediff(r, "elapsed", &r->tv_start, &r->tv_end);
838 if (r->e_text && r->kv)
839 heim_audit_addkv(r, HEIM_SVC_AUDIT_VIS, "e-text", "%s", r->e_text);
841 nelem = r->kv ? heim_array_get_length(r->kv) : 0;
842 for (i=0, j=0; i < nelem; i++) {
846 /* We know these are strings... */
847 s = heim_array_get_value(r->kv, i);
848 kvpair = heim_string_get_utf8(s);
850 if (j < sizeof(kvbuf) - 1)
852 for (; *kvpair && j < sizeof(kvbuf) - 1; j++)
853 kvbuf[j] = *kvpair++;
857 heim_log(r->hcontext, r->logf, 3, "%s %s %s %s %s%s%s%s",
858 r->reqtype, retval, r->from,
859 r->cname ? r->cname : "<unknown>",
860 r->sname ? r->sname : "<unknown>",
861 kvbuf, r->reason ? " " : "",
862 r->reason ? heim_string_get_utf8(r->reason) : "");