werror.c 11.2 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
int syslog_flag = 0;
60

61
62
static const char *program_name = NULL;

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

static const struct argp_option
werror_options[] =
{
Niels Möller's avatar
Niels Möller committed
70
  { "quiet", 'q', NULL, 0, "Suppress all warnings and diagnostic messages", 0 },
71
72
73
  { "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 },
74
75
  { "log-file", WERROR_LOG, "File name", 0,
    "Append messages to this file.", 0},
76
77
78
79
  { NULL, 0, NULL, 0, NULL, 0 }
};

static error_t
80
werror_argp_parser(int key, char *arg,
81
		   struct argp_state *state)
82
83
84
85
86
{
  switch(key)
    {
    default:
      return ARGP_ERR_UNKNOWN;
87
    case ARGP_KEY_END:
88
    case ARGP_KEY_INIT:
89
      program_name = state->name;
90
      break;
91
92
93
94
95
96
97
98
99
100
101
102
    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;
103
104
105
106
107
108
109
110
111
112
113
114
115
    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
	  set_error_stream(fd);
      }
116
117
118
119
120
121
122
123
    }
  return 0;
}

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

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

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

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

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

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

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

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

Niels Möller's avatar
Niels Möller committed
167
168
169
170
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
171

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

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

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

187
188
int
dup_error_stream(void)
189
190
191
192
193
194
195
196
{
  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
197
198
199
200

      /* 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. */
201
202
203
204
205
206
207
208
209
210
211
212
213
214
      if (fd > STDERR_FILENO)
	{
	  io_set_close_on_exec(fd);
	  error_fd = fd;
	  return 1;
	}

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

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

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

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

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

  error_buffer[error_pos++] = c;
}

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

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

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

  werror_cstring(s);
268
}
269

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

  format_decimal(length, buffer, n);

278
  werror_write(length, buffer);
279
280
}

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

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

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

296
297
static void
werror_hex(UINT32 n)
298
299
300
301
{
  unsigned left = 8;
  
  while ( (left > 1)
302
	  && !(n & 0xf0000000UL))
303
    {
304
305
      left --;
      n <<= 4;
306
    }
307
308
		    
  while (left--)
309
310
311
312
    {
      werror_hex_digit((n >> 28) & 0xf);
      n <<= 4;
    }
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
354
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
355
werror_paranoia_putc(UINT8 c)
356
{
357
  switch (c)
358
    {
359
    case '\\':
360
      werror_cstring("\\\\");
361
362
363
364
365
366
      break;
    case '\r':
      /* Ignore */
      break;
    default:
      if (!isprint(c))
367
	{
368
	  werror_putc('\\');
369
	  werror_hex_putc(c);
370
371
	  break;
	}
372
373
      /* Fall through */
    case '\n':
374
      werror_putc(c);
375
      break;
376
377
378
    }
}

379
380
void
werror_vformat(const char *f, va_list args)
381
{
382
383
  if (program_name)
    {
384
385
      werror_write(strlen(program_name), program_name);
      werror_write(2, ": ");
386
387
    }
  
388
389
390
391
392
393
394
395
  while (*f)
    {
      if (*f == '%')
	{
	  int do_hex = 0;
	  int do_free = 0;
	  int do_paranoia = 0;
	  int do_utf8 = 0;
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
424
	  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':
425
	      (do_paranoia ? werror_paranoia_putc : werror_putc)(va_arg(args, int));
426
427
428
429
430
431
432
	      break;
	    case 'n':
	      werror_bignum(va_arg(args, MP_INT *), do_hex ? 16 : 10);
	      break;
	    case 'z':
	      {
		char *s = va_arg(args, char *);
433

434
435
		if (do_hex)
		  werror_hexdump(strlen(s), s);
Niels Möller's avatar
Niels Möller committed
436

437
438
439
440
		else if (do_paranoia)
		  while (*s)
		    werror_paranoia_putc(*s++);
		else
441
		  werror_write(strlen(s), s);
442
		
443
444
445
446
447
448
449
450
451
452
453
		break;
	      }
	    case 'a':
	      {
		int atom = va_arg(args, int);
		
		assert(atom);

		werror_write(get_atom_length(atom), get_atom_name(atom));
		
		break;
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
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
	      }
	    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);
	      }
	      break;
Niels Möller's avatar
Niels Möller committed
523

524
	    default:
Niels Möller's avatar
Niels Möller committed
525
	      fatal("werror_vformat: bad format string!\n");
526
527
528
529
530
531
	      break;
	    }
	}
      else
	werror_putc(*f++);
    }
Niels Möller's avatar
Niels Möller committed
532
}
533

534
535
void
werror(const char *format, ...) 
536
{
537
  va_list args;
538

539
540
541
  /* It is somewhat reasonable to use both -q and -v. In this case
   * werror()-messages should be displayed. */
  if (verbose_flag || !quiet_flag)
542
543
544
545
    {
      va_start(args, format);
      werror_vformat(format, args);
      va_end(args);
546
547
548
549
      werror_flush();
    }
}

550
551
void
trace(const char *format, ...) 
552
553
554
555
556
557
558
559
{
  va_list args;

  if (trace_flag)
    {
      va_start(args, format);
      werror_vformat(format, args);
      va_end(args);
560
561
      werror_flush();
    }
562
563
}

564
565
void
debug(const char *format, ...) 
566
{
567
568
  va_list args;

569
  if (debug_flag)
570
571
572
573
574
575
    {
      va_start(args, format);
      werror_vformat(format, args);
      va_end(args);
      werror_flush();
    }
576
577
}

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

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

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

  va_start(args, format);
598
  werror_vformat(format, args);
599
  va_end(args);
600
  werror_flush();
601

602
603
604
#if WITH_GCOV
  exit(255);
#else
605
  abort();
606
#endif
607
}
608

609
610
static unsigned
format_size_in_hex(UINT32 n)
611
612
613
614
615
616
617
618
619
{
  int i;
  int e;
  
  /* Table of 16^(2^n) */
  static const UINT32 powers[] = { 0x10UL, 0x100UL, 0x10000UL };

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

620
  /* Determine the smallest e such that n < 16^e */
621
622
623
624
625
626
627
628
629
630
631
632
633
634
  for (i = SIZE - 1 , e = 0; i >= 0; i--)
    {
      if (n >= powers[i])
	{
	  e += 1UL << i;
	  n /= powers[i];
	}
    }

#undef SIZE
  
  return e+1;
}