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