/** Print a list of valuepairs to the request list. * * @param[in] level Debug level (1-4). * @param[in] request to read logging params from. * @param[in] vp to print. */ void rdebug_pair_list(int level, REQUEST *request, VALUE_PAIR *vp) { vp_cursor_t cursor; char buffer[256]; if (!vp || !request || !request->log.func) return; if (!radlog_debug_enabled(L_DBG, level, request)) return; for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) { /* * Take this opportunity to verify all the VALUE_PAIRs are still valid. */ if (!talloc_get_type(vp, VALUE_PAIR)) { REDEBUG("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp)); fr_log_talloc_report(vp); rad_assert(0); } vp_prints(buffer, sizeof(buffer), vp); RDEBUGX(level, "\t%s", buffer); } }
void vradlog_request(log_type_t type, log_debug_t lvl, REQUEST *request, char const *msg, va_list ap) { size_t len = 0; char const *filename = default_log.file; FILE *fp = NULL; char buffer[10240]; /* The largest config item size, then extra for prefixes and suffixes */ char *p; char const *extra = ""; va_list aq; /* * Debug messages get treated specially. */ if ((type & L_DBG) != 0) { if (!radlog_debug_enabled(type, lvl, request)) { return; } /* * Use the debug output file, if specified, * otherwise leave it as the default log file. */ #ifdef WITH_COMMAND_SOCKET filename = default_log.debug_file; if (!filename) #endif filename = default_log.file; } if (request && filename) { radlog_func_t rl = request->radlog; request->radlog = NULL; /* * This is SLOW! Doing it for every log message * in every request is NOT recommended! */ /* FIXME: escape chars! */ if (radius_xlat(buffer, sizeof(buffer), request, filename, NULL, NULL) < 0) { return; } request->radlog = rl; p = strrchr(buffer, FR_DIR_SEP); if (p) { *p = '\0'; if (rad_mkdir(buffer, S_IRWXU) < 0) { ERROR("Failed creating %s: %s", buffer, fr_syserror(errno)); return; } *p = FR_DIR_SEP; } fp = fopen(buffer, "a"); } /* * Print timestamps to the file. */ if (fp) { time_t timeval; timeval = time(NULL); #ifdef HAVE_GMTIME_R if (log_dates_utc) { struct tm utc; gmtime_r(&timeval, &utc); ASCTIME_R(&utc, buffer, sizeof(buffer) - 1); } else #endif { CTIME_R(&timeval, buffer, sizeof(buffer) - 1); } len = strlen(buffer); p = strrchr(buffer, '\n'); if (p) { p[0] = ' '; p[1] = '\0'; } len += strlcpy(buffer + len, fr_int2str(levels, type, ": "), sizeof(buffer) - len); if (len >= sizeof(buffer)) goto finish; } if (request && request->module[0]) { len = snprintf(buffer + len, sizeof(buffer) - len, "%s : ", request->module); if (len >= sizeof(buffer)) goto finish; } /* * If we don't copy the original ap we get a segfault from vasprintf. This is apparently * due to ap sometimes being implemented with a stack offset which is invalidated if * ap is passed into another function. See here: * http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html * * I don't buy that explanation, but doing a va_copy here does prevent SEGVs seen when * running unit tests which generate errors under CI. */ va_copy(aq, ap); vsnprintf(buffer + len, sizeof(buffer) - len, msg, aq); va_end(aq); finish: switch (type) { case L_DBG_WARN: extra = "WARNING: "; type = L_DBG_WARN_REQ; break; case L_DBG_ERR: extra = "ERROR: "; type = L_DBG_ERR_REQ; break; default: break; } if (!fp) { if (debug_flag > 2) extra = ""; request ? radlog(type, "(%u) %s%s", request->number, extra, buffer) : radlog(type, "%s%s", extra, buffer); } else { if (request) { fprintf(fp, "(%u) %s", request->number, extra); } fputs(buffer, fp); fputc('\n', fp); fclose(fp); } }