tcpscript.c
author viric@llimona
Wed, 04 Jul 2007 21:16:53 +0200
changeset 7 6102b11bac49
parent 6 07f184944f3f
child 11 53b2466f1c2f
permissions -rw-r--r--
Better help for maxconns
viric@0
     1
/*
viric@0
     2
 * Copyright (c) 1980 Regents of the University of California.
viric@0
     3
 * All rights reserved.
viric@0
     4
 *
viric@0
     5
 * Redistribution and use in source and binary forms, with or without
viric@0
     6
 * modification, are permitted provided that the following conditions
viric@0
     7
 * are met:
viric@0
     8
 * 1. Redistributions of source code must retain the above copyright
viric@0
     9
 *    notice, this list of conditions and the following disclaimer.
viric@0
    10
 * 2. Redistributions in binary form must reproduce the above copyright
viric@0
    11
 *    notice, this list of conditions and the following disclaimer in the
viric@0
    12
 *    documentation and/or other materials provided with the distribution.
viric@0
    13
 * 3. All advertising materials mentioning features or use of this software
viric@0
    14
 *    must display the following acknowledgement:
viric@0
    15
 *	This product includes software developed by the University of
viric@0
    16
 *	California, Berkeley and its contributors.
viric@0
    17
 * 4. Neither the name of the University nor the names of its contributors
viric@0
    18
 *    may be used to endorse or promote products derived from this software
viric@0
    19
 *    without specific prior written permission.
viric@0
    20
 *
viric@0
    21
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
viric@0
    22
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
viric@0
    23
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
viric@0
    24
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
viric@0
    25
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
viric@0
    26
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
viric@0
    27
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
viric@0
    28
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
viric@0
    29
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
viric@0
    30
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
viric@0
    31
 * SUCH DAMAGE.
viric@0
    32
 */
viric@0
    33
viric@0
    34
/*
viric@0
    35
 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
viric@0
    36
 * - added Native Language Support
viric@0
    37
 *
viric@0
    38
 * 2000-07-30 Per Andreas Buer <per@linpro.no> - added "q"-option
viric@0
    39
 */
viric@0
    40
viric@0
    41
/*
viric@6
    42
 * tcpscript - Modified from 'script' - 2007 Lluis Batlle i Rossell
viric@0
    43
 */
viric@0
    44
#include <stdio.h>
viric@0
    45
#include <stdlib.h>
viric@0
    46
#include <sys/types.h>
viric@0
    47
#include <sys/stat.h>
viric@0
    48
#include <termios.h>
viric@0
    49
#include <sys/ioctl.h>
viric@0
    50
#include <sys/time.h>
viric@0
    51
#include <sys/signal.h>
viric@1
    52
#include <sys/socket.h>
viric@1
    53
#include <netinet/in.h>
viric@0
    54
viric@0
    55
#ifdef __linux__
viric@0
    56
#include <unistd.h>
viric@0
    57
#include <string.h>
viric@0
    58
#endif
viric@0
    59
viric@1
    60
#define HAVE_openpty
viric@1
    61
#define _PATH_BSHELL "/bin/sh"
viric@0
    62
#ifdef HAVE_openpty
viric@0
    63
#include <pty.h>
viric@0
    64
#endif
viric@0
    65
viric@0
    66
void finish(int);
viric@0
    67
void done(void);
viric@0
    68
void fail(void);
viric@0
    69
void resize(int);
viric@0
    70
void fixtty(void);
viric@0
    71
void getmaster(void);
viric@0
    72
void getslave(void);
viric@0
    73
void doinput(void);
viric@0
    74
void dooutput(void);
viric@0
    75
void doshell(void);
viric@0
    76
viric@0
    77
char	*shell;
viric@0
    78
int	master;
viric@0
    79
int	slave;
viric@0
    80
int	child;
viric@0
    81
int	subchild;
viric@0
    82
char	*fname;
viric@0
    83
viric@0
    84
struct	termios tt;
viric@0
    85
struct	winsize win;
viric@0
    86
int	lb;
viric@0
    87
int	l;
viric@0
    88
#ifndef HAVE_openpty
viric@0
    89
char	line[] = "/dev/ptyXX";
viric@0
    90
#endif
viric@0
    91
char	*cflg = NULL;
viric@0
    92
int	qflg = 0;
viric@1
    93
int	tcp_port = 4000;
viric@3
    94
int	limit_conns = 1;
viric@1
    95
