1 /* Icecast
2  *
3  * This program is distributed under the GNU General Public License, version 2.
4  * A copy of this license is included with this source.
5  *
6  * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7  *                      Michael Smith <msmith@xiph.org>,
8  *                      oddsock <oddsock@xiph.org>,
9  *                      Karl Heyes <karl@xiph.org>
10  *                      and others (see AUTHORS for details).
11  */
12 
13 #ifdef HAVE_CONFIG_H
14 #include <config.h>
15 #endif
16 
17 #include "compat.h"
18 #include <stdio.h>
19 #include <string.h>
20 
21 #include "thread/thread.h"
22 #include "httpp/httpp.h"
23 
24 #include "connection.h"
25 #include "refbuf.h"
26 #include "client.h"
27 
28 #include "cfgfile.h"
29 #include "logging.h"
30 #include "util.h"
31 #include "errno.h"
32 #include "global.h"
33 
34 #define CATMODULE "logging"
35 
36 void fatal_error (const char *perr);
37 
38 /* the global log descriptors */
39 int errorlog = 0;
40 int playlistlog = 0;
41 
42 /*
43 ** ADDR IDENT USER DATE REQUEST CODE BYTES REFERER AGENT [TIME]
44 **
45 ** ADDR = client->con->ip
46 ** IDENT = always - , we don't support it because it's useless
47 ** USER = client->username
48 ** DATE = _make_date(client->con->con_time)
49 ** REQUEST = build from client->parser
50 ** CODE = client->respcode
51 ** BYTES = client->con->sent_bytes
52 ** REFERER = get from client->parser
53 ** AGENT = get from client->parser
54 ** TIME = timing_get_time() - client->con->con_time
55 */
logging_access_id(access_log * accesslog,client_t * client)56 void logging_access_id (access_log *accesslog, client_t *client)
57 {
58     const char *req = NULL;
59     time_t now;
60     time_t stayed;
61     const char *referrer, *user_agent, *ip = "-";
62     char *username, datebuf[50];
63     char reqbuf[256];
64 
65     if (client->flags & CLIENT_SKIP_ACCESSLOG)
66         return;
67 
68     now = time(NULL);
69 
70     /* build the data */
71     util_get_clf_time (datebuf, sizeof(datebuf), now);
72     if (accesslog->qstr)
73         req = httpp_getvar (client->parser, HTTPP_VAR_RAWURI);
74     if (req == NULL)
75         req = httpp_getvar (client->parser, HTTPP_VAR_URI);
76     /* build the request */
77     snprintf (reqbuf, sizeof(reqbuf), "%.10s %.235s %.5s/%s",
78             httpp_getvar (client->parser, HTTPP_VAR_REQ_TYPE), req,
79             httpp_getvar (client->parser, HTTPP_VAR_PROTOCOL),
80             httpp_getvar (client->parser, HTTPP_VAR_VERSION));
81 
82     stayed = (client->connection.con_time > now) ? 0 : (now - client->connection.con_time); // in case the clock has shifted
83     username = (client->username && client->username[0]) ? util_url_escape (client->username) : strdup("-");
84     referrer = httpp_getvar (client->parser, "referer");
85     user_agent = httpp_getvar (client->parser, "user-agent");
86 
87     if (accesslog->log_ip)
88         ip = client->connection.ip;
89 
90     if (accesslog->type == LOG_ACCESS_CLF_ESC)
91     {
92         char *rq = util_url_escape (reqbuf),
93              *rf = referrer ? util_url_escape (referrer) : strdup ("-"),
94              *ua = user_agent ? util_url_escape (user_agent) : strdup ("-");
95 
96         log_write_direct (accesslog->logid,
97                 "%s - %s %s %s %d %" PRIu64 " %.150s %.150s %lu",
98                 ip, username, datebuf, rq, client->respcode, client->connection.sent_bytes,
99                 rf, ua, (unsigned long)stayed);
100         free (ua);
101         free (rf);
102         free (rq);
103     }
104     else
105     {
106         if (referrer == NULL)           referrer = "-";
107         if (user_agent == NULL)         user_agent = "-";
108 
109         log_write_direct (accesslog->logid,
110                 "%s - %s [%s] \"%s\" %d %" PRIu64 " \"%.150s\" \"%.150s\" %lu",
111                 ip, username, datebuf, reqbuf, client->respcode, client->connection.sent_bytes,
112                 referrer, user_agent, (unsigned long)stayed);
113     }
114     free (username);
115     client->respcode = -1;
116 }
117 
118 
logging_access(client_t * client)119 void logging_access (client_t *client)
120 {
121     ice_config_t *config = config_get_config();
122     logging_access_id (&config->access_log, client);
123     config_release_config ();
124 }
125 
126 
127 /* This function will provide a log of metadata for each
128    mountpoint.  The metadata *must* be in UTF-8, and thus
129    you can assume that the log itself is UTF-8 encoded */
logging_playlist(const char * mount,const char * metadata,long listeners)130 void logging_playlist(const char *mount, const char *metadata, long listeners)
131 {
132     time_t now;
133     char datebuf[128];
134 
135     if (playlistlog == -1) {
136         return;
137     }
138 
139     now = time(NULL);
140 
141     util_get_clf_time (datebuf, sizeof(datebuf), now);
142     /* This format MAY CHANGE OVER TIME.  We are looking into finding a good
143        standard format for this, if you have any ideas, please let us know */
144     log_write_direct (playlistlog, "%s|%s|%ld|%s",
145              datebuf,
146              mount,
147              listeners,
148              metadata);
149 }
150 
151 
logging_preroll(int log_id,const char * intro_name,client_t * client)152 void logging_preroll (int log_id, const char *intro_name, client_t *client)
153 {
154     char datebuf[128];
155 
156     util_get_clf_time (datebuf, sizeof(datebuf), client->worker->current_time.tv_sec);
157 
158     log_write_direct (log_id, "%s|%s|%" PRIu64 "|%s|%ld|%s",
159              datebuf, client->mount, client->connection.id,
160              &client->connection.ip[0], (long)client->intro_offset, intro_name);
161 }
162 
163 
log_parse_failure(void * ctx,const char * fmt,...)164 void log_parse_failure (void *ctx, const char *fmt, ...)
165 {
166     char line [200];
167     va_list ap;
168     char *eol;
169 
170     va_start (ap, fmt);
171     vsnprintf (line, sizeof (line), fmt, ap);
172     eol = strrchr (line, '\n');
173     if (eol) *eol='\0';
174     va_end (ap);
175     log_write (errorlog, 2, "xml/", "parsing", "%s", line);
176 }
177 
178 
recheck_log_file(ice_config_t * config,int * id,const char * file)179 static int recheck_log_file (ice_config_t *config, int *id, const char *file)
180 {
181     char fn [FILENAME_MAX] = "";
182 
183     if (file == NULL)
184     {
185         log_close (*id);
186         *id = -1;
187         return 0;
188     }
189     if (strcmp (file, "-") != 0)
190         snprintf (fn, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, file);
191     if (*id < 0)
192     {
193         *id = log_open (fn);
194         if (*id < 0)
195         {
196             char buf[1024];
197             snprintf (buf,1024, "could not open log %.300s: %s", fn, strerror(errno));
198             fatal_error (buf);
199             return -1;
200         }
201         INFO1 ("Using log file %s", fn);
202         return 0;
203     }
204     log_set_filename (*id, fn);
205     log_reopen (*id);
206     return 0;
207 }
208 
209 
recheck_access_log(ice_config_t * config,struct access_log * access)210 static int recheck_access_log (ice_config_t *config, struct access_log *access)
211 {
212     if (recheck_log_file (config, &access->logid, access->name) < 0)
213         return -1;
214     if (access->logid == -1)
215         return 0; // closed
216     long max_size = (access->size > 10000) ? access->size : config->access_log.size;
217     log_set_trigger (access->logid, max_size);
218     log_set_reopen_after (access->logid, access->duration);
219     if (access->display > 0)
220         log_set_lines_kept (access->logid, access->display);
221     int archive = (access->archive == -1) ? config->access_log.archive : access->archive;
222     log_set_archive_timestamp (access->logid, archive);
223     log_set_level (access->logid, 4);
224     // DEBUG4 ("log %s, size %ld, duration %u, archive %d", access->name, max_size, access->duration, archive);
225     return 0;
226 }
227 
228 
restart_logging(ice_config_t * config)229 int restart_logging (ice_config_t *config)
230 {
231     ice_config_t *current = config_get_config_unlocked();
232     int ret = 0;
233 
234     config->error_log.logid = current->error_log.logid;
235     config->access_log.logid = current->access_log.logid;
236     config->playlist_log.logid = current->playlist_log.logid;
237     config->preroll_log.logid = current->preroll_log.logid;
238 
239     if (recheck_log_file (config, &config->error_log.logid, config->error_log.name) < 0)
240         ret = -1;
241     else
242     {
243         log_set_trigger (config->error_log.logid, config->error_log.size);
244         log_set_reopen_after (config->error_log.logid, config->error_log.duration);
245         if (config->error_log.display > 0)
246             log_set_lines_kept (config->error_log.logid, config->error_log.display);
247         log_set_archive_timestamp (config->error_log.logid, config->error_log.archive);
248         log_set_level (config->error_log.logid, config->error_log.level);
249     }
250     thread_use_log_id (config->error_log.logid);
251     errorlog = config->error_log.logid; /* value stays static so avoid taking the config lock */
252 
253     if (recheck_log_file (config, &config->preroll_log.logid, config->preroll_log.name) < 0)
254         ret = -1;
255     else
256     {
257         log_set_trigger (config->preroll_log.logid, config->preroll_log.size);
258         log_set_reopen_after (config->preroll_log.logid, config->preroll_log.duration);
259         if (config->preroll_log.display > 0)
260             log_set_lines_kept (config->preroll_log.logid, config->preroll_log.display);
261         log_set_archive_timestamp (config->preroll_log.logid, config->preroll_log.archive);
262         log_set_level (config->preroll_log.logid, 4);
263     }
264 
265     if (recheck_access_log (config, &config->access_log) < 0)
266        ret = -1;
267 
268     if (recheck_log_file (config, &config->playlist_log.logid, config->playlist_log.name) < 0)
269         ret = -1;
270     else
271     {
272         log_set_trigger (config->playlist_log.logid, config->playlist_log.size);
273         log_set_reopen_after (config->playlist_log.logid, config->playlist_log.duration);
274         if (config->playlist_log.display > 0)
275             log_set_lines_kept (config->playlist_log.logid, config->playlist_log.display);
276         log_set_archive_timestamp (config->playlist_log.logid, config->playlist_log.archive);
277         log_set_level (config->playlist_log.logid, 4);
278     }
279     playlistlog = config->playlist_log.logid;
280 
281     // any logs for template based mounts
282     if (config->mounts)
283     {
284         mount_proxy *m = config->mounts;
285         while (m)
286         {
287             if (recheck_access_log (config, &m->access_log) < 0)
288                 ret = -1;
289             m = m->next;
290         }
291     }
292     // any logs for specifically named mounts
293     if (config->mounts_tree)
294     {
295         avl_node *node = avl_get_first (config->mounts_tree);
296         while (node)
297         {
298             mount_proxy *m = (mount_proxy *)node->key;
299             node = avl_get_next (node);
300 
301             if (recheck_access_log (config, &m->access_log) < 0)
302                 ret = -1;
303         }
304     }
305     return ret;
306 }
307 
308 
init_logging(ice_config_t * config)309 int init_logging (ice_config_t *config)
310 {
311     worker_logger_init();
312 
313     if (strcmp (config->error_log.name, "-") == 0)
314         config->error_log.logid = log_open_file (stderr);
315     if (strcmp(config->access_log.name, "-") == 0)
316         config->access_log.logid = log_open_file (stderr);
317     return restart_logging (config);
318 }
319 
320 
start_logging(ice_config_t * config)321 int start_logging (ice_config_t *config)
322 {
323     worker_logger (0);
324     return 0;
325 }
326 
327 
stop_logging(void)328 void stop_logging(void)
329 {
330     worker_logger (1);
331 }
332 
333