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 : }
|