viric@1
    96
enum {
viric@1
    97
    MAXCONNS = 10
viric@1
    98
};
viric@5
    99
static int accept_socket;
viric@6
   100
const char version[] = "0.1";
viric@0
   101
viric@0
   102
static char *progname;
viric@0
   103
viric@0
   104
static void
viric@0
   105
die_if_link(char *fn) {
viric@0
   106
	struct stat s;
viric@0
   107
viric@0
   108
	if (lstat(fn, &s) == 0 && (S_ISLNK(s.st_mode) || s.st_nlink > 1)) {
viric@0
   109
		fprintf(stderr,
viric@1
   110
			"Warning: `%s' is a link.\n"
viric@0
   111
			  "Use `%s [options] %s' if you really "
viric@0
   112
			  "want to use it.\n"
viric@1
   113
			  "Script not started.\n",
viric@0
   114
			fn, progname, fn);
viric@0
   115
		exit(1);
viric@0
   116
	}
viric@0
   117
}
viric@0
   118
viric@0
   119
/*
viric@0
   120
 * script -t prints time delays as floating point numbers
viric@0
   121
 * The example program (scriptreplay) that we provide to handle this
viric@0
   122
 * timing output is a perl script, and does not handle numbers in
viric@0
   123
 * locale format (not even when "use locale;" is added).
viric@0
   124
 * So, since these numbers are not for human consumption, it seems
viric@0
   125
 * easiest to set LC_NUMERIC here.
viric@0
   126
 */
viric@0
   127
viric@0
   128
int
viric@0
   129
main(int argc, char **argv) {
viric@0
   130
	extern int optind;
viric@0
   131
	char *p;
viric@0
   132
	int ch;
viric@0
   133
viric@0
   134
	progname = argv[0];
viric@0
   135
	if ((p = strrchr(progname, '/')) != NULL)
viric@0
   136
		progname = p+1;
viric@0
   137
viric@0
   138
viric@0
   139
	if (argc == 2) {
viric@0
   140
		if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) {
viric@6
   141
			printf("%s %s, modification of 'script' by %s\n",
viric@6
   142
			       progname, version,
viric@6
   143
                   "Lluis Batlle i Rossell 2007");
viric@0
   144
			return 0;
viric@0
   145
		}
viric@0
   146
	}
viric@0
   147
viric@3
   148
	while ((ch = getopt(argc, argv, "c:qp:l:")) != -1)
viric@0
   149
		switch((char)ch) {
viric@0
   150
		case 'c':
viric@0
   151
			cflg = optarg;
viric@0
   152
			break;
viric@1
   153
		case 'p':
viric@1
   154
			tcp_port = atoi(optarg);
viric@1
   155
			break;
viric@3
   156
		case 'l':
viric@3
   157
			limit_conns = atoi(optarg);
viric@3
   158
			break;
viric@0
   159
		case 'q':
viric@0
   160
			qflg++;
viric@0
   161
			break;
viric@0
   162
		case '?':
viric@0
   163
		default:
viric@0
   164
			fprintf(stderr,
viric@7
   165
				"usage: script [-l maxconns] [-p port] [-q] [-t] \n");
viric@0
   166
			exit(1);
viric@0
   167
		}
viric@0
   168
	argc -= optind;
viric@0
   169
	argv += optind;
viric@0
   170
viric@0
   171
	shell = getenv("SHELL");
viric@0
   172
	if (shell == NULL)
viric@0
   173
		shell = _PATH_BSHELL;
viric@0
   174
viric@0
   175
	getmaster();
viric@0
   176
	fixtty();
viric@0
   177
viric@5
   178
    accept_socket = listen_tcp();
viric@5
   179
viric@0
   180
	(void) signal(SIGCHLD, finish);
viric@0
   181
	child = fork();
viric@0
   182
	if (child < 0) {
viric@0
   183
		perror("fork");
viric@0
   184
		fail();
viric@0
   185
	}
viric@0
   186
	if (child == 0) {
viric@0
   187
		subchild = child = fork();
viric@0
   188
		if (child < 0) {
viric@0
   189
			perror("fork");
viric@0
   190
			fail();
viric@0
   191
		}
viric@0
   192
		if (child)
viric@0
   193
			dooutput();
viric@0
   194
		else
viric@5
   195
        {
viric@5
   196
            close(accept_socket);
viric@0
   197
			doshell();
viric@5
   198
        }
viric@0
   199
	} else
