Home | History | Annotate | Line # | Download | only in ssh
ssh.c revision 1.3
      1 /*	$NetBSD: ssh.c,v 1.3 2009/10/21 23:12:09 snj Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1995 Gordon W. Ross
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 /*
     29  * Small Shell - Nothing fancy.  Just runs programs.
     30  * The RAMDISK root uses this to save space.
     31  */
     32 
     33 #include <errno.h>
     34 #include <fcntl.h>
     35 #include <setjmp.h>
     36 #include <signal.h>
     37 #include <stdio.h>
     38 #include <stdlib.h>
     39 #include <string.h>
     40 #include <unistd.h>
     41 
     42 #include <sys/param.h>
     43 #include <sys/wait.h>
     44 
     45 /* XXX - SunOS hacks... */
     46 #ifndef	WCOREDUMP
     47 #define	WCOREDUMP(x) ((x) & 0200)
     48 #endif
     49 
     50 #ifndef	__P
     51 #define __P(x) x
     52 #endif	/* __P */
     53 
     54 extern char *optarg;
     55 extern int optind, opterr;
     56 
     57 #define MAXLINE 256
     58 #define MAXARGS 32
     59 
     60 #define	MAXPATH 256
     61 char cur_path[MAXPATH] = "PATH=/bin:/usr/bin";
     62 
     63 char rc_name[] = ".sshrc";
     64 char *prompt = "ssh: ";
     65 
     66 int eflag;	/* exit on cmd failure */
     67 int iflag;	/* interactive mode (catch interrupts) */
     68 int sflag;	/* read from stdin (ignore file arg) */
     69 int xflag;	/* execution trace */
     70 
     71 /* Command file: name, line number, arg count, arg vector */
     72 char *cf_name;
     73 int cf_line;
     74 int cf_argc;
     75 char **cf_argv;
     76 
     77 int def_omode = 0666;
     78 int run_bg_pid;
     79 
     80 jmp_buf next_cmd;
     81 
     82 void catchsig __P((int sig));
     83 void child_newfd __P((int setfd, char *file, int otype));
     84 int find_in_path __P((char *cmd, char *filebuf));
     85 void print_termsig __P((FILE *fp, int cstat));
     86 int runfile __P((FILE *fp));
     87 
     88 
     89 main(argc, argv)
     90 	int argc;
     91 	char **argv;
     92 {
     93 	struct sigaction sa;
     94 	FILE *cfp;		/* command file ptr */
     95 	int c, sig;
     96 	int error = 0;
     97 
     98 	while ((c = getopt(argc, argv, "eisx")) != -1) {
     99 		switch (c) {
    100 		case 'e':
    101 			eflag++;
    102 			break;
    103 		case 'i':
    104 			eflag++;
    105 			break;
    106 		case 's':
    107 			sflag++;
    108 			break;
    109 		case 'x':
    110 			xflag++;
    111 			break;
    112 		case '?':
    113 			error++;
    114 			break;
    115 		}
    116 	}
    117 	if (error) {
    118 		fprintf(stderr, "usage:  ssh [-eisx] [cmd_file [...]]\n");
    119 		exit(1);
    120 	}
    121 	cf_argc = argc - optind;
    122 	cf_argv = &argv[optind];
    123 
    124 	/* If this is a login shell, run the rc file. */
    125 	if (argv[0] && argv[0][0] == '-') {
    126 		cf_line = 0;
    127 		cf_name = rc_name;
    128 		if ((cfp = fopen(cf_name, "r")) != NULL) {
    129 			error = runfile(cfp);
    130 			fclose(cfp);
    131 		}
    132 	}
    133 
    134 	/* If no file names, read commands from stdin. */
    135 	if (cf_argc == 0)
    136 		sflag++;
    137 	/* If stdin is a tty, be interactive. */
    138 	if (sflag && isatty(fileno(stdin)))
    139 		iflag++;
    140 
    141 	/* Maybe run a command file... */
    142 	if (!sflag && cf_argc) {
    143 		cf_line = 0;
    144 		cf_name = cf_argv[0];
    145 		cfp = fopen(cf_name, "r");
    146 		if (cfp == NULL) {
    147 			perror(cf_name);
    148 			exit(1);
    149 		}
    150 		error = runfile(cfp);
    151 		fclose(cfp);
    152 		exit(error);
    153 	}
    154 
    155 	/* Read commands from stdin. */
    156 	cf_line = 0;
    157 	cf_name = "(stdin)";
    158 	if (iflag) {
    159 		eflag = 0;	/* don't kill shell on error. */
    160 		sig = setjmp(next_cmd);
    161 		if (sig == 0) {
    162 			/* Initialization... */
    163 			sa.sa_handler = catchsig;
    164 			sa.sa_flags = 0;
    165 			sigemptyset(&sa.sa_mask);
    166 			sigaction(SIGINT,  &sa, NULL);
    167 			sigaction(SIGQUIT, &sa, NULL);
    168 			sigaction(SIGTERM, &sa, NULL);
    169 		} else {
    170 			/* Got here via longjmp. */
    171 			fprintf(stderr, " signal %d\n", sig);
    172 			sigemptyset(&sa.sa_mask);
    173 			sigprocmask(SIG_SETMASK, &sa.sa_mask, NULL);
    174 		}
    175 	}
    176 	error = runfile(stdin);
    177 	exit (error);
    178 }
    179 
    180 void
    181 catchsig(sig)
    182 	int sig;
    183 {
    184 	longjmp(next_cmd, sig);
    185 }
    186 
    187 /*
    188  * Run command from the passed stdio file pointer.
    189  * Returns exit status.
    190  */
    191 int
    192 runfile(cfp)
    193 	FILE *cfp;
    194 {
    195 	char ibuf[MAXLINE];
    196 	char *argv[MAXARGS];
    197 	char *p;
    198 	int i, argc, exitcode, cpid, cstat;
    199 
    200 	/* The command loop. */
    201 	exitcode = 0;
    202 	for (;;) {
    203 		if (iflag) {
    204 			fprintf(stderr, prompt);
    205 			fflush(stderr);
    206 		}
    207 
    208 		if ((fgets(ibuf, sizeof(ibuf), cfp)) == NULL)
    209 			break;
    210 		cf_line++;
    211 
    212 		argc = 0;
    213 		p = ibuf;
    214 
    215 		while (argc < MAXARGS-1) {
    216 			/* skip blanks or tabs */
    217 			while ((*p == ' ') || (*p == '\t')) {
    218 			next_token:
    219 				*p++ = '\0';
    220 			}
    221 			/* end of line? */
    222 			if ((*p == '\n') || (*p == '#')) {
    223 				*p = '\0';
    224 				break;	/* to eol */
    225 			}
    226 			if (*p == '\0')
    227 				break;
    228 			/* save start of token */
    229 			argv[argc++] = p;
    230 			/* find end of token */
    231 			while (*p) {
    232 				if ((*p == '\n') || (*p == '#')) {
    233 					*p = '\0';
    234 					goto eol;
    235 				}
    236 				if ((*p == ' ') || (*p == '\t'))
    237 					goto next_token;
    238 				p++;
    239 			}
    240 		}
    241 	eol:
    242 
    243 		if (argc > 0) {
    244 			if (xflag) {
    245 				fprintf(stderr, "x");
    246 				for (i = 0; i < argc; i++) {
    247 					fprintf(stderr, " %s", argv[i]);
    248 				}
    249 				fprintf(stderr, "\n");
    250 			}
    251 			argv[argc] = NULL;
    252 			exitcode = cmd_eval(argc, argv);
    253 		}
    254 
    255 		/* Collect children. */
    256 		while ((cpid = waitpid(0, &cstat, WNOHANG)) > 0) {
    257 			if (iflag) {
    258 				fprintf(stderr, "[%d] ", cpid);
    259 				if (WTERMSIG(cstat)) {
    260 					print_termsig(stderr, cstat);
    261 				} else {
    262 					fprintf(stderr, "Exited, status %d\n",
    263 							WEXITSTATUS(cstat));
    264 				}
    265 			}
    266 		}
    267 
    268 		if (exitcode && eflag)
    269 			break;
    270 	}
    271 	/* return status of last command */
    272 	return (exitcode);
    273 }
    274 
    275 
    276 /****************************************************************
    277  *  Table of buildin commands
    278  *  for cmd_eval() to search...
    279  ****************************************************************/
    280 
    281 struct cmd {
    282 	char *name;
    283 	int (*func)();
    284 	char *help;
    285 };
    286 struct cmd cmd_table[];
    287 
    288 /*
    289  * Evaluate a command named as argv[0]
    290  * with arguments argv[1],argv[2]...
    291  * Returns exit status.
    292  */
    293 int
    294 cmd_eval(argc, argv)
    295 	int argc;
    296 	char **argv;
    297 {
    298 	struct cmd *cp;
    299 
    300 	/*
    301 	 * Do linear search for a builtin command.
    302 	 * Performance does not matter here.
    303 	 */
    304 	for (cp = cmd_table; cp->name; cp++) {
    305 		if (!strcmp(cp->name, argv[0])) {
    306 			/* Pass only args to builtin. */
    307 			--argc; argv++;
    308 			return (cp->func(argc, argv));
    309 		}
    310 	}
    311 
    312 	/*
    313 	 * If no matching builtin, let "run ..."
    314 	 * have a chance to try an external.
    315 	 */
    316 	return (cmd_run(argc, argv));
    317 }
    318 
    319 /*****************************************************************
    320  *  Here are the actual commands.  For these,
    321  *  the command name has been skipped, so
    322  *  argv[0] is the first arg (if any args).
    323  *  All return an exit status.
    324  ****************************************************************/
    325 
    326 char help_cd[] = "cd [dir]";
    327 
    328 int
    329 cmd_cd(argc, argv)
    330 	int argc;
    331 	char **argv;
    332 {
    333 	char *dir;
    334 	int err;
    335 
    336 	if (argc > 0)
    337 		dir = argv[0];
    338 	else {
    339 		dir = getenv("HOME");
    340 		if (dir == NULL)
    341 			dir = "/";
    342 	}
    343 	if (chdir(dir)) {
    344 		perror(dir);
    345 		return (1);
    346 	}
    347 	return(0);
    348 }
    349 
    350 char help_exit[] = "exit [n]";
    351 
    352 int
    353 cmd_exit(argc, argv)
    354 	int argc;
    355 	char **argv;
    356 {
    357 	int val = 0;
    358 
    359 	if (argc > 0)
    360 		val = atoi(argv[0]);
    361 	exit(val);
    362 }
    363 
    364 char help_help[] = "help [command]";
    365 
    366 int
    367 cmd_help(argc, argv)
    368 	int argc;
    369 	char **argv;
    370 {
    371 	struct cmd *cp;
    372 
    373 	if (argc > 0) {
    374 		for (cp = cmd_table; cp->name; cp++) {
    375 			if (!strcmp(cp->name, argv[0])) {
    376 				printf("usage:  %s\n", cp->help);
    377 				return (0);
    378 			}
    379 		}
    380 		printf("%s: no such command\n", argv[0]);
    381 	}
    382 
    383 	printf("Builtin commands: ");
    384 	for (cp = cmd_table; cp->name; cp++) {
    385 		printf(" %s", cp->name);
    386 	}
    387 	printf("\nFor specific usage:  help [command]\n");
    388 	return (0);
    389 }
    390 
    391 char help_path[] = "path [dir1:dir2:...]";
    392 
    393 int
    394 cmd_path(argc, argv)
    395 	int argc;
    396 	char **argv;
    397 {
    398 	int i;
    399 
    400 	if (argc <= 0) {
    401 		printf("%s\n", cur_path);
    402 		return(0);
    403 	}
    404 
    405 	strncpy(cur_path+5, argv[0], MAXPATH-6);
    406 	putenv(cur_path);
    407 
    408 	return (0);
    409 }
    410 
    411 /*****************************************************************
    412  *  The "run" command is the big one.
    413  *  Does fork/exec/wait, redirection...
    414  *  Returns exit status of child
    415  *  (or zero for a background job)
    416  ****************************************************************/
    417 
    418 char help_run[] = "\
    419 run [-bg] [-i ifile] [-o ofile] [-e efile] program [args...]\n\
    420 or simply:  program [args...]";
    421 
    422 int
    423 cmd_run(argc, argv)
    424 	int argc;
    425 	char **argv;
    426 {
    427 	struct sigaction sa;
    428 	int pid, err, cstat, fd;
    429 	char file[MAXPATHLEN];
    430 	int background;
    431 	char *opt, *ifile, *ofile, *efile;
    432 	extern char **environ;
    433 
    434 	/*
    435 	 * Parse options:
    436 	 * -b  : background
    437 	 * -i  : input file
    438 	 * -o  : output file
    439 	 * -e  : error file
    440 	 */
    441 	background = 0;
    442 	ifile = ofile = efile = NULL;
    443 	while ((argc > 0) && (argv[0][0] == '-')) {
    444 		opt = argv[0];
    445 		--argc; argv++;
    446 		switch (opt[1]) {
    447 		case 'b':
    448 			background++;
    449 			break;
    450 		case 'i':
    451 			ifile = argv[0];
    452 			goto shift;
    453 		case 'o':
    454 			ofile = argv[0];
    455 			goto shift;
    456 		case 'e':
    457 			efile = argv[0];
    458 			goto shift;
    459 		default:
    460 			fprintf(stderr, "run %s: bad option\n", opt);
    461 			return (1);
    462 		shift:
    463 			--argc; argv++;
    464 		}
    465 	}
    466 
    467 	if (argc <= 0) {
    468 		fprintf(stderr, "%s:%d run: missing command\n",
    469 				cf_name, cf_line);
    470 		return (1);
    471 	}
    472 
    473 	/* Commands containing '/' get no path search. */
    474 	if (strchr(argv[0], '/')) {
    475 		strncpy(file, argv[0], sizeof(file)-1);
    476 		if (access(file, X_OK)) {
    477 			perror(file);
    478 			return (1);
    479 		}
    480 	} else {
    481 		if (find_in_path(argv[0], file)) {
    482 			fprintf(stderr, "%s: command not found\n", argv[0]);
    483 			return (1);
    484 		}
    485 	}
    486 
    487 	pid = fork();
    488 	if (pid == 0) {
    489 		/* child runs this */
    490 		/* handle redirection options... */
    491 		if (ifile)
    492 			child_newfd(0, ifile, O_RDONLY);
    493 		if (ofile)
    494 			child_newfd(1, ofile, O_WRONLY|O_CREAT);
    495 		if (efile)
    496 			child_newfd(2, efile, O_WRONLY|O_CREAT);
    497 		if (background) {
    498 			/* Ignore SIGINT, SIGQUIT */
    499 			sa.sa_handler = SIG_IGN;
    500 			sa.sa_flags = 0;
    501 			sigemptyset(&sa.sa_mask);
    502 			sigaction(SIGINT,  &sa, NULL);
    503 			sigaction(SIGQUIT, &sa, NULL);
    504 		}
    505 		err = execve(file, argv, environ);
    506 		perror(argv[0]);
    507 		return (1);
    508 	}
    509 	/* parent */
    510 	/* Handle background option... */
    511 	if (background) {
    512 		fprintf(stderr, "[%d]\n", pid);
    513 		run_bg_pid = pid;
    514 		return (0);
    515 	}
    516 	if (waitpid(pid, &cstat, 0) < 0) {
    517 		perror("waitpid");
    518 		return (1);
    519 	}
    520 	if (WTERMSIG(cstat)) {
    521 		print_termsig(stderr, cstat);
    522 	}
    523 	return (WEXITSTATUS(cstat));
    524 }
    525 
    526 /*****************************************************************
    527  *  table of builtin commands
    528  ****************************************************************/
    529 struct cmd cmd_table[] = {
    530 	{ "cd",   cmd_cd,   help_cd },
    531 	{ "exit", cmd_exit, help_exit },
    532 	{ "help", cmd_help, help_help },
    533 	{ "path", cmd_path, help_path },
    534 	{ "run",  cmd_run,  help_run },
    535 	{ 0 },
    536 };
    537 
    538 /*****************************************************************
    539  *  helper functions for the "run" command
    540  ****************************************************************/
    541 
    542 int
    543 find_in_path(cmd, filebuf)
    544 	char *cmd;
    545 	char *filebuf;
    546 {
    547 	char *dirp, *endp, *bufp;	/* dir, end */
    548 
    549 	dirp = cur_path + 5;
    550 	while (*dirp) {
    551 		endp = dirp;
    552 		bufp = filebuf;
    553 		while (*endp && (*endp != ':'))
    554 			*bufp++ = *endp++;
    555 		*bufp++ = '/';
    556 		strcpy(bufp, cmd);
    557 		if (access(filebuf, X_OK) == 0)
    558 			return (0);
    559 		if (*endp == ':')
    560 			endp++;
    561 		dirp = endp;	/* next dir */
    562 	}
    563 	return (-1);
    564 }
    565 
    566 /*
    567  * Set the file descriptor SETFD to FILE,
    568  * which was opened with OTYPE and MODE.
    569  */
    570 void
    571 child_newfd(setfd, file, otype)
    572 	int setfd;	/* what to set (i.e. 0,1,2) */
    573 	char *file;
    574 	int otype;	/* O_RDONLY, etc. */
    575 {
    576 	int newfd;
    577 
    578 	close(setfd);
    579 	if ((newfd = open(file, otype, def_omode)) < 0) {
    580 		perror(file);
    581 		exit(1);
    582 	}
    583 	if (newfd != setfd) {
    584 		dup2(newfd, setfd);
    585 		close(newfd);
    586 	}
    587 }
    588 
    589 void
    590 print_termsig(fp, cstat)
    591 	FILE *fp;
    592 	int cstat;
    593 {
    594 	fprintf(fp, "Terminated, signal %d",
    595 			WTERMSIG(cstat));
    596 	if (WCOREDUMP(cstat))
    597 		fprintf(fp, " (core dumped)");
    598 	fprintf(fp, "\n");
    599 }
    600