capture.c 44.2 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2008 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
4
 * All rights reserved.
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 */

/*
 * Testbed note:  This code has developed over the last several
 * years in RCS.  This is an import of the current version of
 * capture from the /usr/src/utah RCS repository into the testbed,
 * with some new hacks to port it to Linux.
 *
 * - dga, 10/10/2000
 */

/*
 * A LITTLE hack to record output from a tty device to a file, and still
 * have it available to tip using a pty/tty pair.
 */
20
21

#define SAFEMODE
22
23
24
	
#include <sys/param.h>

Mike Hibler's avatar
Mike Hibler committed
25
26
#include <unistd.h>
#include <string.h>
27
28
29
30
31
32
#include <stdio.h>
#include <ctype.h>
#include <strings.h>
#include <syslog.h>
#include <termios.h>
#include <errno.h>
33
34
#include <stdlib.h>
#include <stdarg.h>
Timothy Stack's avatar
   
Timothy Stack committed
35
36
37
#include <time.h>
#include <assert.h>
#include <paths.h>
38
39
40
41
42
43
44
45
46

#include <sys/param.h>
#include <sys/file.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/termios.h>
47
48
49
50
51
#ifdef USESOCKETS
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
52
53
#include <setjmp.h>
#include <netdb.h>
Mike Hibler's avatar
Mike Hibler committed
54
55
56
#ifndef __linux__
#include <rpc/rpc.h>
#endif
57
58
59
60
#ifdef WITHSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif /* WITHSSL */
61
#include "config.h"
62
#endif /* USESOCKETS */
63
#include "capdecls.h"
64
65
66
67
68

#define geterr(e)	strerror(e)

void quit(int);
void reinit(int);
69
void newrun(int);
70
void terminate(int);
71
void cleanup(void);
72
73
74
void capture(void);

void usage(void);
75
void warning(char *format, ...);
76
77
void die(char *format, ...);
void dolog(int level, char *format, ...);
78

Mike Hibler's avatar
Mike Hibler committed
79
80
81
82
83
84
85
86
87
88
int val2speed(int val);
void rawmode(char *devname, int speed);
void writepid(void);
void createkey(void);
int handshake(void);
#ifdef USESOCKETS
int clientconnect(void);
#endif
int handleupload(void);

89
90
#ifdef __linux__
#define _POSIX_VDISABLE '\0'
91
92
93
#define revoke(tty)	(0)
#endif

94
95
96
97
#ifndef LOG_TESTBED
#define LOG_TESTBED	LOG_USER
#endif

98
99
100
101
102
/*
 *  Configurable things.
 */
#define PIDNAME		"%s/%s.pid"
#define LOGNAME		"%s/%s.log"
103
#define RUNNAME		"%s/%s.run"
104
105
106
#define TTYNAME		"%s/%s"
#define PTYNAME		"%s/%s-pty"
#define ACLNAME		"%s/%s.acl"
107
#define DEVNAME		"%s/%s"
108
#define BUFSIZE		4096
109
#define DROP_THRESH	(32*1024)
Timothy Stack's avatar
   
Timothy Stack committed
110
#define MAX_UPLOAD_SIZE	(1 * 1024 * 1024)
111
#define DEFAULT_CERTFILE PREFIX"/etc/capture.pem"
Timothy Stack's avatar
   
Timothy Stack committed
112
113
#define DEFAULT_CLIENT_CERTFILE PREFIX"/etc/client.pem"
#define DEFAULT_CAFILE	PREFIX"/etc/emulab.pem"
Chad Barb's avatar
   
Chad Barb committed
114

115
116
117
char 	*Progname;
char 	*Pidname;
char	*Logname;
118
char	*Runname;
119
char	*Ttyname;
120
121
122
char	*Ptyname;
char	*Devname;
char	*Machine;
Timothy Stack's avatar
   
Timothy Stack committed
123
int	logfd = -1, runfd, devfd = -1, ptyfd = -1;
124
int	hwflow = 0, speed = B9600, debug = 0, runfile = 0, standalone = 0;
125
int	stampinterval = -1;
126
127
sigset_t actionsigmask;
sigset_t allsigmask;
128
int	 powermon = 0;
Timothy Stack's avatar
   
Timothy Stack committed
129
130
131
132
#ifndef  USESOCKETS
#define relay_snd 0
#define relay_rcv 0
#else
133
char		  *Bossnode = BOSSNODE;
Mike Hibler's avatar
Mike Hibler committed
134
struct sockaddr_in Bossaddr;
135
136
char		  *Aclname;
int		   serverport = SERVERPORT;
Timothy Stack's avatar
   
