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 36 : int debug_convert_old_level(int old_level)
83 : {
84 36 : if ((old_level != 0) && !(old_level & 0x000F))
85 14 : return old_level;
86 :
87 22 : int new_level = SSSDBG_FATAL_FAILURE;
88 :
89 22 : if (old_level <= 0)
90 13 : 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 106 : static void debug_fflush(void)
123 : {
124 106 : fflush(debug_file ? debug_file : stderr);
125 106 : }
126 :
127 212 : static void debug_vprintf(const char *format, va_list ap)
128 : {
129 212 : vfprintf(debug_file ? debug_file : stderr, format, ap);
130 212 : }
131 :
132 : static void debug_printf(const char *format, ...)
133 : SSS_ATTRIBUTE_PRINTF(1, 2);
134 :
135 106 : static void debug_printf(const char *format, ...)
136 : {
137 : va_list ap;
138 :
139 106 : va_start(ap, format);
140 :
141 106 : debug_vprintf(format, ap);
142 :
143 106 : va_end(ap);
144 106 : }
145 :
146 : #ifdef WITH_JOURNALD
147 20 : 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 20 : char *message = NULL;
157 20 : char *code_file = NULL;
158 20 : char *code_line = NULL;
159 : const char *domain;
160 :
161 : /* First, evaluate the message to be sent */
162 20 : ret = vasprintf(&message, format, ap);
163 20 : if (ret == -1) {
164 : /* ENOMEM, just return */
165 0 : return ENOMEM;
166 : }
167 :
168 20 : res = asprintf(&code_file, "CODE_FILE=%s", file);
169 20 : if (res == -1) {
170 0 : ret = ENOMEM;
171 0 : goto journal_done;
172 : }
173 :
174 20 : res = asprintf(&code_line, "CODE_LINE=%ld", line);
175 20 : if (res == -1) {
176 0 : ret = ENOMEM;
177 0 : goto journal_done;
178 : }
179 :
180 : /* If this log message was sent from a provider,
181 : * track the domain.
182 : */
183 20 : domain = getenv(SSS_DOM_ENV);
184 20 : if (domain == NULL) {
185 20 : domain = "";
186 : }
187 :
188 : /* Send the log message to journald, specifying the
189 : * source code location and other tracking data.
190 : */
191 20 : 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 20 : ret = -res;
200 :
201 : journal_done:
202 20 : free(code_line);
203 20 : free(code_file);
204 20 : free(message);
205 20 : return ret;
206 : }
207 : #endif /* WiTH_JOURNALD */
208 :
209 126 : void debug_fn(const char *file,
210 : long line,
211 : const char *function,
212 : int level,
213 : const char *format, ...)
214 : {
215 : va_list ap;
216 : struct timeval tv;
217 : struct tm *tm;
218 : char datetime[20];
219 : int year;
220 :
221 : #ifdef WITH_JOURNALD
222 : errno_t ret;
223 : va_list ap_fallback;
224 :
225 126 : if (!debug_file && !debug_to_stderr) {
226 : /* If we are not outputting logs to files, we should be sending them
227 : * to journald.
228 : * NOTE: on modern systems, this is where stdout/stderr will end up
229 : * from system services anyway. The only difference here is that we
230 : * can also provide extra structuring data to make it more easily
231 : * searchable.
232 : */
233 20 : va_start(ap, format);
234 20 : va_copy(ap_fallback, ap);
235 20 : ret = journal_send(file, line, function, level, format, ap);
236 20 : va_end(ap);
237 20 : if (ret != EOK) {
238 : /* Emergency fallback, send to STDERR */
239 0 : debug_vprintf(format, ap_fallback);
240 0 : debug_fflush();
241 : }
242 20 : va_end(ap_fallback);
243 20 : return;
244 : }
245 : #endif
246 :
247 106 : if (debug_timestamps) {
248 96 : gettimeofday(&tv, NULL);
249 96 : tm = localtime(&tv.tv_sec);
250 96 : year = tm->tm_year + 1900;
251 : /* get date time without year */
252 96 : memcpy(datetime, ctime(&tv.tv_sec), 19);
253 96 : datetime[19] = '\0';
254 96 : if (debug_microseconds) {
255 86 : debug_printf("(%s:%.6ld %d) [%s] [%s] (%#.4x): ",
256 : datetime, tv.tv_usec,
257 : year, debug_prg_name,
258 : function, level);
259 : } else {
260 10 : debug_printf("(%s %d) [%s] [%s] (%#.4x): ",
261 : datetime, year,
262 : debug_prg_name, function, level);
263 : }
264 : } else {
265 10 : debug_printf("[%s] [%s] (%#.4x): ",
266 : debug_prg_name, function, level);
267 : }
268 :
269 106 : va_start(ap, format);
270 106 : debug_vprintf(format, ap);
271 106 : va_end(ap);
272 106 : debug_fflush();
273 : }
274 :
275 74792 : void ldb_debug_messages(void *context, enum ldb_debug_level level,
276 : const char *fmt, va_list ap)
277 : {
278 74792 : int loglevel = SSSDBG_UNRESOLVED;
279 : int ret;
280 74792 : char * message = NULL;
281 :
282 74792 : switch(level) {
283 : case LDB_DEBUG_FATAL:
284 0 : loglevel = SSSDBG_FATAL_FAILURE;
285 0 : break;
286 : case LDB_DEBUG_ERROR:
287 0 : loglevel = SSSDBG_CRIT_FAILURE;
288 0 : break;
289 : case LDB_DEBUG_WARNING:
290 1081 : loglevel = SSSDBG_TRACE_FUNC;
291 1081 : break;
292 : case LDB_DEBUG_TRACE:
293 73711 : loglevel = SSSDBG_TRACE_ALL;
294 73711 : break;
295 : }
296 :
297 74792 : ret = vasprintf(&message, fmt, ap);
298 74792 : if (ret < 0) {
299 : /* ENOMEM */
300 0 : return;
301 : }
302 :
303 74792 : if (DEBUG_IS_SET(loglevel))
304 0 : debug_fn(__FILE__, __LINE__, "ldb", loglevel, "%s\n", message);
305 :
306 74792 : free(message);
307 : }
308 :
309 : /* In cases SSSD used to run as the root user, but runs as the SSSD user now,
310 : * we need to chown the log files
311 : */
312 3 : int chown_debug_file(const char *filename,
313 : uid_t uid, gid_t gid)
314 : {
315 : char *logpath;
316 : const char *log_file;
317 : errno_t ret;
318 :
319 3 : if (filename == NULL) {
320 3 : log_file = debug_log_file;
321 : } else {
322 0 : log_file = filename;
323 : }
324 :
325 3 : ret = asprintf(&logpath, "%s/%s.log", LOG_PATH, log_file);
326 3 : if (ret == -1) {
327 0 : return ENOMEM;
328 : }
329 :
330 3 : ret = chown(logpath, uid, gid);
331 3 : free(logpath);
332 3 : if (ret != 0) {
333 3 : ret = errno;
334 3 : DEBUG(SSSDBG_FATAL_FAILURE, "chown failed for [%s]: [%d]\n",
335 : log_file, ret);
336 3 : return ret;
337 : }
338 :
339 0 : return EOK;
340 : }
341 :
342 0 : int open_debug_file_ex(const char *filename, FILE **filep, bool want_cloexec)
343 : {
344 0 : FILE *f = NULL;
345 : char *logpath;
346 : const char *log_file;
347 : mode_t old_umask;
348 : int ret;
349 : int debug_fd;
350 : int flags;
351 :
352 0 : if (filename == NULL) {
353 0 : log_file = debug_log_file;
354 : } else {
355 0 : log_file = filename;
356 : }
357 :
358 0 : ret = asprintf(&logpath, "%s/%s.log", LOG_PATH, log_file);
359 0 : if (ret == -1) {
360 0 : return ENOMEM;
361 : }
362 :
363 0 : if (debug_file && !filep) fclose(debug_file);
364 :
365 0 : old_umask = umask(SSS_DFL_UMASK);
366 0 : errno = 0;
367 0 : f = fopen(logpath, "a");
368 0 : if (f == NULL) {
369 0 : sss_log(SSS_LOG_EMERG, "Could not open file [%s]. Error: [%d][%s]\n",
370 0 : logpath, errno, strerror(errno));
371 0 : free(logpath);
372 0 : return EIO;
373 : }
374 0 : umask(old_umask);
375 :
376 0 : debug_fd = fileno(f);
377 0 : if (debug_fd == -1) {
378 0 : fclose(f);
379 0 : free(logpath);
380 0 : return EIO;
381 : }
382 :
383 0 : if(want_cloexec) {
384 0 : flags = fcntl(debug_fd, F_GETFD, 0);
385 0 : (void) fcntl(debug_fd, F_SETFD, flags | FD_CLOEXEC);
386 : }
387 :
388 0 : if (filep == NULL) {
389 0 : debug_file = f;
390 : } else {
391 0 : *filep = f;
392 : }
393 0 : free(logpath);
394 0 : return EOK;
395 : }
396 :
397 0 : int open_debug_file(void)
398 : {
399 0 : return open_debug_file_ex(NULL, NULL, true);
400 : }
401 :
402 0 : int rotate_debug_files(void)
403 : {
404 : int ret;
405 : errno_t error;
406 :
407 0 : if (!debug_to_file) return EOK;
408 :
409 : do {
410 0 : error = 0;
411 0 : ret = fclose(debug_file);
412 0 : if (ret != 0) {
413 0 : error = errno;
414 : }
415 :
416 : /* Check for EINTR, which means we should retry
417 : * because the system call was interrupted by a
418 : * signal
419 : */
420 0 : } while (error == EINTR);
421 :
422 0 : if (error != 0) {
423 : /* Even if we were unable to close the debug log, we need to make
424 : * sure that we open up a new one. Log rotation will remove the
425 : * current file, so all debug messages will be disappearing.
426 : *
427 : * We should write an error to the syslog warning of the resource
428 : * leak and then proceed with opening the new file.
429 : */
430 0 : sss_log(SSS_LOG_ALERT, "Could not close debug file [%s]. [%d][%s]\n",
431 : debug_log_file, error, strerror(error));
432 0 : sss_log(SSS_LOG_ALERT, "Attempting to open new file anyway. "
433 : "Be aware that this is a resource leak\n");
434 : }
435 :
436 0 : debug_file = NULL;
437 :
438 0 : return open_debug_file();
439 : }
440 :
441 0 : void talloc_log_fn(const char *message)
442 : {
443 0 : DEBUG(SSSDBG_FATAL_FAILURE, "%s\n", message);
444 0 : }
|