viric@5
   200
    {
viric@5
   201
        close(accept_socket);
viric@0
   202
		(void) signal(SIGWINCH, resize);
viric@5
   203
    }
viric@0
   204
	doinput();
viric@0
   205
viric@0
   206
	return 0;
viric@0
   207
}
viric@0
   208
viric@0
   209
void
viric@0
   210
doinput() {
viric@0
   211
	register int cc;
viric@0
   212
	char ibuf[BUFSIZ];
viric@0
   213
viric@0
   214
	while ((cc = read(0, ibuf, BUFSIZ)) > 0)
viric@0
   215
		(void) write(master, ibuf, cc);
viric@0
   216
	done();
viric@0
   217
}
viric@0
   218
viric@0
   219
#include <sys/wait.h>
viric@0
   220
viric@0
   221
void
viric@0
   222
finish(int dummy) {
viric@0
   223
	int status;
viric@0
   224
	register int pid;
viric@0
   225
	register int die = 0;
viric@0
   226
viric@0
   227
	while ((pid = wait3(&status, WNOHANG, 0)) > 0)
viric@0
   228
		if (pid == child)
viric@0
   229
			die = 1;
viric@0
   230
viric@0
   231
	if (die)
viric@0
   232
		done();
viric@0
   233
}
viric@0
   234
viric@0
   235
void
viric@0
   236
resize(int dummy) {
viric@0
   237
	/* transmit window change information to the child */
viric@0
   238
	(void) ioctl(0, TIOCGWINSZ, (char *)&win);
viric@0
   239
	(void) ioctl(slave, TIOCSWINSZ, (char *)&win);
viric@0
   240
viric@0
   241
	kill(child, SIGWINCH);
viric@0
   242
}
viric@0
   243
viric@1
   244
void fatal()
viric@1
   245
{
viric@5
   246
    done();
viric@1
   247
    exit(-1);
viric@1
   248
}
viric@1
   249
viric@3
   250
int set_reusable(int s)
viric@3
   251
{
viric@3
   252
    int boolean = 1;
viric@3
   253
    int res;
viric@3
   254
viric@3
   255
    res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &boolean, sizeof(boolean));
viric@3
   256
    if (res != 0)
viric@3
   257
        perror("Setsockopt REUSE failed"), fatal();
viric@3
   258
}
viric@3
   259
viric@1
   260
int listen_tcp()
viric@1
   261
{
viric@1
   262
    int s;
viric@1
   263
    struct sockaddr_in addr;
viric@1
   264
    int res;
viric@1
   265
viric@1
   266
    s = socket(PF_INET, SOCK_STREAM, 0);
viric@1
   267
    if (s == -1)
viric@1
   268
        perror("Failed socket()"), fatal();
viric@1
   269
viric@3
   270
    set_reusable(s);
viric@3
   271
viric@1
   272
    addr.sin_family = AF_INET;
viric@1
   273
    addr.sin_port = htons(tcp_port);
viric@1
   274
    addr.sin_addr.s_addr = INADDR_ANY;
viric@1
   275
    res = bind(s, (struct sockaddr *) &addr, sizeof(addr));
viric@1
   276
    if (s != 0)
viric@1
   277
        perror("Failed bind()"), fatal();
viric@1
   278
viric@1
   279
    res = listen(s, 1);
viric@1
   280
    if (s != 0)
viric@1
   281
        perror("Failed listen()"), fatal();
viric@1
   282
viric@2
   283
    if (!qflg)
viric@2
   284
        printf("Listening on port %i\n", tcp_port);
viric@1
   285
    return s;
viric@1
   286
}
viric@1
   287
viric@1
   288
void remove_element(int index, int *array, int total)
viric@1
   289
{
viric@1
   290
    int i;
viric@1
   291
    for (i = index; i < (total - 1); ++i)
viric@1
   292
        array[i] = array[i+1];
viric@1
   293
}
viric@1
   294
viric@0
   295
void
viric@0
   296
