werror.c 11.5 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
2
/* werror.c
 *
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 *
 * $Id$ */

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 1998 Niels Mller
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
J.H.M. Dassen's avatar
J.H.M. Dassen committed
23
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Niels Möller's avatar
Niels Möller committed
24
25
26
27
 */

#include "werror.h"

Niels Möller's avatar
Niels Möller committed
28
#include "charset.h"
29
30
31
32

/* For format_size_in_decimal */
#include "format.h"

33
34
#include "gc.h"
#include "io.h"
35
#include "parse.h"
36
#include "xalloc.h"
Niels Möller's avatar
Niels Möller committed
37

38
#include <assert.h>
Niels Möller's avatar
Niels Möller committed
39
#include <ctype.h>
40
#include <string.h>
41

42
#if HAVE_UNISTD_H
43
#include <unistd.h>
44
#endif
Niels Möller's avatar
Niels Möller committed
45

46
47
48
49
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

Niels Möller's avatar
Niels Möller committed
50
#if HAVE_SYSLOG_H
51
52
53
#include <syslog.h>
#endif

54
55
/* Global flags */
int trace_flag = 0;
56
57
int debug_flag = 0;
int quiet_flag = 0;
58
int verbose_flag = 0;
59

60
61
static const char *program_name = NULL;

62
63
#define WERROR_TRACE -1
#define WERROR_DEBUG -2
64
#define WERROR_LOG -3
65
66
67
68

static const struct argp_option
werror_options[] =
{
Niels Möller's avatar
Niels Möller committed
69
  { "quiet", 'q', NULL, 0, "Suppress all warnings and diagnostic messages", 0 },
70
71
72
  { "verbose", 'v', NULL, 0, "Verbose diagnostic messages", 0},
  { "trace", WERROR_TRACE, NULL, 0, "Detailed trace", 0 },
  { "debug", WERROR_DEBUG, NULL, 0, "Print huge amounts of debug information", 0 },
73
74
  { "log-file", WERROR_LOG, "File name", 0,
    "Append messages to this file.", 0},
75
76
77
78
  { NULL, 0, NULL, 0, NULL, 0 }
};

static error_t
79
werror_argp_parser(int key, char *arg,
80
		   struct argp_state *state)
81
82
83
84
85
{
  switch(key)
    {
    default:
      return ARGP_ERR_UNKNOWN;
86
    case ARGP_KEY_END:
87
    case ARGP_KEY_INIT:
88
      program_name = state->name;
89
      break;
90
91
92
93
94
95
96
97
98
99
100
101
    case 'q':
      quiet_flag = 1;
      break;
    case 'v':
      verbose_flag = 1;
      break;
    case WERROR_TRACE:
      trace_flag = 1;
      break;
    case WERROR_DEBUG:
      debug_flag = 1;
      break;
102
103
104
105
106
107
108
109
110
111
112
    case WERROR_LOG:
      {
	/* FIXME: For clients, this is right: We only get lsh-related
	 * messages to the log file, and child processes are not
	 * affected. But for the server, perhaps we should also dup
	 * the logfile over stderr? */
	
	int fd = open(arg, O_WRONLY | O_CREAT | O_APPEND, 0666);
	if (fd < 0)
	  argp_error(state, "Failed to open log file `%s'.", arg);
	else
113
	  set_error_stream(fd);
114
      }
115
116
117
118
119
120
121
122
    }
  return 0;
}

const struct argp werror_argp =
{
  werror_options,
  werror_argp_parser,
123
  NULL, NULL, NULL, NULL, NULL
124
125
};

126
static int error_fd = STDERR_FILENO;
127
128
129
130
131

#define BUF_SIZE 500
static UINT8 error_buffer[BUF_SIZE];
static UINT32 error_pos = 0;

Niels Möller's avatar
Niels Möller committed
132
133
static const struct exception *
(*error_write)(int fd, UINT32 length, const UINT8 *data) = write_raw;
134

