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