dooutput() {
viric@1
   297
	int cc;
viric@0
   298
	char obuf[BUFSIZ];
viric@1
   299
    int conns[MAXCONNS];
viric@1
   300
    int nconns;
viric@1
   301
    int i;
viric@0
   302
viric@0
   303
	(void) close(0);
viric@0
   304
#ifdef HAVE_openpty
viric@0
   305
	(void) close(slave);
viric@0
   306
#endif
viric@1
   307
    fd_set read_set;
viric@1
   308
    fd_set should_read_set;
viric@1
   309
    fd_set tcp_set;
viric@0
   310
viric@1
   311
    FD_ZERO(&should_read_set);
viric@1
   312
    FD_SET(accept_socket, &should_read_set);
viric@1
   313
    FD_SET(master, &should_read_set);
viric@1
   314
viric@1
   315
    nconns = 0;
viric@0
   316
	for (;;) {
viric@1
   317
        int max;
viric@1
   318
        read_set = should_read_set;
viric@1
   319
viric@1
   320
        /* Calculate max fd */
viric@1
   321
        max = accept_socket;
viric@1
   322
        if (master > max)
viric@1
   323
            max = master;
viric@1
   324
        for (i = 0; i < nconns; ++i)
viric@1
   325
            if (conns[i] > max)
viric@1
   326
                max = conns[i];
viric@1
   327
        
viric@1
   328
        select(max + 1, &read_set, 0, 0, 0);
viric@1
   329
viric@1
   330
        if (FD_ISSET(master, &read_set))
viric@1
   331
        {
viric@1
   332
            cc = read(master, obuf, sizeof (obuf));
viric@1
   333
            if (cc <= 0)
viric@1
   334
                break;
viric@1
   335
            /* Write to the terminal output */
viric@1
   336
            (void) write(1, obuf, cc);
viric@1
   337
            /* Broadcast messages */
viric@1
   338
            for (i = 0; i < nconns; ++i)
viric@1
   339
                (void) write(conns[i], obuf, cc);
viric@1
   340
        }
viric@1
   341
        for (i = 0; i < nconns; ++i)
viric@1
   342
        {
viric@1
   343
            if (FD_ISSET(conns[i], &read_set))
viric@1
   344
            {
viric@1
   345
                int size;
viric@1
   346
                size = read(conns[i], obuf, sizeof(obuf));
viric@1
   347
                if (size == 0)
viric@1
   348
                {
viric@1
   349
                    close(conns[i]);
viric@1
   350
                    FD_CLR(conns[i], &should_read_set);
viric@1
   351
                    remove_element(i, conns, nconns);
viric@1
   352
                    nconns--;
viric@1
   353
                }
viric@1
   354
            }
viric@1
   355
        }
viric@1
   356
        if (FD_ISSET(accept_socket, &read_set))
viric@1
   357
        {
viric@1
   358
            int s;
viric@1
   359
            s = accept(accept_socket, 0, 0);
viric@1
   360
            if (s == -1)
viric@1
   361
            {
viric@1
   362
                perror("accept() failed");
viric@1
   363
                break;
viric@1
   364
            }
viric@3
   365
            if (nconns < (MAXCONNS - 1) && nconns < limit_conns)
viric@1
   366
            {
viric@1
   367
                FD_SET(s, &should_read_set);
viric@1
   368
                conns[nconns] = s;
viric@1
   369
                nconns++;
viric@1
   370
            } else
viric@1
   371
                close(s);
viric@1
   372
        }
viric@0
   373
	}
viric@1
   374
viric@1
   375
    close(accept_socket);
viric@1
   376
    for (i = 0; i < nconns; ++i)
viric@1
   377
        close(conns[i]);
viric@1
   378
viric@0
   379
	done();
viric@0
   380
}
viric@0
   381
viric@0
   382
void
viric@0
   383
doshell() {
viric@0
   384
	char *shname;
viric@0
   385
viric@0
   386
#if 0
viric@0
   387
	int t;
viric@0
   388
viric@0
   389
	t = open(_PATH_TTY, O_RDWR);
viric@0
   390
	if (t >= 0) {
viric@0
   391
		(void) ioctl(t, TIOCNOTTY, (char *)0);
viric@0
   392
		(void) close(t);
viric@0
   393
	}
viric@0
   394
#endif
viric@0
   395
viric@0
   396
	getslave();
viric@0
   397
	(void) close(master);
viric@0
   398
	(void) dup2(slave, 0);
viric@0
   399
	(void) dup2(slave, 1);
viric@0
   400
	(void) dup2(slave, 2);
viric@0
   401
	(void) close(slave);
viric@0
   402
viric@0
   403
	shname = strrchr(shell, '/');
viric@0
   404
	if (shname)
viric@0
   405
		shname++;
viric@0
   406
	else
viric@0
   407
		shname = shell;
viric@0
   408
viric@0
   409
	if (cflg)
viric@0
   410
		execl(shell, shname, "-c", cflg, 0);
viric@0
   411
	else
viric@0
   412
		execl(shell, shname, "-i", 0);
viric@0
   413
viric@0
   414
	perror(shell);
viric@0
   415
	fail();
viric@0
   416
}
viric@0
   417