Niels Möller's avatar
Niels Möller committed
135
#if HAVE_SYSLOG
Niels Möller's avatar
Niels Möller committed
136
137
static const struct exception *
write_syslog(int fd UNUSED, UINT32 length, const UINT8 *data)
138
{
139
  struct lsh_string *s;
140

141
142
143
144
145
  /* Data must not contain any NUL:s */
  assert(!memchr(data, '\0', length));
  
  /* NUL-terminate the string. */
  s = ssh_format("%ls", length, data);
146

147
148
149
  /* FIXME: Should we use different log levels for werror, verbose and
   * debug? */
  
150
  syslog(LOG_NOTICE, "%s", lsh_get_cstring(s));
151
152
  lsh_string_free(s);
  
Niels Möller's avatar
Niels Möller committed
153
  return NULL;
154
155
}

156
/* FIXME: Delete argument and use program_name. */
157
158
void
set_error_syslog(const char *id)
159
{
160
  openlog(id, LOG_PID | LOG_CONS, LOG_DAEMON);
161
162
163
164
165
  error_write = write_syslog;
  error_fd = -1;
}
#endif /* HAVE_SYSLOG */

Niels Möller's avatar
Niels Möller committed
166
167
168
169
static const struct exception *
write_ignore(int fd UNUSED,
	     UINT32 length UNUSED, const UINT8 *data UNUSED)
{ return NULL; }
Niels Möller's avatar
Niels Möller committed
170

171
void
172
set_error_stream(int fd)
173
174
175
{
  error_fd = fd;

176
177
178
179
180
181
182
183
  error_write = write_raw;
}

void
set_error_nonblocking(int fd)
{
  if (error_fd == fd)
    error_write = write_raw_with_poll;
184
185
}

186
187
int
dup_error_stream(void)
188
189
190
191
192
193
194
195
{
  if (error_fd < 0)
    /* We're not writing error messages on any file; there's no
     * problem. */
    return 1;
  else
    {
      int fd = dup(error_fd);
Niels Möller's avatar
Niels Möller committed
196
197
198
199

      /* This function is used to get stderr away from the stdio fd
       * range. In the unlikely event that dup returns an fd <=
       * STDERR_FILENO, we treat that as an error. */
200
201
202
203
204
205
206
207
208
209
210
211
212
213
      if (fd > STDERR_FILENO)
	{
	  io_set_close_on_exec(fd);
	  error_fd = fd;
	  return 1;
	}

      if (fd >= 0)
	close(fd);
      
      return 0;
    }
}

214
215
void
set_error_ignore(void)
216
217
{
  error_write = write_ignore;
218
  error_fd = -1;
219
220
}

Niels Möller's avatar
Niels Möller committed
221
#define WERROR(l, d) (error_write(error_fd, (l), (d)))
222

223
224
static void
werror_flush(void)
225
226
227
228
229
230
231
232
{
  if (error_pos)
    {
      WERROR(error_pos, error_buffer);
      error_pos = 0;
    }
}

233
234
static void
werror_putc(UINT8 c)
235
236
237
238
239
240
241
{
  if (error_pos == BUF_SIZE)
    werror_flush();

  error_buffer[error_pos++] = c;
}

242
243
static void
werror_write(UINT32 length, const UINT8 *msg)
244
{
245
  if (error_pos + length <= BUF_SIZE)
246
    {
247
248
      memcpy(error_buffer + error_pos, msg, length);
      error_pos += length;
249
250
251
252
    }
  else
    {
      werror_flush();
253
      WERROR(length, msg);
254
255
256
    }
}

257
258
static void
werror_cstring(char *s) { werror_write(strlen(s), s); }
259

260
261
static void
werror_bignum(mpz_t n, int base)
262
{
Niels Möller's avatar
Niels Möller committed
263
  char *s = alloca(mpz_sizeinbase(n, base) + 2);
264
265
266
  mpz_get_str(s, 16, n);

  werror_cstring(s);
267
}
268

269
270
static void
werror_decimal(UINT32 n)
271
{
272
  unsigned length = format_size_in_decimal(n);
Niels Möller's avatar
Niels Möller committed
273
  UINT8 *buffer = alloca(length);
274
275
276

  format_decimal(length, buffer, n);

277
  werror_write(length, buffer);
278
279
}

280
static unsigned format_size_in_hex(UINT32 n);
Niels Möller's avatar
Niels Möller committed
281

282
283
static void
werror_hex_digit(unsigned digit)
284
285
{
  werror_putc("0123456789abcdef"[digit]);
286
287
}

