xref: /freebsd/sys/kern/imgact_shell.c (revision 6916a1da50267d0e17d6eb4ed076b1507d694b9e)
19454b2d8SWarner Losh /*-
2cfefd687SGarrett Wollman  * Copyright (c) 1993, David Greenman
3cfefd687SGarrett Wollman  * All rights reserved.
4cfefd687SGarrett Wollman  *
5cfefd687SGarrett Wollman  * Redistribution and use in source and binary forms, with or without
6cfefd687SGarrett Wollman  * modification, are permitted provided that the following conditions
7cfefd687SGarrett Wollman  * are met:
8cfefd687SGarrett Wollman  * 1. Redistributions of source code must retain the above copyright
9cfefd687SGarrett Wollman  *    notice, this list of conditions and the following disclaimer.
10cfefd687SGarrett Wollman  * 2. Redistributions in binary form must reproduce the above copyright
11cfefd687SGarrett Wollman  *    notice, this list of conditions and the following disclaimer in the
12cfefd687SGarrett Wollman  *    documentation and/or other materials provided with the distribution.
13cfefd687SGarrett Wollman  *
14cfefd687SGarrett Wollman  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15cfefd687SGarrett Wollman  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16cfefd687SGarrett Wollman  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171984b014SDavid Greenman  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18cfefd687SGarrett Wollman  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19cfefd687SGarrett Wollman  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20cfefd687SGarrett Wollman  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21cfefd687SGarrett Wollman  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22cfefd687SGarrett Wollman  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23cfefd687SGarrett Wollman  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24cfefd687SGarrett Wollman  * SUCH DAMAGE.
25cfefd687SGarrett Wollman  */
26cfefd687SGarrett Wollman 
27677b542eSDavid E. O'Brien #include <sys/cdefs.h>
28677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$");
29677b542eSDavid E. O'Brien 
30f540b106SGarrett Wollman #include <sys/param.h>
316916a1daSMaxim Sobolev #include <sys/vnode.h>
326916a1daSMaxim Sobolev #include <sys/proc.h>
33aa855a59SPeter Wemm #include <sys/systm.h>
34ad7507e2SSteven Wallace #include <sys/sysproto.h>
3526f9a767SRodney W. Grimes #include <sys/exec.h>
36f540b106SGarrett Wollman #include <sys/imgact.h>
37f540b106SGarrett Wollman #include <sys/kernel.h>
38cfefd687SGarrett Wollman 
3992d91f76SGarrett Wollman #if BYTE_ORDER == LITTLE_ENDIAN
40cfefd687SGarrett Wollman #define SHELLMAGIC	0x2123 /* #! */
4192d91f76SGarrett Wollman #else
4292d91f76SGarrett Wollman #define SHELLMAGIC	0x2321
4392d91f76SGarrett Wollman #endif
4492d91f76SGarrett Wollman 
45cfefd687SGarrett Wollman /*
469d5abbddSJens Schweikhardt  * Shell interpreter image activator. An interpreter name beginning
47610ecfe0SMaxim Sobolev  *	at imgp->args->begin_argv is the minimal successful exit requirement.
48cfefd687SGarrett Wollman  */
49d323ddf3SMatthew Dillon int
50c52007c2SDavid Greenman exec_shell_imgact(imgp)
51c52007c2SDavid Greenman 	struct image_params *imgp;
52cfefd687SGarrett Wollman {
53c52007c2SDavid Greenman 	const char *image_header = imgp->image_header;
54610ecfe0SMaxim Sobolev 	const char *ihp;
55ec217396SMaxim Sobolev 	int error, offset;
566916a1daSMaxim Sobolev 	size_t length, clength;
576916a1daSMaxim Sobolev 	struct vattr vattr;
58cfefd687SGarrett Wollman 
59cfefd687SGarrett Wollman 	/* a shell script? */
60e0c95ed9SBruce Evans 	if (((const short *) image_header)[0] != SHELLMAGIC)
61cfefd687SGarrett Wollman 		return(-1);
62cfefd687SGarrett Wollman 
63cfefd687SGarrett Wollman 	/*
64cfefd687SGarrett Wollman 	 * Don't allow a shell script to be the shell for a shell
65cfefd687SGarrett Wollman 	 *	script. :-)
66cfefd687SGarrett Wollman 	 */
67c52007c2SDavid Greenman 	if (imgp->interpreted)
68cfefd687SGarrett Wollman 		return(ENOEXEC);
69cfefd687SGarrett Wollman 
70c52007c2SDavid Greenman 	imgp->interpreted = 1;
71cfefd687SGarrett Wollman 
72cfefd687SGarrett Wollman 	/*
736916a1daSMaxim Sobolev 	 * At this point we have the first page of the file mapped.
746916a1daSMaxim Sobolev 	 * However, we don't know how far into the page the contents are
756916a1daSMaxim Sobolev 	 * valid -- the actual file might be much shorter than the page.
766916a1daSMaxim Sobolev 	 * So find out the file size.
776916a1daSMaxim Sobolev  	 */
786916a1daSMaxim Sobolev 	error = VOP_GETATTR(imgp->vp, &vattr, imgp->proc->p_ucred, curthread);
796916a1daSMaxim Sobolev 	if (error)
806916a1daSMaxim Sobolev 		return (error);
816916a1daSMaxim Sobolev 
826916a1daSMaxim Sobolev 	clength = (vattr.va_size > MAXSHELLCMDLEN) ?
836916a1daSMaxim Sobolev 	    MAXSHELLCMDLEN : vattr.va_size;
846916a1daSMaxim Sobolev 	/*
85610ecfe0SMaxim Sobolev 	 * Figure out the number of bytes that need to be reserved in the
86610ecfe0SMaxim Sobolev 	 * argument string to copy the contents of the interpreter's command
87610ecfe0SMaxim Sobolev 	 * line into the argument string.
88cfefd687SGarrett Wollman 	 */
89cfefd687SGarrett Wollman 	ihp = &image_header[2];
90610ecfe0SMaxim Sobolev 	offset = 0;
916916a1daSMaxim Sobolev 	while (ihp < &image_header[clength]) {
92610ecfe0SMaxim Sobolev 		/* Skip any whitespace */
93b4305f8dSMaxim Sobolev 		if ((*ihp == ' ') || (*ihp == '\t')) {
94610ecfe0SMaxim Sobolev 			ihp++;
95610ecfe0SMaxim Sobolev 			continue;
96610ecfe0SMaxim Sobolev 		}
97cfefd687SGarrett Wollman 
98610ecfe0SMaxim Sobolev 		/* End of line? */
99b4305f8dSMaxim Sobolev 		if ((*ihp == '\n') || (*ihp == '#') || (*ihp == '\0'))
100610ecfe0SMaxim Sobolev 			break;
101cfefd687SGarrett Wollman 
102610ecfe0SMaxim Sobolev 		/* Found a token */
1036916a1daSMaxim Sobolev 		do {
104610ecfe0SMaxim Sobolev 			offset++;
105610ecfe0SMaxim Sobolev 			ihp++;
1066916a1daSMaxim Sobolev 		} while ((*ihp != ' ') && (*ihp != '\t') && (*ihp != '\n') &&
1076916a1daSMaxim Sobolev 		    (*ihp != '#') && (*ihp != '\0') &&
1086916a1daSMaxim Sobolev 		    (ihp < &image_header[clength]));
109610ecfe0SMaxim Sobolev 		/* Include terminating nulls in the offset */
110610ecfe0SMaxim Sobolev 		offset++;
111610ecfe0SMaxim Sobolev 	}
112cfefd687SGarrett Wollman 
113610ecfe0SMaxim Sobolev 	/* If the script gives a null line as the interpreter, we bail */
114610ecfe0SMaxim Sobolev 	if (offset == 0)
115cfefd687SGarrett Wollman 		return (ENOEXEC);
116cfefd687SGarrett Wollman 
117610ecfe0SMaxim Sobolev 	/* Check that we aren't too big */
1186916a1daSMaxim Sobolev 	if (ihp == &image_header[MAXSHELLCMDLEN])
119610ecfe0SMaxim Sobolev 		return (ENAMETOOLONG);
120cfefd687SGarrett Wollman 
121cfefd687SGarrett Wollman 	/*
122610ecfe0SMaxim Sobolev 	 * The full path name of the original script file must be tagged
123610ecfe0SMaxim Sobolev 	 * onto the end, adjust the offset to deal with it.
124610ecfe0SMaxim Sobolev 	 *
125610ecfe0SMaxim Sobolev 	 * The original argv[0] is being replaced, set 'length' to the number
126610ecfe0SMaxim Sobolev 	 * of bytes being removed.  So 'offset' is the number of bytes being
127610ecfe0SMaxim Sobolev 	 * added and 'length' is the number of bytes being removed.
128cfefd687SGarrett Wollman 	 */
129610ecfe0SMaxim Sobolev 	offset += strlen(imgp->args->fname) + 1;	/* add fname */
130610ecfe0SMaxim Sobolev 	length = (imgp->args->argc == 0) ? 0 :
131610ecfe0SMaxim Sobolev 	    strlen(imgp->args->begin_argv) + 1;		/* bytes to delete */
132610ecfe0SMaxim Sobolev 
133610ecfe0SMaxim Sobolev 	if (offset - length > imgp->args->stringspace)
134610ecfe0SMaxim Sobolev 		return (E2BIG);
135610ecfe0SMaxim Sobolev 
136610ecfe0SMaxim Sobolev 	bcopy(imgp->args->begin_argv + length, imgp->args->begin_argv + offset,
137610ecfe0SMaxim Sobolev 	    imgp->args->endp - (imgp->args->begin_argv + length));
138610ecfe0SMaxim Sobolev 
139610ecfe0SMaxim Sobolev 	offset -= length;		/* calculate actual adjustment */
140610ecfe0SMaxim Sobolev 	imgp->args->begin_envv += offset;
141610ecfe0SMaxim Sobolev 	imgp->args->endp += offset;
142610ecfe0SMaxim Sobolev 	imgp->args->stringspace -= offset;
143610ecfe0SMaxim Sobolev 
144610ecfe0SMaxim Sobolev 	/*
145610ecfe0SMaxim Sobolev 	 * If there were no arguments then we've added one, otherwise
146610ecfe0SMaxim Sobolev 	 * decr argc remove old argv[0], incr argc for fname add, net 0
147610ecfe0SMaxim Sobolev 	 */
148610ecfe0SMaxim Sobolev 	if (imgp->args->argc == 0)
149610ecfe0SMaxim Sobolev 		imgp->args->argc = 1;
150610ecfe0SMaxim Sobolev 
151610ecfe0SMaxim Sobolev 	/*
152610ecfe0SMaxim Sobolev 	 * Loop through the interpreter name yet again, copying as
153610ecfe0SMaxim Sobolev 	 * we go.
154610ecfe0SMaxim Sobolev 	 */
155610ecfe0SMaxim Sobolev 	ihp = &image_header[2];
156610ecfe0SMaxim Sobolev 	offset = 0;
1576916a1daSMaxim Sobolev 	while (ihp < &image_header[clength]) {
158610ecfe0SMaxim Sobolev 		/* Skip whitespace */
159b4305f8dSMaxim Sobolev 		if ((*ihp == ' ') || (*ihp == '\t')) {
160610ecfe0SMaxim Sobolev 			ihp++;
161610ecfe0SMaxim Sobolev 			continue;
162cfefd687SGarrett Wollman 		}
163cfefd687SGarrett Wollman 
164610ecfe0SMaxim Sobolev 		/* End of line? */
165b4305f8dSMaxim Sobolev 		if ((*ihp == '\n') || (*ihp == '#') || (*ihp == '\0'))
166610ecfe0SMaxim Sobolev 			break;
167cfefd687SGarrett Wollman 
168610ecfe0SMaxim Sobolev 		/* Found a token, copy it */
1696916a1daSMaxim Sobolev 		do {
170610ecfe0SMaxim Sobolev 			imgp->args->begin_argv[offset++] = *ihp++;
1716916a1daSMaxim Sobolev 		} while ((*ihp != ' ') && (*ihp != '\t') && (*ihp != '\n') &&
1726916a1daSMaxim Sobolev 		    (*ihp != '#') && (*ihp != '\0') &&
1736916a1daSMaxim Sobolev 		    (ihp < &image_header[MAXSHELLCMDLEN]));
174610ecfe0SMaxim Sobolev 		imgp->args->begin_argv[offset++] = '\0';
175610ecfe0SMaxim Sobolev 		imgp->args->argc++;
176cfefd687SGarrett Wollman 	}
177cfefd687SGarrett Wollman 
178610ecfe0SMaxim Sobolev 	/*
179610ecfe0SMaxim Sobolev 	 * Finally, add the filename onto the end for the interpreter to
180610ecfe0SMaxim Sobolev 	 * use and copy the interpreter's name to imgp->interpreter_name
181610ecfe0SMaxim Sobolev 	 * for exec to use.
182610ecfe0SMaxim Sobolev 	 */
183610ecfe0SMaxim Sobolev 	error = copystr(imgp->args->fname, imgp->args->buf + offset,
184610ecfe0SMaxim Sobolev 	    imgp->args->stringspace, &length);
185cfefd687SGarrett Wollman 
186610ecfe0SMaxim Sobolev 	if (error == 0)
187610ecfe0SMaxim Sobolev 		error = copystr(imgp->args->begin_argv, imgp->interpreter_name,
188610ecfe0SMaxim Sobolev 		    MAXSHELLCMDLEN, &length);
189610ecfe0SMaxim Sobolev 
190610ecfe0SMaxim Sobolev 	return (error);
191cfefd687SGarrett Wollman }
19292d91f76SGarrett Wollman 
19392d91f76SGarrett Wollman /*
19492d91f76SGarrett Wollman  * Tell kern_execve.c about it, with a little help from the linker.
19592d91f76SGarrett Wollman  */
196820ca326SMatthew Dillon static struct execsw shell_execsw = { exec_shell_imgact, "#!" };
197aa855a59SPeter Wemm EXEC_SET(shell, shell_execsw);
198