1/*
2 * Copyright (C) 2009-2011 Julien BLACHE <jb@jblache.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18
19#ifdef HAVE_CONFIG_H
20# include <config.h>
21#endif
22
23#include <stdio.h>
24#include <unistd.h>
25#include <stdarg.h>
26#include <string.h>
27#include <time.h>
28#include <errno.h>
29#include <sys/stat.h>
30#include <pthread.h>
31
32#include <event.h>
33
34#include <libavutil/log.h>
35
36#include "conffile.h"
37#include "logger.h"
38
39
40static pthread_mutex_t logger_lck = PTHREAD_MUTEX_INITIALIZER;
41static int logdomains;
42static int threshold;
43static int console;
44static char *logfilename;
45static FILE *logfile;
46static char *labels[] = { "config", "daap", "db", "httpd", "main", "mdns", "misc", "rsp", "scan", "xcode", "event", "remote", "dacp", "ffmpeg", "artwork", "player", "raop", "laudio", "dmap", "dbperf" };
47
48
49static int
50set_logdomains(char *domains)
51{
52  char *ptr;
53  char *d;
54  int i;
55
56  logdomains = 0;
57
58  while ((d = strtok_r(domains, " ,", &ptr)))
59    {
60      domains = NULL;
61
62      for (i = 0; i < N_LOGDOMAINS; i++)
63	{
64	  if (strcmp(d, labels[i]) == 0)
65	    {
66	      logdomains |= (1 << i);
67	      break;
68	    }
69	}
70
71      if (i == N_LOGDOMAINS)
72	{
73	  fprintf(stderr, "Error: unknown log domain '%s'\n", d);
74	  return -1;
75	}
76    }
77
78  return 0;
79}
80
81static void
82vlogger(int severity, int domain, const char *fmt, va_list args)
83{
84  va_list ap;
85  char stamp[32];
86  time_t t;
87  int ret;
88
89
90  if (!((1 << domain) & logdomains) || (severity > threshold))
91    return;
92
93  pthread_mutex_lock(&logger_lck);
94
95  if (!logfile && !console)
96    {
97      pthread_mutex_unlock(&logger_lck);
98      return;
99    }
100
101  if (logfile)
102    {
103      t = time(NULL);
104      ret = strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&t));
105      if (ret == 0)
106	stamp[0] = '\0';
107
108      fprintf(logfile, "[%s] %8s: ", stamp, labels[domain]);
109
110      va_copy(ap, args);
111      vfprintf(logfile, fmt, ap);
112      va_end(ap);
113
114      fflush(logfile);
115    }
116
117  if (1)
118    {
119      fprintf(stderr, "%8s: ", labels[domain]);
120
121      va_copy(ap, args);
122      vfprintf(stderr, fmt, ap);
123      va_end(ap);
124    }
125
126  pthread_mutex_unlock(&logger_lck);
127}
128
129void
130DPRINTF(int severity, int domain, const char *fmt, ...)
131{
132  va_list ap;
133
134  va_start(ap, fmt);
135  vlogger(severity, domain, fmt, ap);
136  va_end(ap);
137}
138
139void
140logger_ffmpeg(void *ptr, int level, const char *fmt, va_list ap)
141{
142  int severity;
143
144  /* Can't use a switch() because some definitions have the same value */
145  if ((level == AV_LOG_FATAL) || (level == AV_LOG_ERROR))
146    severity = E_LOG;
147  else if ((level == AV_LOG_WARNING) || (level == AV_LOG_INFO) || (level == AV_LOG_VERBOSE))
148    severity = E_WARN;
149  else if (level == AV_LOG_DEBUG)
150    severity = E_DBG;
151  else if (level == AV_LOG_QUIET)
152    severity = E_SPAM;
153  else
154    severity = E_LOG;
155
156  vlogger(severity, L_FFMPEG, fmt, ap);
157}
158
159void
160logger_libevent(int severity, const char *msg)
161{
162  switch (severity)
163    {
164      case _EVENT_LOG_DEBUG:
165	severity = E_DBG;
166	break;
167
168      case _EVENT_LOG_ERR:
169	severity = E_LOG;
170	break;
171
172      case _EVENT_LOG_WARN:
173	severity = E_WARN;
174	break;
175
176      case _EVENT_LOG_MSG:
177	severity = E_INFO;
178	break;
179
180      default:
181	severity = E_LOG;
182	break;
183    }
184
185  DPRINTF(severity, L_EVENT, "%s\n", msg);
186}
187
188#ifdef LAUDIO_USE_ALSA
189void
190logger_alsa(const char *file, int line, const char *function, int err, const char *fmt, ...)
191{
192  va_list ap;
193
194  va_start(ap, fmt);
195  vlogger(E_LOG, L_LAUDIO, fmt, ap);
196  va_end(ap);
197}
198#endif /* LAUDIO_USE_ALSA */
199
200void
201logger_reinit(void)
202{
203  FILE *fp;
204
205  if (!logfile)
206    return;
207
208  pthread_mutex_lock(&logger_lck);
209
210  fp = fopen(logfilename, "a");
211  if (!fp)
212    {
213      fprintf(logfile, "Could not reopen logfile: %s\n", strerror(errno));
214
215      goto out;
216    }
217
218  fclose(logfile);
219  logfile = fp;
220
221 out:
222  pthread_mutex_unlock(&logger_lck);
223}
224
225
226/* The functions below are used at init time with a single thread running */
227void
228logger_domains(void)
229{
230  int i;
231
232  fprintf(stdout, "%s", labels[0]);
233
234  for (i = 1; i < N_LOGDOMAINS; i++)
235    fprintf(stdout, ", %s", labels[i]);
236
237  fprintf(stdout, "\n");
238}
239
240void
241logger_detach(void)
242{
243  console = 0;
244}
245
246int
247logger_init(char *file, char *domains, int severity)
248{
249  int ret;
250
251  if ((sizeof(labels) / sizeof(labels[0])) != N_LOGDOMAINS)
252    {
253      fprintf(stderr, "WARNING: log domains do not match\n");
254
255      return -1;
256    }
257
258  console = 1;
259  threshold = severity;
260
261  if (domains)
262    {
263      ret = set_logdomains(domains);
264      if (ret < 0)
265	return ret;
266    }
267  else
268    logdomains = ~0;
269
270  if (!file)
271    return 0;
272
273  logfile = fopen(file, "a");
274  if (!logfile)
275    {
276      fprintf(stderr, "Could not open logfile %s: %s\n", file, strerror(errno));
277
278      return -1;
279    }
280
281  ret = fchown(fileno(logfile), runas_uid, 0);
282  if (ret < 0)
283    fprintf(stderr, "Failed to set ownership on logfile: %s\n", strerror(errno));
284
285  ret = fchmod(fileno(logfile), 0644);
286  if (ret < 0)
287    fprintf(stderr, "Failed to set permissions on logfile: %s\n", strerror(errno));
288
289  logfilename = file;
290
291  return 0;
292}
293
294void
295logger_deinit(void)
296{
297  if (logfile)
298    fclose(logfile);
299}
300