288
289
static void
werror_hex_putc(UINT8 c)
290
{
291
292
293
  werror_hex_digit(c / 16);
  werror_hex_digit(c % 16);
}
294

295
296
static void
werror_hex(UINT32 n)
297
298
299
300
{
  unsigned left = 8;
  
  while ( (left > 1)
301
	  && !(n & 0xf0000000UL))
302
    {
303
304
      left --;
      n <<= 4;
305
    }
306
307
		    
  while (left--)
308
309
310
311
    {
      werror_hex_digit((n >> 28) & 0xf);
      n <<= 4;
    }
312
313
}

314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
static void
werror_hexdump(UINT32 length, UINT8 *data)
{
  UINT32 i = 0;
  
  werror("(size %i = 0x%xi)\n", length, length);

  for (i = 0; i<length; i+= 16)
    {
      unsigned j = format_size_in_hex(i);
      unsigned r = length - i;
      
      for ( ; j < 8; j++)
	werror_putc('0');

      werror_hex(i);
      werror_cstring(": ");

      if (r > 16)
	r = 16;

      for (j = 0; j<r; j++)
	werror_hex_putc(data[i+j]);

      for (; j<17; j++)
	werror_cstring("  ");

      for (j = 0; j<r; j++)
	{
	  UINT8 c = data[i+j];
	  if ( (c < 32) || (c > 126) )
	    c = '.';
	  werror_putc(c);
	}

      werror_cstring("\n");
    }
}

static void
354
werror_paranoia_putc(UINT8 c)
355
{
356
  switch (c)
357
    {
358
    case '\\':
359
      werror_cstring("\\\\");
360
361
362
363
364
365
      break;
    case '\r':
      /* Ignore */
      break;
    default:
      if (!isprint(c))
366
	{
367
	  werror_putc('\\');
368
	  werror_hex_putc(c);
369
370
	  break;
	}
371
372
      /* Fall through */
    case '\n':
373
      werror_putc(c);
374
      break;
375
376
377
    }
}

378
379
void
werror_vformat(const char *f, va_list args)
380
{
381
382
  if (program_name)
    {
383
384
      werror_write(strlen(program_name), program_name);
      werror_write(2, ": ");
385
386
    }
  
387
388
389
390
391
392
393
394
  while (*f)
    {
      if (*f == '%')
	{
	  int do_hex = 0;
	  int do_free = 0;
	  int do_paranoia = 0;
	  int do_utf8 = 0;
395

396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
	  while (*++f)
	    switch (*f)
	      {
	      case 'x':
		do_hex = 1;
		break;
	      case 'f':
		do_free = 1;
		break;
	      case 'p':
		do_paranoia = 1;
		break;
	      case 'u':
		do_utf8 = 1;
		break;
	      default:
		goto end_options;
	      }
	end_options:
	  switch(*f++)
	    {
	    case '%':
	      werror_putc(*f);
	      break;
	    case 'i':
	      (do_hex ? werror_hex : werror_decimal)(va_arg(args, UINT32));
	      break;
	    case 'c':
424
	      (do_paranoia ? werror_paranoia_putc : werror_putc)(va_arg(args, int));
425
426
427
428
	      break;
	    case 'n':
	      werror_bignum(va_arg(args, MP_INT *), do_hex ? 16 : 10);
	      break;
429
430
431
432
	    case 'a':
	      {
		int atom = va_arg(args, int);

Niels Möller's avatar
Niels Möller committed
433
434
435
436
                if (atom)
                  werror_write(get_atom_length(atom), get_atom_name(atom));
		else
                  werror_write(9, "<unknown>");
437
		break;
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
	      }
	    case 's':
	      {
		UINT32 length = va_arg(args, UINT32);
		UINT8 *s = va_arg(args, UINT8 *);

		struct lsh_string *u = NULL; 

		if (do_utf8 && !local_is_utf8())
		  {
		    u = low_utf8_to_local(length, s, 0);
		    if (!u)
		      {
			werror_cstring("<Invalid utf-8 string>");
			break;
		      }
		    length = u->length;
		    s = u->data;
		  }
		if (do_hex)
		  {
		    assert(!do_paranoia);
		    werror_hexdump(length, s);
		  }
		else if (do_paranoia)
		  {
		    UINT32 i;
		    for (i=0; i<length; i++)
		      werror_paranoia_putc(*s++);
		  }
		else
		  werror_write(length, s);

		if (u)
		  lsh_string_free(u);
	      }
	      break;
	    case 'S':
	      {
		struct lsh_string *s = va_arg(args, struct lsh_string *);

		if (do_utf8)
		  {
		    s = utf8_to_local(s, 0, do_free);
		    if (!s)
		      {
			werror_cstring("<Invalid utf-8 string>");
			break;
		      }
		    do_free = 1;
		  }
		if (do_hex)
		  {
		    assert(!do_paranoia);
		    werror_hexdump(s->length, s->data);
		  }
		else if (do_paranoia)
		  {
		    UINT32 i;
		    for (i=0; i<s->length; i++)
		      werror_paranoia_putc(s->data[i]);
		  }
		else
		  werror_write(s->length, s->data);

		if (do_free)
		  lsh_string_free(s);
505
506

		break;
507
	      }
508
509
510
511
	    case 't':
	      {
		struct lsh_object *o = va_arg(args, struct lsh_object *);
		const char *type;
Niels Möller's avatar
Niels Möller committed
512

513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
		if (!o)
		  type = "<NULL>";
		else if (o->isa)
		  type = o->isa->name;
		else
		  type = "<STATIC>";

		werror_write(strlen(type), type);

		break;
	      }
	    case 'z':
	      {
		char *s = va_arg(args, char *);

		if (do_hex)
		  werror_hexdump(strlen(s), s);

		else if (do_paranoia)
		  while (*s)
		    werror_paranoia_putc(*s++);
		else
		  werror_write(strlen(s), s);
		
		break;
	      }
539
	    default:
Niels Möller's avatar
Niels Möller committed
540
	      fatal("werror_vformat: bad format string!\n");
541
542
543
544
545
546
	      break;
	    }
	}
      else
	werror_putc(*f++);
    }
Niels Möller's avatar
Niels Möller committed
547
}
548