Timothy Stack committed
137
138
139
140
int		   sockfd, tipactive, portnum, relay_snd, relay_rcv;
int		   upportnum = -1, upfd = -1, upfilefd = -1;
char		   uptmpnam[64];
size_t		   upfilesize = 0;
141
struct sockaddr_in tipclient;
Timothy Stack's avatar
   
Timothy Stack committed
142
143
struct sockaddr_in relayclient;
struct in_addr	   relayaddr;
144
145
146
secretkey_t	   secretkey;
char		   ourhostname[MAXHOSTNAMELEN];
int		   needshake;
147
148
gid_t		   tipgid;
uid_t		   tipuid;
Timothy Stack's avatar
   
Timothy Stack committed
149
char		  *uploadCommand;
150
151
152
153
154

#ifdef  WITHSSL

SSL_CTX * ctx;
SSL * sslCon;
Timothy Stack's avatar
   
Timothy Stack committed
155
156
SSL * sslRelay;
SSL * sslUpload;
157
158
159
160

int initializedSSL = 0;

const char * certfile = NULL;
Timothy Stack's avatar
   
Timothy Stack committed
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
const char * cafile = NULL;

int
initializessl(void)
{
	static int initializedSSL = 0;
	
	if (initializedSSL)
		return 0;
	
	SSL_load_error_strings();
	SSL_library_init();
	
	ctx = SSL_CTX_new( SSLv23_method() );
	if (ctx == NULL) {
		dolog( LOG_NOTICE, "Failed to create context.");
		return 1;
	}
	
#ifndef PREFIX
#define PREFIX
#endif
	
	if (relay_snd) {
		if (!cafile) { cafile = DEFAULT_CAFILE; }
		if (SSL_CTX_load_verify_locations(ctx, cafile, NULL) == 0) {
			die("cannot load verify locations");
		}
		
		/*
		 * Make it so the client must provide authentication.
		 */
		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER |
				   SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0);
		
		/*
		 * No session caching! Useless and eats up memory.
		 */
		SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
		
		if (!certfile) { certfile = DEFAULT_CLIENT_CERTFILE; }
		if (SSL_CTX_use_certificate_file( ctx,
						  certfile,
						  SSL_FILETYPE_PEM ) <= 0) {
			dolog(LOG_NOTICE, 
			      "Could not load %s as certificate file.",
			      certfile );
			return 1;
		}
		
		if (SSL_CTX_use_PrivateKey_file( ctx,
						 certfile,
						 SSL_FILETYPE_PEM ) <= 0) {
			dolog(LOG_NOTICE, 
			      "Could not load %s as key file.",
			      certfile );
			return 1;
		}
	}
	else {
		if (!certfile) { certfile = DEFAULT_CERTFILE; }
		
		if (SSL_CTX_use_certificate_file( ctx,
						  certfile,
						  SSL_FILETYPE_PEM ) <= 0) {
			dolog(LOG_NOTICE, 
			      "Could not load %s as certificate file.",
			      certfile );
			return 1;
		}
		
		if (SSL_CTX_use_PrivateKey_file( ctx,
						 certfile,
						 SSL_FILETYPE_PEM ) <= 0) {
			dolog(LOG_NOTICE, 
			      "Could not load %s as key file.",
			      certfile );
			return 1;
		}
	}
		
	initializedSSL = 1;

	return 0;
}

int
sslverify(SSL *ssl, char *requiredunit)
{
	X509		*peer = NULL;
	char		cname[256], unitname[256];
	
	assert(ssl != NULL);
	assert(requiredunit != NULL);

	if (SSL_get_verify_result(ssl) != X509_V_OK) {
		dolog(LOG_NOTICE,
		      "sslverify: Certificate did not verify!\n");
		return -1;
	}
	
	if (! (peer = SSL_get_peer_certificate(ssl))) {
		dolog(LOG_NOTICE, "sslverify: No certificate presented!\n");
		return -1;
	}

	/*
	 * Grab stuff from the cert.
	 */
	X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
				  NID_organizationalUnitName,
				  unitname, sizeof(unitname));

	X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
				  NID_commonName,
				  cname, sizeof(cname));
	X509_free(peer);
	
	/*
	 * On the server, things are a bit more difficult since
	 * we share a common cert locally and a per group cert remotely.
	 *
	 * Make sure common name matches.
	 */
	if (strcmp(cname, BOSSNODE)) {
		dolog(LOG_NOTICE,
		      "sslverify: commonname mismatch: %s!=%s\n",
		      cname, BOSSNODE);
		return -1;
	}