viric@0
   418
void
viric@0
   419
fixtty() {
viric@0
   420
	struct termios rtt;
viric@0
   421
viric@0
   422
	rtt = tt;
viric@0
   423
	cfmakeraw(&rtt);
viric@0
   424
	rtt.c_lflag &= ~ECHO;
viric@0
   425
	(void) tcsetattr(0, TCSAFLUSH, &rtt);
viric@0
   426
}
viric@0
   427
viric@0
   428
void
viric@0
   429
fail() {
viric@0
   430
viric@0
   431
	(void) kill(0, SIGTERM);
viric@0
   432
	done();
viric@0
   433
}
viric@0
   434
viric@0
   435
void
viric@0
   436
done() {
viric@0
   437
	time_t tvec;
viric@0
   438
viric@0
   439
	if (subchild) {
viric@0
   440
		(void) close(master);
viric@0
   441
	} else {
viric@0
   442
		(void) tcsetattr(0, TCSAFLUSH, &tt);
viric@0
   443
	}
viric@0
   444
	exit(0);
viric@0
   445
}
viric@0
   446
viric@0
   447
void
viric@0
   448
getmaster() {
viric@0
   449
#ifdef HAVE_openpty
viric@0
   450
	(void) tcgetattr(0, &tt);
viric@0
   451
	(void) ioctl(0, TIOCGWINSZ, (char *)&win);
viric@0
   452
	if (openpty(&master, &slave, NULL, &tt, &win) < 0) {
viric@1
   453
		fprintf(stderr, "openpty failed\n");
viric@0
   454
		fail();
viric@0
   455
	}
viric@0
   456
#else
viric@0
   457
	char *pty, *bank, *cp;
viric@0
   458
	struct stat stb;
viric@0
   459
viric@0
   460
	pty = &line[strlen("/dev/ptyp")];
viric@0
   461
	for (bank = "pqrs"; *bank; bank++) {
viric@0
   462
		line[strlen("/dev/pty")] = *bank;
viric@0
   463
		*pty = '0';
viric@0
   464
		if (stat(line, &stb) < 0)
viric@0
   465
			break;
viric@0
   466
		for (cp = "0123456789abcdef"; *cp; cp++) {
viric@0
   467
			*pty = *cp;
viric@0
   468
			master = open(line, O_RDWR);
viric@0
   469
			if (master >= 0) {
viric@0
   470
				char *tp = &line[strlen("/dev/")];
viric@0
   471
				int ok;
viric@0
   472
viric@0
   473
				/* verify slave side is usable */
viric@0
   474
				*tp = 't';
viric@0
   475
				ok = access(line, R_OK|W_OK) == 0;
viric@0
   476
				*tp = 'p';
viric@0
   477
				if (ok) {
viric@0
   478
					(void) tcgetattr(0, &tt);
viric@0
   479
				    	(void) ioctl(0, TIOCGWINSZ, 
viric@0
   480
						(char *)&win);
viric@0
   481
					return;
viric@0
   482
				}
viric@0
   483
				(void) close(master);
viric@0
   484
			}
viric@0
   485
		}
viric@0
   486
	}
viric@1
   487
	fprintf(stderr, "Out of pty's\n");
viric@0
   488
	fail();
viric@0
   489
#endif /* not HAVE_openpty */
viric@0
   490
}
viric@0
   491
viric@0
   492
void
viric@0
   493
getslave() {
viric@0
   494
#ifndef HAVE_openpty
viric@0
   495
	line[strlen("/dev/")] = 't';
viric@0
   496
	slave = open(line, O_RDWR);
viric@0
   497
	if (slave < 0) {
viric@0
   498
		perror(line);
viric@0
   499
		fail();
viric@0
   500
	}
viric@0
   501
	(void) tcsetattr(slave, TCSAFLUSH, &tt);
viric@0
   502
	(void) ioctl(slave, TIOCSWINSZ, (char *)&win);
viric@0
   503
#endif
viric@0
   504
	(void) setsid();
viric@0
   505
	(void) ioctl(slave, TIOCSCTTY, 0);
viric@0
   506
}