549
550
void
werror(const char *format, ...) 
551
{
552
  va_list args;
553

554
555
556
  /* It is somewhat reasonable to use both -q and -v. In this case
   * werror()-messages should be displayed. */
  if (verbose_flag || !quiet_flag)
557
558
559
560
    {
      va_start(args, format);
      werror_vformat(format, args);
      va_end(args);
561
562
563
564
      werror_flush();
    }
}

565
566
void
trace(const char *format, ...) 
567
568
569
570
571
572
573
574
{
  va_list args;

  if (trace_flag)
    {
      va_start(args, format);
      werror_vformat(format, args);
      va_end(args);
575
576
      werror_flush();
    }
577
578
}

579
580
void
debug(const char *format, ...) 
581
{
582
583
  va_list args;

584
  if (debug_flag)
585
586
587
588
589
590
    {
      va_start(args, format);
      werror_vformat(format, args);
      va_end(args);
      werror_flush();
    }
591
592
}

593
594
void
verbose(const char *format, ...) 
595
{
596
597
  va_list args;

598
  if (verbose_flag)
599
600
601
602
603
604
    {
      va_start(args, format);
      werror_vformat(format, args);
      va_end(args);
      werror_flush();
    }
605
606
}

607
608
void
fatal(const char *format, ...) 
609
610
611
612
{
  va_list args;

  va_start(args, format);
613
  werror_vformat(format, args);
614
  va_end(args);
615
  werror_flush();
616

617
618
619
#if WITH_GCOV
  exit(255);
#else
620
  abort();
621
#endif
622
}
623

624
625
static unsigned
format_size_in_hex(UINT32 n)
626
627
628
629
630
631
632
633
634
{
  int i;
  int e;
  
  /* Table of 16^(2^n) */
  static const UINT32 powers[] = { 0x10UL, 0x100UL, 0x10000UL };

#define SIZE (sizeof(powers) / sizeof(powers[0])) 

635
  /* Determine the smallest e such that n < 16^e */
636
637
638
639
640
641
642
643
644
645
646
647
648
649
  for (i = SIZE - 1 , e = 0; i >= 0; i--)
    {
      if (n >= powers[i])
	{
	  e += 1UL << i;
	  n /= powers[i];
	}
    }

#undef SIZE
  
  return e+1;
}