LCOV - code coverage report
Current view: top level - util - debug.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 71 149 47.7 %
Date: 2016-06-29 Functions: 8 14 57.1 %

          Line data    Source code
       1             : /*
       2             :     Authors:
       3             :         Simo Sorce <ssorce@redhat.com>
       4             :         Stephen Gallagher <sgallagh@redhat.com>
       5             : 
       6             :     Copyright (C) 2009 Red Hat
       7             : 
       8             :     This program is free software; you can redistribute it and/or modify
       9             :     it under the terms of the GNU General Public License as published by
      10             :     the Free Software Foundation; either version 3 of the License, or
      11             :     (at your option) any later version.
      12             : 
      13             :     This program is distributed in the hope that it will be useful,
      14             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :     GNU General Public License for more details.
      17             : 
      18             :     You should have received a copy of the GNU General Public License
      19             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20             : */
      21             : 
      22             : #include "config.h"
      23             : 
      24             : #include <stdio.h>
      25             : #include <stdarg.h>
      26             : #include <stdlib.h>
      27             : #include <fcntl.h>
      28             : 
      29             : #include <sys/types.h>
      30             : #include <sys/stat.h>
      31             : #include <sys/time.h>
      32             : 
      33             : #ifdef WITH_JOURNALD
      34             : #include <systemd/sd-journal.h>
      35             : #endif
      36             : 
      37             : #include "util/util.h"
      38             : 
      39             : const char *debug_prg_name = "sssd";
      40             : 
      41             : int debug_level = SSSDBG_UNRESOLVED;
      42             : int debug_timestamps = SSSDBG_TIMESTAMP_UNRESOLVED;
      43             : int debug_microseconds = SSSDBG_MICROSECONDS_UNRESOLVED;
      44             : int debug_to_file = 0;
      45             : int debug_to_stderr = 0;
      46             : const char *debug_log_file = "sssd";
      47             : FILE *debug_file = NULL;
      48             : 
      49          60 : errno_t set_debug_file_from_fd(const int fd)
      50             : {
      51             :     FILE *dummy;
      52             :     errno_t ret;
      53             : 
      54          60 :     errno = 0;
      55          60 :     dummy = fdopen(fd, "a");
      56          60 :     if (dummy == NULL) {
      57           0 :         ret = errno;
      58           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
      59             :               "fdopen failed [%d][%s].\n", ret, strerror(ret));
      60           0 :         sss_log(SSS_LOG_ERR,
      61             :                 "Could not open debug file descriptor [%d]. "
      62             :                 "Debug messages will not be written to the file "
      63             :                 "for this child process [%s][%s]\n",
      64             :                 fd, debug_prg_name, strerror(ret));
      65           0 :         return ret;
      66             :     }
      67             : 
      68          60 :     debug_file = dummy;
      69             : 
      70          60 :     return EOK;
      71             : }
      72             : 
      73           0 : int get_fd_from_debug_file(void)
      74             : {
      75           0 :     if (debug_file == NULL) {
      76           0 :         return STDERR_FILENO;
      77             :     }
      78             : 
      79           0 :     return fileno(debug_file);
      80             : }
      81             : 
      82          48 : int debug_convert_old_level(int old_level)
      83             : {
      84          48 :     if ((old_level != 0) && !(old_level & 0x000F))
      85          11 :         return old_level;
      86             : 
      87          37 :     int new_level = SSSDBG_FATAL_FAILURE;
      88             : 
      89          37 :     if (old_level <= 0)
      90          28 :         return new_level;
      91             : 
      92           9 :     if (old_level >= 1)
      93           9 :         new_level |= SSSDBG_CRIT_FAILURE;
      94             : 
      95           9 :     if (old_level >= 2)
      96           8 :         new_level |= SSSDBG_OP_FAILURE;
      97             : 
      98           9 :     if (old_level >= 3)
      99           7 :         new_level |= SSSDBG_MINOR_FAILURE;
     100             : 
     101           9 :     if (old_level >= 4)
     102           6 :         new_level |= SSSDBG_CONF_SETTINGS;
     103             : 
     104           9 :     if (old_level >= 5)
     105           5 :         new_level |= SSSDBG_FUNC_DATA;
     106             : 
     107           9 :     if (old_level >= 6)
     108           4 :         new_level |= SSSDBG_TRACE_FUNC;
     109             : 
     110           9 :     if (old_level >= 7)
     111           3 :         new_level |= SSSDBG_TRACE_LIBS;
     112             : 
     113           9 :     if (old_level >= 8)
     114           2 :         new_level |= SSSDBG_TRACE_INTERNAL;
     115             : 
     116           9 :     if (old_level >= 9)
     117           1 :         new_level |= SSSDBG_TRACE_ALL | SSSDBG_BE_FO;
     118             : 
     119           9 :     return new_level;
     120             : }
     121             : 
     122         139 : static void debug_fflush(void)
     123             : {
     124         139 :     fflush(debug_file ? debug_file : stderr);
     125         139 : }
     126             : 
     127         278 : static void debug_vprintf(const char *format, va_list ap)
     128             : {
     129         278 :     vfprintf(debug_file ? debug_file : stderr, format, ap);
     130         278 : }
     131             : 
     132             : static void debug_printf(const char *format, ...)
     133             :                 SSS_ATTRIBUTE_PRINTF(1, 2);
     134             : 
     135         139 : static void debug_printf(const char *format, ...)
     136             : {
     137             :     va_list ap;
     138             : 
     139         139 :     va_start(ap, format);
     140             : 
     141         139 :     debug_vprintf(format, ap);
     142             : 
     143         139 :     va_end(ap);
     144         139 : }
     145             : 
     146             : #ifdef WITH_JOURNALD
     147             : errno_t journal_send(const char *file,
     148             :         long line,
     149             :         const char *function,
     150             :         int level,
     151             :         const char *format,
     152             :         va_list ap)
     153             : {
     154             :     errno_t ret;
     155             :     int res;
     156             :     char *message = NULL;
     157             :     char *code_file = NULL;
     158             :     char *code_line = NULL;
     159             :     const char *domain;
     160             : 
     161             :     /* First, evaluate the message to be sent */
     162             :     ret = vasprintf(&message, format, ap);
     163             :     if (ret == -1) {
     164             :         /* ENOMEM, just return */
     165             :         return ENOMEM;
     166             :     }
     167             : 
     168             :     res = asprintf(&code_file, "CODE_FILE=%s", file);
     169             :     if (res == -1) {
     170             :         ret = ENOMEM;
     171             :         goto journal_done;
     172             :     }
     173             : 
     174             :     res = asprintf(&code_line, "CODE_LINE=%ld", line);
     175             :     if (res == -1) {
     176             :         ret = ENOMEM;
     177             :         goto journal_done;
     178             :     }
     179             : 
     180             :     /* If this log message was sent from a provider,
     181             :      * track the domain.
     182             :      */
     183             :     domain = getenv(SSS_DOM_ENV);
     184             :     if (domain == NULL) {
     185             :         domain = "";
     186             :     }
     187             : 
     188             :     /* Send the log message to journald, specifying the
     189             :      * source code location and other tracking data.
     190             :      */
     191             :     res = sd_journal_send_with_location(
     192             :             code_file, code_line, function,
     193             :             "MESSAGE=%s", message,
     194             :             "PRIORITY=%i", LOG_DEBUG,
     195             :             "SSSD_DOMAIN=%s", domain,
     196             :             "SSSD_PRG_NAME=%s", debug_prg_name,
     197             :             "SSSD_DEBUG_LEVEL=%x", level,
     198             :             NULL);
     199             :     ret = -res;
     200             : 
     201             : journal_done:
     202             :     free(code_line);
     203             :     free(code_file);
     204             :     free(message);
     205             :     return ret;
     206             : }
     207             : #endif /* WiTH_JOURNALD */
     208             : 
     209         139 : void sss_vdebug_fn(const char *file,
     210             :                    long line,
     211             :                    const char *function,
     212             :                    int level,
     213             :                    int flags,
     214             :                    const char *format,
     215             :                    va_list ap)
     216             : {
     217             :     struct timeval tv;
     218             :     struct tm *tm;
     219             :     char datetime[20];
     220             :     int year;
     221             : 
     222             : #ifdef WITH_JOURNALD
     223             :     errno_t ret;
     224             :     va_list ap_fallback;
     225             : 
     226             :     if (!debug_file && !debug_to_stderr) {
     227             :         /* If we are not outputting logs to files, we should be sending them
     228             :          * to journald.
     229             :          * NOTE: on modern systems, this is where stdout/stderr will end up
     230             :          * from system services anyway. The only difference here is that we
     231             :          * can also provide extra structuring data to make it more easily
     232             :          * searchable.
     233             :          */
     234             :         va_copy(ap_fallback, ap);
     235             :         ret = journal_send(file, line, function, level, format, ap);
     236             :         if (ret != EOK) {
     237             :             /* Emergency fallback, send to STDERR */
     238             :             debug_vprintf(format, ap_fallback);
     239             :             debug_fflush();
     240             :         }
     241             :         va_end(ap_fallback);
     242             :         return;
     243             :     }
     244             : #endif
     245             : 
     246         139 :     if (debug_timestamps) {
     247         129 :         gettimeofday(&tv, NULL);
     248         129 :         tm = localtime(&tv.tv_sec);
     249         129 :         year = tm->tm_year + 1900;
     250             :         /* get date time without year */
     251         129 :         memcpy(datetime, ctime(&tv.tv_sec), 19);
     252         129 :         datetime[19] = '\0';
     253         129 :         if (debug_microseconds) {
     254         119 :             debug_printf("(%s:%.6ld %d) [%s] [%s] (%#.4x): ",
     255             :                          datetime, tv.tv_usec,
     256             :                          year, debug_prg_name,
     257             :                          function, level);
     258             :         } else {
     259          10 :             debug_printf("(%s %d) [%s] [%s] (%#.4x): ",
     260             :                          datetime, year,
     261             :                          debug_prg_name, function, level);
     262             :         }
     263             :     } else {
     264          10 :         debug_printf("[%s] [%s] (%#.4x): ",
     265             :                      debug_prg_name, function, level);
     266             :     }
     267             : 
     268         139 :     debug_vprintf(format, ap);
     269         139 :     if (flags & APPEND_LINE_FEED) {
     270           0 :         debug_printf("\n");
     271             :     }
     272         139 :     debug_fflush();
     273         139 : }
     274             : 
     275         139 : void sss_debug_fn(const char *file,
     276             :                   long line,
     277             :                   const char *function,
     278             :                   int level,
     279             :                   const char *format, ...)
     280             : {
     281             :     va_list ap;
     282             : 
     283         139 :     va_start(ap, format);
     284         139 :     sss_vdebug_fn(file, line, function, level, 0, format, ap);
     285         139 :     va_end(ap);
     286         139 : }
     287             : 
     288      108636 : void ldb_debug_messages(void *context, enum ldb_debug_level level,
     289             :                         const char *fmt, va_list ap)
     290             : {
     291      108636 :     int loglevel = SSSDBG_UNRESOLVED;
     292             : 
     293      108636 :     switch(level) {
     294             :     case LDB_DEBUG_FATAL:
     295           0 :         loglevel = SSSDBG_FATAL_FAILURE;
     296           0 :         break;
     297             :     case LDB_DEBUG_ERROR:
     298           0 :         loglevel = SSSDBG_CRIT_FAILURE;
     299           0 :         break;
     300             :     case LDB_DEBUG_WARNING:
     301        1161 :         loglevel = SSSDBG_TRACE_FUNC;
     302        1161 :         break;
     303             :     case LDB_DEBUG_TRACE:
     304      107475 :         loglevel = SSSDBG_TRACE_ALL;
     305      107475 :         break;
     306             :     }
     307             : 
     308      108636 :     if (DEBUG_IS_SET(loglevel)) {
     309           0 :         sss_vdebug_fn(__FILE__, __LINE__, "ldb", loglevel, APPEND_LINE_FEED,
     310             :                       fmt, ap);
     311             :     }
     312      108636 : }
     313             : 
     314             : /* In cases SSSD used to run as the root user, but runs as the SSSD user now,
     315             :  * we need to chown the log files
     316             :  */
     317           0 : int chown_debug_file(const char *filename,
     318             :                      uid_t uid, gid_t gid)
     319             : {
     320             :     char *logpath;
     321             :     const char *log_file;
     322             :     errno_t ret;
     323             : 
     324           0 :     if (filename == NULL) {
     325           0 :         log_file = debug_log_file;
     326             :     } else {
     327           0 :         log_file = filename;
     328             :     }
     329             : 
     330           0 :     ret = asprintf(&logpath, "%s/%s.log", LOG_PATH, log_file);
     331           0 :     if (ret == -1) {
     332           0 :         return ENOMEM;
     333             :     }
     334             : 
     335           0 :     ret = chown(logpath, uid, gid);
     336           0 :     free(logpath);
     337           0 :     if (ret != 0) {
     338           0 :         ret = errno;
     339           0 :         if (ret == ENOENT) {
     340             :             /* Log does not exist. We might log to journald
     341             :              * or starting for first time.
     342             :              * It's not a failure. */
     343           0 :             return EOK;
     344             :         }
     345             : 
     346           0 :         DEBUG(SSSDBG_FATAL_FAILURE, "chown failed for [%s]: [%d]\n",
     347             :               log_file, ret);
     348           0 :         return ret;
     349             :     }
     350             : 
     351           0 :     return EOK;
     352             : }
     353             : 
     354           0 : int open_debug_file_ex(const char *filename, FILE **filep, bool want_cloexec)
     355             : {
     356           0 :     FILE *f = NULL;
     357             :     char *logpath;
     358             :     const char *log_file;
     359             :     mode_t old_umask;
     360             :     int ret;
     361             :     int debug_fd;
     362             :     int flags;
     363             : 
     364           0 :     if (filename == NULL) {
     365           0 :         log_file = debug_log_file;
     366             :     } else {
     367           0 :         log_file = filename;
     368             :     }
     369             : 
     370           0 :     ret = asprintf(&logpath, "%s/%s.log", LOG_PATH, log_file);
     371           0 :     if (ret == -1) {
     372           0 :         return ENOMEM;
     373             :     }
     374             : 
     375           0 :     if (debug_file && !filep) fclose(debug_file);
     376             : 
     377           0 :     old_umask = umask(SSS_DFL_UMASK);
     378           0 :     errno = 0;
     379           0 :     f = fopen(logpath, "a");
     380           0 :     if (f == NULL) {
     381           0 :         sss_log(SSS_LOG_EMERG, "Could not open file [%s]. Error: [%d][%s]\n",
     382           0 :                                logpath, errno, strerror(errno));
     383           0 :         free(logpath);
     384           0 :         return EIO;
     385             :     }
     386           0 :     umask(old_umask);
     387             : 
     388           0 :     debug_fd = fileno(f);
     389           0 :     if (debug_fd == -1) {
     390           0 :         fclose(f);
     391           0 :         free(logpath);
     392           0 :         return EIO;
     393             :     }
     394             : 
     395           0 :     if(want_cloexec) {
     396           0 :         flags = fcntl(debug_fd, F_GETFD, 0);
     397           0 :         (void) fcntl(debug_fd, F_SETFD, flags | FD_CLOEXEC);
     398             :     }
     399             : 
     400           0 :     if (filep == NULL) {
     401           0 :         debug_file = f;
     402             :     } else {
     403           0 :         *filep = f;
     404             :     }
     405           0 :     free(logpath);
     406           0 :     return EOK;
     407             : }
     408             : 
     409           0 : int open_debug_file(void)
     410             : {
     411           0 :     return open_debug_file_ex(NULL, NULL, true);
     412             : }
     413             : 
     414           0 : int rotate_debug_files(void)
     415             : {
     416             :     int ret;
     417             :     errno_t error;
     418             : 
     419           0 :     if (!debug_to_file) return EOK;
     420             : 
     421             :     do {
     422           0 :         error = 0;
     423           0 :         ret = fclose(debug_file);
     424           0 :         if (ret != 0) {
     425           0 :             error = errno;
     426             :         }
     427             : 
     428             :         /* Check for EINTR, which means we should retry
     429             :          * because the system call was interrupted by a
     430             :          * signal
     431             :          */
     432           0 :     } while (error == EINTR);
     433             : 
     434           0 :     if (error != 0) {
     435             :         /* Even if we were unable to close the debug log, we need to make
     436             :          * sure that we open up a new one. Log rotation will remove the
     437             :          * current file, so all debug messages will be disappearing.
     438             :          *
     439             :          * We should write an error to the syslog warning of the resource
     440             :          * leak and then proceed with opening the new file.
     441             :          */
     442           0 :         sss_log(SSS_LOG_ALERT, "Could not close debug file [%s]. [%d][%s]\n",
     443             :                                debug_log_file, error, strerror(error));
     444           0 :         sss_log(SSS_LOG_ALERT, "Attempting to open new file anyway. "
     445             :                                "Be aware that this is a resource leak\n");
     446             :     }
     447             : 
     448           0 :     debug_file = NULL;
     449             : 
     450           0 :     return open_debug_file();
     451             : }
     452             : 
     453           0 : void talloc_log_fn(const char *message)
     454             : {
     455           0 :     DEBUG(SSSDBG_FATAL_FAILURE, "%s\n", message);
     456           0 : }

Generated by: LCOV version 1.10