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