291

Timothy Stack's avatar
   
Timothy Stack committed
292
293
294
295
296
297
298
299
300
301
302
303
304
	/*
	 * If the node is remote, then the unitname must match the type.
	 * Simply a convention. 
	 */
	if (strcmp(unitname, requiredunit)) {
		dolog(LOG_NOTICE,
		      "sslverify: unitname mismatch: %s!=Capture Server\n",
		      unitname);
		return -1;
	}
	
	return 0;
}
305
306
#endif /* WITHSSL */ 
#endif /* USESOCKETS */
307
308

int
309
main(int argc, char **argv)
310
311
{
	char strbuf[MAXPATHLEN], *newstr();
Mike Hibler's avatar
Mike Hibler committed
312
	int op, i;
313
	struct sigaction sa;
314
315
	extern int optind;
	extern char *optarg;
316
317
318
#ifdef  USESOCKETS
	struct sockaddr_in name;
#endif
319

Mike Hibler's avatar
Mike Hibler committed
320
321
322
323
	if ((Progname = rindex(argv[0], '/')))
		Progname++;
	else
		Progname = *argv;
324

325
	while ((op = getopt(argc, argv, "rds:Hb:ip:c:T:aou:v:P")) != EOF)
326
		switch (op) {
327
#ifdef	USESOCKETS
328
329
330
331
#ifdef  WITHSSL
		case 'c':
		        certfile = optarg;
		        break;
332
#endif  /* WITHSSL */
333
334
335
		case 'b':
			Bossnode = optarg;
			break;
336
337
338
339

		case 'p':
			serverport = atoi(optarg);
			break;
340
341
342
343

		case 'i':
			standalone = 1;
			break;
344
#endif /* USESOCKETS */
345
346
347
348
		case 'H':
			++hwflow;
			break;

349
350
351
352
353
354
355
356
		case 'd':
			debug++;
			break;

		case 'r':
			runfile++;
			break;

357
358
359
360
361
		case 's':
			if ((i = atoi(optarg)) == 0 ||
			    (speed = val2speed(i)) == 0)
				usage();
			break;
362
363
364
365
366
		case 'T':
			stampinterval = atoi(optarg);
			if (stampinterval < 0)
				usage();
			break;
367
368
369
		case 'P':
			powermon = 1;
			break;
Timothy Stack's avatar
   
Timothy Stack committed
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
#ifdef  WITHSSL
		case 'a':
			relay_snd = 1;
			break;
			
		case 'o':
			relay_rcv = 1;
			break;
			
		case 'u':
			uploadCommand = optarg;
			break;

		case 'v':
			cafile = optarg;
			break;
#endif
387
388
389
390
391
392
393
394
		}

	argc -= optind;
	argv += optind;

	if (argc != 2)
		usage();

395
396
397
	if (!debug)
		(void)daemon(0, 0);

398
399
	Machine = argv[0];

Mike Hibler's avatar
Mike Hibler committed
400
	(void) snprintf(strbuf, sizeof(strbuf), PIDNAME, LOGPATH, argv[0]);
401
	Pidname = newstr(strbuf);
Mike Hibler's avatar
Mike Hibler committed
402
	(void) snprintf(strbuf, sizeof(strbuf), LOGNAME, LOGPATH, argv[0]);
403
	Logname = newstr(strbuf);
Mike Hibler's avatar
Mike Hibler committed
404
	(void) snprintf(strbuf, sizeof(strbuf), RUNNAME, LOGPATH, argv[0]);
405
	Runname = newstr(strbuf);
Mike Hibler's avatar
Mike Hibler committed
406
	(void) snprintf(strbuf, sizeof(strbuf), TTYNAME, TIPPATH, argv[0]);
407
	Ttyname = newstr(strbuf);
Mike Hibler's avatar
Mike Hibler committed
408
	(void) snprintf(strbuf, sizeof(strbuf), PTYNAME, TIPPATH, argv[0]);
409
	Ptyname = newstr(strbuf);
Mike Hibler's avatar
Mike Hibler committed
410
	(void) snprintf(strbuf, sizeof(strbuf), DEVNAME, DEVPATH, argv[1]);
411
412
	Devname = newstr(strbuf);

413
	openlog(Progname, LOG_PID, LOG_TESTBED);
414
415
	dolog(LOG_NOTICE, "starting");

416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
	/*
	 * We process the "action" signals sequentially, there are just
	 * too many interdependencies.  We block em while we shut down too.
	 */
	sigemptyset(&actionsigmask);
	sigaddset(&actionsigmask, SIGHUP);
	sigaddset(&actionsigmask, SIGUSR1);
	sigaddset(&actionsigmask, SIGUSR2);
	allsigmask = actionsigmask;
	sigaddset(&allsigmask, SIGINT);
	sigaddset(&allsigmask, SIGTERM);
	memset(&sa, 0, sizeof sa);
	sa.sa_handler = quit;
	sa.sa_mask = allsigmask;
	sigaction(SIGINT, &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);
Timothy Stack's avatar
   
Timothy Stack committed
432
433
434
435
436
	if (!relay_snd) {
		sa.sa_handler = reinit;
		sa.sa_mask = actionsigmask;
		sigaction(SIGHUP, &sa, NULL);
	}
437
438
439
440
441
442
443
	if (runfile) {
		sa.sa_handler = newrun;
		sigaction(SIGUSR1, &sa, NULL);
	}
	sa.sa_handler = terminate;
	sigaction(SIGUSR2, &sa, NULL);

Timothy Stack's avatar
   
Timothy Stack committed
444
#ifdef HAVE_SRANDOMDEV
445
	srandomdev();
Timothy Stack's avatar
   
Timothy Stack committed
446
447
448
#else
	srand(time(NULL));
#endif
449
	
450
	/*
451
	 * Open up run/log file, console tty, and controlling pty.
452
	 */
453
	if (runfile) {
454
455
456
		unlink(Runname);
		
		if ((runfd = open(Runname,O_WRONLY|O_CREAT|O_APPEND,0600)) < 0)
457
			die("%s: open: %s", Runname, geterr(errno));
458
459
		if (fchmod(runfd, 0640) < 0)
			die("%s: fchmod: %s", Runname, geterr(errno));
460
	}
461
#ifdef  USESOCKETS
Mike Hibler's avatar
Mike Hibler committed
462
463
464
	/*
	 * Verify the bossnode and stash the address info
	 */
Mike Hibler's avatar
Mike Hibler committed
465
466
467
468
469
470
471
472
473
474
475
	{
		struct hostent *he;

		he = gethostbyname(Bossnode);
		if (he == 0) {
			die("gethostbyname(%s): %s",
			    Bossnode, hstrerror(h_errno));
		}
		memcpy ((char *)&Bossaddr.sin_addr, he->h_addr, he->h_length);
		Bossaddr.sin_family = AF_INET;
		Bossaddr.sin_port   = htons(serverport);
Mike Hibler's avatar
Mike Hibler committed
476
477
	}

Mike Hibler's avatar
Mike Hibler committed
478
	(void) snprintf(strbuf, sizeof(strbuf), ACLNAME, ACLPATH, Machine);
479
480
	Aclname = newstr(strbuf);
	
481
482
483
484
485
486
487
488
489
490
491
	/*
	 * Create and bind our socket.
	 */
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
		die("socket(): opening stream socket: %s", geterr(errno));

	i = 1;
	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
		die("setsockopt(): SO_REUSEADDR: %s", geterr(errno));
492
	
493
494
495
496
497
498
499
500
501
502
503
504
505
506
	/* Create wildcard name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
	name.sin_port = 0;
	if (bind(sockfd, (struct sockaddr *) &name, sizeof(name)))
		die("bind(): binding stream socket: %s", geterr(errno));

	/* Find assigned port value and print it out. */
	i = sizeof(name);
	if (getsockname(sockfd, (struct sockaddr *)&name, &i))
		die("getsockname(): %s", geterr(errno));
	portnum = ntohs(name.sin_port);

	if (listen(sockfd, 1) < 0)
507
		die("listen(): %s", geterr(errno));
508

509
510
	if (gethostname(ourhostname, sizeof(ourhostname)) < 0)
		die("gethostname(): %s", geterr(errno));
511

512
513
	createkey();
	dolog(LOG_NOTICE, "Ready! Listening on TCP port %d", portnum);
Timothy Stack's avatar
   
Timothy Stack committed
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
539
540
541

	if (relay_snd) {
		struct sockaddr_in sin;
		struct hostent *he;
		secretkey_t key;
		char *port_idx;
		int port;

		if ((port_idx = strchr(argv[0], ':')) == NULL)
			die("%s: bad format, expecting 'host:port'", argv[0]);
		*port_idx = '\0';
		port_idx += 1;
		if (sscanf(port_idx, "%d", &port) != 1)
			die("%s: bad port number", port_idx);
		he = gethostbyname(argv[0]);
		if (he == 0) {
			die("gethostbyname(%s): %s",
			    argv[0], hstrerror(h_errno));
		}
		bzero(&sin, sizeof(sin));
		memcpy ((char *)&sin.sin_addr, he->h_addr, he->h_length);
		sin.sin_family = AF_INET;
		sin.sin_port = htons(port);

		if ((ptyfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
			die("socket(): %s", geterr(errno));
		if (connect(ptyfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
			die("connect(): %s", geterr(errno));
Mike Hibler's avatar
Mike Hibler committed
542
		snprintf(key.key, sizeof(key.key), "RELAY %d", portnum);
Timothy Stack's avatar
   
Timothy Stack committed
543
544
545
		key.keylen = strlen(key.key);
		if (write(ptyfd, &key, sizeof(key)) != sizeof(key))
			die("write(): %s", geterr(errno));
546
#ifdef  WITHSSL
Timothy Stack's avatar
   
Timothy Stack committed
547
548
549
550
551
552
553
554
555
556
		initializessl();
		sslRelay = SSL_new(ctx);
		if (!sslRelay)
			die("SSL_new()");
		if (SSL_set_fd(sslRelay, ptyfd) <= 0)
			die("SSL_set_fd()");
		if (SSL_connect(sslRelay) <= 0)
			die("SSL_connect()");
		if (sslverify(sslRelay, "Capture Server"))
			die("SSL connection did not verify");
557
#endif
Timothy Stack's avatar
   
Timothy Stack committed
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
		if (fcntl(ptyfd, F_SETFL, O_NONBLOCK) < 0)
			die("fcntl(O_NONBLOCK): %s", geterr(errno));
		tipactive = 1;
	}

	if (relay_rcv) {
		struct hostent *he;

		he = gethostbyname(argv[1]);
		if (he == 0) {
			die("gethostbyname(%s): %s",
			    argv[1], hstrerror(h_errno));
		}
		memcpy ((char *)&relayaddr, he->h_addr, he->h_length);
	}
573
#else
574
	if ((ptyfd = open(Ptyname, O_RDWR)) < 0)
575
		die("%s: open: %s", Ptyname, geterr(errno));
576
#endif
Timothy Stack's avatar
   
Timothy Stack committed
577
578
579
580
581
582
583
584
585
586
587
588
	
	if (!relay_snd) {
		if ((logfd = open(Logname,O_WRONLY|O_CREAT|O_APPEND,0640)) < 0)
			die("%s: open: %s", Logname, geterr(errno));
		if (chmod(Logname, 0640) < 0)
			die("%s: chmod: %s", Logname, geterr(errno));
	}
	
	if (!relay_rcv) {
		rawmode(Devname, speed);
	}
	
589
590
591
592
593
594
595
596
	writepid();

	capture();

	cleanup();
	exit(0);
}

597
598
599
#ifdef TWOPROCESS
int	pid;

600
void
601
capture(void)
602
{
Mike Hibler's avatar
Mike Hibler committed
603
604
	int flags = FNDELAY;

605
606
607
608
609
610
611
612
613
614
615
616
	(void) fcntl(ptyfd, F_SETFL, &flags);

	if (pid = fork())
		in();
	else
		out();
}

/*
 * Loop reading from the console device, writing data to log file and
 * to the pty for tip to pick up.
 */
617
in(void)
618
{
619
	char buf[BUFSIZE];
620
621
	int cc;
	sigset_t omask;
622
623
	
	while (1) {
624
		if ((cc = read(devfd, buf, BUFSIZE)) < 0) {
625
626
627
			if ((errno == EWOULDBLOCK) || (errno == EINTR))
				continue;
			else
628
				die("%s: read: %s", Devname, geterr(errno));
629
		}
630
		sigprocmask(SIG_BLOCK, &actionsigmask, &omask);
631
632

		if (write(logfd, buf, cc) < 0)
633
			die("%s: write: %s", Logname, geterr(errno));
634

635
636
		if (runfile) {
			if (write(runfd, buf, cc) < 0)
637
				die("%s: write: %s", Runname, geterr(errno));
638
639
		}

640
641
		if (write(ptyfd, buf, cc) < 0) {
			if ((errno != EIO) && (errno != EWOULDBLOCK))
642
				die("%s: write: %s", Ptyname, geterr(errno));
643
		}
644
		sigprocmask(SIG_SETMASK, &omask, NULL);
645
646
647
648
649
650
	}
}

/*
 * Loop reading input from pty (tip), and send off to the console device.
 */
651
out(void)
652
{
653
	char buf[BUFSIZE];
654
655
	int cc;
	sigset_t omask;
656
657
658
659
660
661
	struct timeval timeout;

	timeout.tv_sec  = 0;
	timeout.tv_usec = 100000;
	
	while (1) {
662
		sigprocmask(SIG_BLOCK, &actionsigmask, &omask);
663
		if ((cc = read(ptyfd, buf, BUFSIZE)) < 0) {
664
			sigprocmask(SIG_SETMASK, &omask, NULL);
665
666
667
668
669
670
			if ((errno == EIO) || (errno == EWOULDBLOCK) ||
			    (errno == EINTR)) {
				select(0, 0, 0, 0, &timeout);
				continue;
			}
			else
671
				die("%s: read: %s", Ptyname, geterr(errno));
672
673
674
		}

		if (write(devfd, buf, cc) < 0)
675
			die("%s: write: %s", Devname, geterr(errno));
676
		
677
		sigprocmask(SIG_SETMASK, &omask, NULL);
678
679
680
	}
}
#else
681
682
static fd_set	sfds;
static int	fdcount;
683
void
684
capture(void)
685
{
686
	fd_set fds;
687
688
	int i, cc, lcc;
	sigset_t omask;
689
	char buf[BUFSIZE];
690
	struct timeval timeout;
691
692
693
694
#ifdef LOG_DROPS
	int drop_topty_chars = 0;
	int drop_todev_chars = 0;
#endif
695

696
697
698
699
700
701
702
703
704
705
706
707
708
	/*
	 * XXX for now we make both directions non-blocking.  This is a
	 * quick hack to achieve the goal that capture never block
	 * uninterruptably for long periods of time (use threads).
	 * This has the unfortunate side-effect that we may drop chars
	 * from the perspective of the user (use threads).  A more exotic
	 * solution would be to poll the readiness of output (use threads)
	 * as well as input and not read from one end unless we can write
	 * the other (use threads).
	 *
	 * I keep thinking (use threads) that there is a better way to do
	 * this (use threads).  Hmm...
	 */
Timothy Stack's avatar
   
Timothy Stack committed
709
	if ((devfd >= 0) && (fcntl(devfd, F_SETFL, O_NONBLOCK) < 0))
710
		die("%s: fcntl(O_NONBLOCK): %s", Devname, geterr(errno));
711
#ifndef USESOCKETS
712
713
714
715
716
717
718
719
720
721
722
723
	/*
	 * It gets better!
	 * In FreeBSD 4.0 and beyond, the fcntl fails because the slave
	 * side is not open even though the only effect of this call is
	 * to set the file description's FNONBLOCK flag (i.e. the pty and
	 * tty code do nothing additional).  Turns out that we could use
	 * ioctl instead of fcntl to set the flag.  The call will still
	 * fail, but the flag will be left set.  Rather than rely on that
	 * dubious behavior, I temporarily open the slave, do the fcntl
	 * and close the slave again.
	 */
#ifdef __FreeBSD__
724
	if ((i = open(Ttyname, O_RDONLY)) < 0)
725
726
727
728
729
		die("%s: open: %s", Ttyname, geterr(errno));
#endif
	if (fcntl(ptyfd, F_SETFL, O_NONBLOCK) < 0)
		die("%s: fcntl(O_NONBLOCK): %s", Ptyname, geterr(errno));
#ifdef __FreeBSD__
730
	close(i);
731
#endif
732
#endif /* USESOCKETS */
733
734

	FD_ZERO(&sfds);
Timothy Stack's avatar
   
Timothy Stack committed
735
736
	if (devfd >= 0)
		FD_SET(devfd, &sfds);
737
738
739
740
741
742
	fdcount = devfd;
#ifdef  USESOCKETS
	if (devfd < sockfd)
		fdcount = sockfd;
	FD_SET(sockfd, &sfds);
#endif	/* USESOCKETS */
Timothy Stack's avatar
   
Timothy Stack committed
743
744
745
746
747
	if (ptyfd >= 0) {
		if (devfd < ptyfd)
			fdcount = ptyfd;
		FD_SET(ptyfd, &sfds);
	}
748
749
750

	fdcount++;

751
	for (;;) {
752
753
#ifdef LOG_DROPS
		if (drop_topty_chars >= DROP_THRESH) {
754
755
			warning("%d dev -> pty chars dropped",
				drop_topty_chars);
756
757
758
			drop_topty_chars = 0;
		}
		if (drop_todev_chars >= DROP_THRESH) {
759
760
			warning("%d pty -> dev chars dropped",
				drop_todev_chars);
761
			drop_todev_chars = 0;
762
		}
763
#endif
764
		fds = sfds;
765
		timeout.tv_usec = 0;
766
		timeout.tv_sec  = 30;
767
#ifdef	USESOCKETS
768
769
770
		if (needshake) {
			timeout.tv_sec += (random() % 60);
		}
771
772
#endif
		i = select(fdcount, &fds, NULL, NULL, &timeout);
773
774
		if (i < 0) {
			if (errno == EINTR) {
775
				warning("input select interrupted, continuing");
776
777
				continue;
			}
778
			die("%s: select: %s", Devname, geterr(errno));
779
		}
780
		if (i == 0) {
781
782
#ifdef	USESOCKETS
			if (needshake) {
Mike Hibler's avatar
Mike Hibler committed
783
				(void) handshake();
784
785
786
				continue;
			}
#endif
787
788
			continue;
		}
789
790
#ifdef	USESOCKETS
		if (FD_ISSET(sockfd, &fds)) {
Mike Hibler's avatar
Mike Hibler committed
791
			(void) clientconnect();
792
		}
Timothy Stack's avatar
   
Timothy Stack committed
793
		if ((upfd >=0) && FD_ISSET(upfd, &fds)) {
Mike Hibler's avatar
Mike Hibler committed
794
			(void) handleupload();
Timothy Stack's avatar
   
Timothy Stack committed
795
		}
796
#endif	/* USESOCKETS */
Timothy Stack's avatar
   
Timothy Stack committed
797
		if ((devfd >= 0) && FD_ISSET(devfd, &fds)) {
798
			errno = 0;
Timothy Stack's avatar
   
Timothy Stack committed
799
800
801
802
803
804
805
806
807
808
809
810
811
#ifdef  WITHSSL
			if (relay_rcv) {
			  cc = SSL_read(sslRelay, buf, sizeof(buf));
			  if (cc <= 0) {
			    FD_CLR(devfd, &sfds);
			    devfd = -1;
			    bzero(&relayclient, sizeof(relayclient));
			    continue;
			  }
			}
			else
#endif
			  cc = read(devfd, buf, sizeof(buf));
812
			if (cc < 0)
813
				die("%s: read: %s", Devname, geterr(errno));
814
			if (cc == 0)
815
				die("%s: read: EOF", Devname);
816
817
			errno = 0;

818
			sigprocmask(SIG_BLOCK, &actionsigmask, &omask);
819
820
821
822
#ifdef	USESOCKETS
			if (!tipactive)
				goto dropped;
#endif
823
			for (lcc = 0; lcc < cc; lcc += i) {
824
#ifdef  WITHSSL
Timothy Stack's avatar
   
Timothy Stack committed
825
826
827
828
				if (relay_snd) {
					i = SSL_write(sslRelay, &buf[lcc], cc-lcc);
				}
			        else if (sslCon != NULL) {
829
830
831
832
833
834
835
				        i = SSL_write(sslCon, &buf[lcc], cc-lcc);
					if (i < 0) { i = 0; } /* XXX Hack */
			        } else
#endif /* WITHSSL */ 
			        {
				        i = write(ptyfd, &buf[lcc], cc-lcc);
				}
836
				if (i < 0) {
837
838
839
840
841
842
843
					/*
					 * Either tip is blocked (^S) or
					 * not running (the latter should
					 * return EIO but doesn't due to a
					 * pty bug).  Note that we have
					 * dropped some chars.
					 */
844
					if (errno == EIO || errno == EAGAIN) {
845
846
#ifdef LOG_DROPS
						drop_topty_chars += (cc-lcc);
847
848
849
#endif
						goto dropped;
					}
850
					die("%s: write: %s",
851
852
					    Ptyname, geterr(errno));
				}
853
854
				if (i == 0) {
#ifdef	USESOCKETS
855
					sigprocmask(SIG_SETMASK, &omask, NULL);
856
857
					goto disconnected;
#else
858
					die("%s: write: zero-length", Ptyname);
859
860
#endif
				}
861
862
			}
dropped:
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
			if (stampinterval >= 0) {
				static time_t laststamp;
				struct timeval tv;
				char stampbuf[40], *cts;
				time_t now;

				gettimeofday(&tv, 0);
				now = tv.tv_sec;
				if (stampinterval == 0 ||
				    now > laststamp + stampinterval) {
					cts = ctime(&now);
					cts[24] = 0;
					snprintf(stampbuf, sizeof stampbuf,
						 "\nSTAMP{%s}\n", cts);
					write(logfd, stampbuf,
					      strlen(stampbuf));
				}
				laststamp = now;
			}
Timothy Stack's avatar
   
Timothy Stack committed
882
883
884
885
886
887
888
			if (logfd >= 0) {
				i = write(logfd, buf, cc);
				if (i < 0)
					die("%s: write: %s", Logname, geterr(errno));
				if (i != cc)
					die("%s: write: incomplete", Logname);
			}
889
890
891
			if (runfile) {
				i = write(runfd, buf, cc);
				if (i < 0)
892
					die("%s: write: %s",
893
894
					    Runname, geterr(errno));
				if (i != cc)
895
					die("%s: write: incomplete", Runname);
896
			}
897
			sigprocmask(SIG_SETMASK, &omask, NULL);
898
899

		}
Timothy Stack's avatar
   
Timothy Stack committed
900
		if ((ptyfd >= 0) && FD_ISSET(ptyfd, &fds)) {
901
902
			int lerrno;

903
			sigprocmask(SIG_BLOCK, &actionsigmask, &omask);
904
			errno = 0;
905
#ifdef WITHSSL
Timothy Stack's avatar
   
Timothy Stack committed
906
907
908
909
910
911
912
913
914
915
			if (relay_snd) {
				cc = SSL_read( sslRelay, buf, sizeof(buf) );
				if (cc < 0) { /* XXX hack */
					cc = 0;
					SSL_free(sslRelay);
					sslRelay = NULL;
					upportnum = -1;
				}
			}
			else if (sslCon != NULL) {
916
			        cc = SSL_read( sslCon, buf, sizeof(buf) );
Timothy Stack's avatar
   
Timothy Stack committed
917
918
919
920
921
				if (cc < 0) { /* XXX hack */
					cc = 0;
					SSL_free(sslCon);
					sslCon = NULL;
				}
922
923
924
			} else
#endif /* WITHSSL */ 
			{
Timothy Stack's avatar
   
Timothy Stack committed
925
			        cc = read(ptyfd, buf, sizeof(buf));
926
			}
927
			lerrno = errno;
928
			sigprocmask(SIG_SETMASK, &omask, NULL);
929
930
			if (cc < 0) {
				/* XXX commonly observed */
931
				if (lerrno == EIO || lerrno == EAGAIN)
932
					continue;
933
#ifdef	USESOCKETS
934
				if (lerrno == ECONNRESET || lerrno == ETIMEDOUT)
935
					goto disconnected;
936
937
938
939
				die("%s: socket read: %s",
				    Machine, geterr(lerrno));
#else
				die("%s: read: %s", Ptyname, geterr(lerrno));
940
#endif
941
942
			}
			if (cc == 0) {
943
944
945
946
947
#ifdef	USESOCKETS
			disconnected:
				/*
				 * Other end disconnected.
				 */
Timothy Stack's avatar
   
Timothy Stack committed
948
949
				if (relay_snd)
					die("relay receiver died");
950
951
952
953
954
955
				dolog(LOG_INFO, "%s disconnecting",
				      inet_ntoa(tipclient.sin_addr));
				FD_CLR(ptyfd, &sfds);
				close(ptyfd);
				tipactive = 0;
				continue;
956
#else
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
				/*
				 * Delay after reading 0 bytes from the pty.
				 * At least under FreeBSD, select on a
				 * disconnected pty (control half) always
				 * return ready and the subsequent read always
				 * returns 0.  To keep capture from eating up
				 * CPU constantly when no one is connected to
				 * the pty (i.e., most of the time) we delay
				 * after doing a zero length read.
				 *
				 * Note we keep tabs on the device so that we
				 * will wake up early if it goes active.
				 */
				timeout.tv_sec  = 1;
				timeout.tv_usec = 0;
				FD_ZERO(&fds);
				FD_SET(devfd, &fds);
				select(devfd+1, &fds, 0, 0, &timeout);
975
				continue;
976
#endif
977
978
979
			}
			errno = 0;

980
			sigprocmask(SIG_BLOCK, &actionsigmask, &omask);
981
			for (lcc = 0; lcc < cc; lcc += i) {
Timothy Stack's avatar
   
Timothy Stack committed
982
983
				if (relay_rcv) {
#ifdef USESOCKETS
984
#ifdef  WITHSSL
Timothy Stack's avatar
   
Timothy Stack committed
985
986
987
988
					if (sslRelay != NULL) {
						i = SSL_write(sslRelay,
							      &buf[lcc],
							      cc - lcc);
989
990
991
					} else
#endif
					{
Timothy Stack's avatar
   
Timothy Stack committed
992
993
994
995
996
997
998
						i = cc - lcc;
					}
#endif
				}
				else {
					i = write(devfd, &buf[lcc], cc-lcc);
				}
999
				if (i < 0) {
1000
					/*
For faster browsing, not all history is shown. View entire blame