xref: /netbsd-src/external/gpl2/xcvs/dist/src/log-buffer.c (revision 5a6c14c844c4c665da5632061aebde7bb2cb5766)
1 /* CVS client logging buffer.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.  */
12 #include <sys/cdefs.h>
13 __RCSID("$NetBSD: log-buffer.c,v 1.2 2016/05/17 14:00:09 christos Exp $");
14 
15 #include <config.h>
16 
17 #include <stdio.h>
18 
19 #include "cvs.h"
20 #include "buffer.h"
21 
22 #if defined CLIENT_SUPPORT || defined SERVER_SUPPORT
23 
24 /* We want to be able to log data sent between us and the server.  We
25    do it using log buffers.  Each log buffer has another buffer which
26    handles the actual I/O, and a file to log information to.
27 
28    This structure is the closure field of a log buffer.  */
29 
30 struct log_buffer
31 {
32     /* The underlying buffer.  */
33     struct buffer *buf;
34     /* The file to log information to.  */
35     FILE *log;
36 
37 #ifdef PROXY_SUPPORT
38     /* Whether errors writing to the log file should be fatal or not.  */
39     bool fatal_errors;
40 
41     /* The name of the file backing this buffer so that it may be deleted on
42      * buffer shutdown.
43      */
44     char *back_fn;
45 
46     /* Set once logging is permanently disabled for a buffer.  */
47     bool disabled;
48 
49     /* The memory buffer (cache) backing this log.  */
50     struct buffer *back_buf;
51 
52     /* The maximum number of bytes to store in memory before beginning logging
53      * to a file.
54      */
55     size_t max;
56 
57     /* Once we start logging to a file we do not want to stop unless asked.  */
58     bool tofile;
59 #endif /* PROXY_SUPPORT */
60 };
61 
62 
63 
64 #ifdef PROXY_SUPPORT
65 /* Force the existance of lb->log.
66  *
67  * INPUTS
68  *   lb			The log buffer.
69  *
70  * OUTPUTS
71  *   lb->log		The new FILE *.
72  *   lb->back_fn	The name of the new log, for later disposal.
73  *
74  * ASSUMPTIONS
75  *   lb->log is NULL or, at least, does not require freeing.
76  *   lb->back_fn is NULL or, at least, does not require freeing..
77  *
78  * RETURNS
79  *   Nothing.
80  *
81  * ERRORS
82  *   Errors creating the log file will output a message via error().  Whether
83  *   the error is fatal or not is dependent on lb->fatal_errors.
84  */
85 static inline void
log_buffer_force_file(struct log_buffer * lb)86 log_buffer_force_file (struct log_buffer *lb)
87 {
88     lb->log = cvs_temp_file (&lb->back_fn);
89     if (!lb->log)
90 	error (lb->fatal_errors, errno, "failed to open log file.");
91 }
92 #endif /* PROXY_SUPPORT */
93 
94 
95 
96 /* Create a log buffer.
97  *
98  * INPUTS
99  *   buf		A pointer to the buffer structure to log input from.
100  *   fp			A file name to log data to.  May be NULL.
101 #ifdef PROXY_SUPPORT
102  *   fatal_errors	Whether errors writing to a log file should be
103  *			considered fatal.
104 #else
105  *   fatal_errors	unused
106 #endif
107  *   input		Whether we will log data for an input or output
108  *			buffer.
109 #ifdef PROXY_SUPPORT
110  *   max		The maximum size of our memory cache.
111 #else
112  *   max		unused
113 #endif
114  *   memory		The function to call when memory allocation errors are
115  *			encountered.
116  *
117  * RETURNS
118  *   A pointer to a new buffer structure.
119  */
120 static int log_buffer_input (void *, char *, size_t, size_t, size_t *);
121 static int log_buffer_output (void *, const char *, size_t, size_t *);
122 static int log_buffer_flush (void *);
123 static int log_buffer_block (void *, bool);
124 static int log_buffer_get_fd (void *);
125 static int log_buffer_shutdown (struct buffer *);
126 struct buffer *
log_buffer_initialize(struct buffer * buf,FILE * fp,bool fatal_errors,size_t max,bool input,void (* memory)(struct buffer *))127 log_buffer_initialize (struct buffer *buf, FILE *fp,
128 # ifdef PROXY_SUPPORT
129 		       bool fatal_errors,
130 		       size_t max,
131 # endif /* PROXY_SUPPORT */
132                        bool input,
133 		       void (*memory) (struct buffer *))
134 {
135     struct log_buffer *lb = xmalloc (sizeof *lb);
136     struct buffer *retbuf;
137 
138     lb->buf = buf;
139     lb->log = fp;
140 #ifdef PROXY_SUPPORT
141     lb->back_fn = NULL;
142     lb->fatal_errors = fatal_errors;
143     lb->disabled = false;
144     assert (size_in_bounds_p (max));
145     lb->max = max;
146     lb->tofile = false;
147     lb->back_buf = buf_nonio_initialize (memory);
148 #endif /* PROXY_SUPPORT */
149     retbuf = buf_initialize (input ? log_buffer_input : NULL,
150 			     input ? NULL : log_buffer_output,
151 			     input ? NULL : log_buffer_flush,
152 			     log_buffer_block, log_buffer_get_fd,
153 			     log_buffer_shutdown, memory, lb);
154 
155     if (!buf_empty_p (buf))
156     {
157 	/* If our buffer already had data, copy it & log it if necessary.  This
158 	 * can happen, for instance, with a pserver, where we deliberately do
159 	 * not instantiate the log buffer until after authentication so that
160 	 * auth data does not get logged (the pserver data will not be logged
161 	 * in this case, but any data which was left unused in the buffer by
162 	 * the auth code will be logged and put in our new buffer).
163 	 */
164 	struct buffer_data *data;
165 #ifdef PROXY_SUPPORT
166 	size_t total = 0;
167 #endif /* PROXY_SUPPORT */
168 
169 	for (data = buf->data; data != NULL; data = data->next)
170 	{
171 #ifdef PROXY_SUPPORT
172 	    if (!lb->tofile)
173 	    {
174 		total = xsum (data->size, total);
175 		if (total >= max)
176 		    lb->tofile = true;
177 	    }
178 
179 	    if (lb->tofile)
180 	    {
181 		if (!lb->log) log_buffer_force_file (lb);
182 		if (lb->log)
183 		{
184 #endif /* PROXY_SUPPORT */
185 		    if (fwrite (data->bufp, 1, data->size, lb->log)
186 			!= (size_t) data->size)
187 			error (
188 #ifdef PROXY_SUPPORT
189 			       fatal_errors,
190 #else /* !PROXY_SUPPORT */
191 			       false,
192 #endif /* PROXY_SUPPORT */
193 			       errno, "writing to log file");
194 		    fflush (lb->log);
195 #ifdef PROXY_SUPPORT
196 		}
197 	    }
198 	    else
199 		/* Log to memory buffer.  */
200 		buf_copy_data (lb->back_buf, data, data);
201 #endif /* PROXY_SUPPORT */
202 	}
203 	buf_append_buffer (retbuf, buf);
204     }
205     return retbuf;
206 }
207 
208 
209 
210 /* The input function for a log buffer.  */
211 static int
log_buffer_input(void * closure,char * data,size_t need,size_t size,size_t * got)212 log_buffer_input (void *closure, char *data, size_t need, size_t size,
213 		  size_t *got)
214 {
215     struct log_buffer *lb = closure;
216     int status;
217 
218     assert (lb->buf->input);
219 
220     status = (*lb->buf->input) (lb->buf->closure, data, need, size, got);
221     if (status != 0)
222 	return status;
223 
224     if (
225 #ifdef PROXY_SUPPORT
226 	!lb->disabled &&
227 #endif /* PROXY_SUPPORT */
228 	*got > 0)
229     {
230 #ifdef PROXY_SUPPORT
231 	if (!lb->tofile
232 	    && xsum (*got, buf_count_mem (lb->back_buf)) >= lb->max)
233 	    lb->tofile = true;
234 
235 	if (lb->tofile)
236 	{
237 	    if (!lb->log) log_buffer_force_file (lb);
238 	    if (lb->log)
239 	    {
240 #endif /* PROXY_SUPPORT */
241 		if (fwrite (data, 1, *got, lb->log) != *got)
242 		    error (
243 #ifdef PROXY_SUPPORT
244 			   lb->fatal_errors,
245 #else /* !PROXY_SUPPORT */
246 			   false,
247 #endif /* PROXY_SUPPORT */
248 			   errno, "writing to log file");
249 		fflush (lb->log);
250 #ifdef PROXY_SUPPORT
251 	    }
252 	}
253 	else
254 	    /* Log to memory buffer.  */
255 	    buf_output (lb->back_buf, data, *got);
256 #endif /* PROXY_SUPPORT */
257     }
258 
259     return 0;
260 }
261 
262 
263 
264 /* The output function for a log buffer.  */
265 static int
log_buffer_output(void * closure,const char * data,size_t have,size_t * wrote)266 log_buffer_output (void *closure, const char *data, size_t have, size_t *wrote)
267 {
268     struct log_buffer *lb = closure;
269     int status;
270 
271     assert (lb->buf->output);
272 
273     status = (*lb->buf->output) (lb->buf->closure, data, have, wrote);
274     if (status != 0)
275 	return status;
276 
277     if (
278 #ifdef PROXY_SUPPORT
279 	!lb->disabled &&
280 #endif /* PROXY_SUPPORT */
281 	*wrote > 0)
282     {
283 #ifdef PROXY_SUPPORT
284 	if (!lb->tofile
285 	    && xsum (*wrote, buf_count_mem (lb->back_buf)) >= lb->max)
286 	    lb->tofile = true;
287 
288 	if (lb->tofile)
289 	{
290 	    if (!lb->log) log_buffer_force_file (lb);
291 	    if (lb->log)
292 	    {
293 #endif /* PROXY_SUPPORT */
294 		if (fwrite (data, 1, *wrote, lb->log) != *wrote)
295 		    error (
296 #ifdef PROXY_SUPPORT
297 			   lb->fatal_errors,
298 #else /* !PROXY_SUPPORT */
299 			   false,
300 #endif /* PROXY_SUPPORT */
301 			   errno, "writing to log file");
302 		fflush (lb->log);
303 #ifdef PROXY_SUPPORT
304 	    }
305 	}
306 	else
307 	    /* Log to memory buffer.  */
308 	    buf_output (lb->back_buf, data, *wrote);
309 #endif /* PROXY_SUPPORT */
310     }
311 
312     return 0;
313 }
314 
315 
316 
317 /* The flush function for a log buffer.  */
318 static int
log_buffer_flush(void * closure)319 log_buffer_flush (void *closure)
320 {
321     struct log_buffer *lb = closure;
322 
323     assert (lb->buf->flush);
324 
325     /* We don't really have to flush the log file here, but doing it
326      * will let tail -f on the log file show what is sent to the
327      * network as it is sent.
328      */
329     if (lb->log && (fflush (lb->log)))
330         error (0, errno, "flushing log file");
331 
332     return (*lb->buf->flush) (lb->buf->closure);
333 }
334 
335 
336 
337 /* The block function for a log buffer.  */
338 static int
log_buffer_block(void * closure,bool block)339 log_buffer_block (void *closure, bool block)
340 {
341     struct log_buffer *lb = closure;
342 
343     if (block)
344 	return set_block (lb->buf);
345     else
346 	return set_nonblock (lb->buf);
347 }
348 
349 
350 
351 #ifdef PROXY_SUPPORT
352 /* Disable logging without shutting down the next buffer in the chain.
353  */
354 struct buffer *
log_buffer_rewind(struct buffer * buf)355 log_buffer_rewind (struct buffer *buf)
356 {
357     struct log_buffer *lb = buf->closure;
358     struct buffer *retbuf;
359     int fd;
360 
361     lb->disabled = true;
362 
363     if (lb->log)
364     {
365 	FILE *tmp = lb->log;
366 	lb->log = NULL;
367 
368 	/* flush & rewind the file.  */
369 	if (fflush (tmp) < 0)
370 	    error (0, errno, "flushing log file");
371 	rewind (tmp);
372 
373 	/* Get a descriptor for the log and close the FILE *.  */
374 	fd = dup (fileno (tmp));
375 	if (fclose (tmp) < 0)
376 	    error (0, errno, "closing log file");
377     }
378     else
379 	fd = open (DEVNULL, O_RDONLY);
380 
381     /* Catch dup/open errors.  */
382     if (fd < 0)
383     {
384 	error (lb->fatal_errors, errno, "failed to rewind log buf.");
385 	return NULL;
386     }
387 
388     /* Create a new fd buffer around the log.  */
389     retbuf = fd_buffer_initialize (fd, 0, NULL, true, buf->memory_error);
390 
391     {
392 	struct buffer *tmp;
393         /* Insert the data which wasn't written to a file.  */
394 	buf_append_buffer (retbuf, lb->back_buf);
395 	tmp = lb->back_buf;
396 	lb->back_buf = NULL;
397 	buf_free (tmp);
398     }
399 
400     return retbuf;
401 }
402 #endif /* PROXY_SUPPORT */
403 
404 
405 
406 /* Disable logging and close the log without shutting down the next buffer in
407  * the chain.
408  */
409 #ifndef PROXY_SUPPORT
410 static
411 #endif /* !PROXY_SUPPORT */
412 void
log_buffer_closelog(struct buffer * buf)413 log_buffer_closelog (struct buffer *buf)
414 {
415     struct log_buffer *lb = buf->closure;
416     void *tmp;
417 
418 #ifdef PROXY_SUPPORT
419     lb->disabled = true;
420 #endif /* PROXY_SUPPORT */
421 
422     /* Close the log.  */
423     if (lb->log)
424     {
425 	tmp = lb->log;
426 	lb->log = NULL;
427 	if (fclose (tmp) < 0)
428 	    error (0, errno, "closing log file");
429     }
430 
431 #ifdef PROXY_SUPPORT
432     /* Delete the log if we know its name.  */
433     if (lb->back_fn)
434     {
435 	tmp = lb->back_fn;
436 	lb->back_fn = NULL;
437 	if (CVS_UNLINK (tmp))
438 	    error (0, errno, "Failed to delete log file.");
439 	free (tmp);
440     }
441 
442     if (lb->back_buf)
443     {
444 	tmp = lb->back_buf;
445 	lb->back_buf = NULL;
446 	buf_free (tmp);
447     }
448 #endif /* PROXY_SUPPORT */
449 }
450 
451 
452 
453 /* Return the file descriptor underlying any child buffers.  */
454 static int
log_buffer_get_fd(void * closure)455 log_buffer_get_fd (void *closure)
456 {
457     struct log_buffer *lb = closure;
458     return buf_get_fd (lb->buf);
459 }
460 
461 
462 
463 /* The shutdown function for a log buffer.  */
464 static int
log_buffer_shutdown(struct buffer * buf)465 log_buffer_shutdown (struct buffer *buf)
466 {
467     struct log_buffer *lb = buf->closure;
468 
469     log_buffer_closelog (buf);
470     return buf_shutdown (lb->buf);
471 }
472 
473 
474 
475 void
setup_logfiles(char * var,struct buffer ** to_server_p,struct buffer ** from_server_p)476 setup_logfiles (char *var, struct buffer **to_server_p,
477                 struct buffer **from_server_p)
478 {
479   char *log = getenv (var);
480 
481   /* Set up logfiles, if any.
482    *
483    * We do this _after_ authentication on purpose.  Wouldn't really like to
484    * worry about logging passwords...
485    */
486   if (log)
487     {
488       int len = strlen (log);
489       char *buf = xmalloc (len + 5);
490       char *p;
491       FILE *fp;
492 
493       strcpy (buf, log);
494       p = buf + len;
495 
496       /* Open logfiles in binary mode so that they reflect
497 	 exactly what was transmitted and received (that is
498 	 more important than that they be maximally
499 	 convenient to view).  */
500       /* Note that if we create several connections in a single CVS client
501 	 (currently used by update.c), then the last set of logfiles will
502 	 overwrite the others.  There is currently no way around this.  */
503       strcpy (p, ".in");
504       fp = fopen (buf, "wb");
505       if (!fp)
506 	error (0, errno, "opening to-server logfile %s", buf);
507       else
508 	*to_server_p = log_buffer_initialize (*to_server_p, fp,
509 # ifdef PROXY_SUPPORT
510 					      false,
511 					      0,
512 # endif /* PROXY_SUPPORT */
513 					      false, NULL);
514 
515       strcpy (p, ".out");
516       fp = fopen (buf, "wb");
517       if (!fp)
518 	error (0, errno, "opening from-server logfile %s", buf);
519       else
520 	*from_server_p = log_buffer_initialize (*from_server_p, fp,
521 # ifdef PROXY_SUPPORT
522 						false,
523 						0,
524 # endif /* PROXY_SUPPORT */
525                                                 true, NULL);
526 
527       free (buf);
528     }
529 }
530 
531 #endif /* CLIENT_SUPPORT || SERVER_SUPPORT */
532