xref: /freebsd/sys/kern/imgact_shell.c (revision 5f49915eb22c1fca384843198b5e174a6b325bcc)
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 
395f49915eSGarance A Drosehn #define	KEEP_OLDCODE	1
405f49915eSGarance A Drosehn #if BYTE_ORDER == LITTLE_ENDIAN		/* temp for OLD_CODE kludge */
415f49915eSGarance A Drosehn #define	DBG_MAGIC	0x2B23		/* #+ in "little-endian" */
425f49915eSGarance A Drosehn #define	OLD_MAGIC	0x3C23		/* #< */
435f49915eSGarance A Drosehn #else
445f49915eSGarance A Drosehn #define	DBG_MAGIC	0x232B		/* #+ in big-endian */
455f49915eSGarance A Drosehn #define	OLD_MAGIC	0x233C		/* #< */
465f49915eSGarance A Drosehn #endif
475f49915eSGarance A Drosehn 
4892d91f76SGarrett Wollman #if BYTE_ORDER == LITTLE_ENDIAN
49cfefd687SGarrett Wollman #define SHELLMAGIC	0x2123 /* #! */
5092d91f76SGarrett Wollman #else
5192d91f76SGarrett Wollman #define SHELLMAGIC	0x2321
5292d91f76SGarrett Wollman #endif
5392d91f76SGarrett Wollman 
54cfefd687SGarrett Wollman /*
555f49915eSGarance A Drosehn  * At the time of this writing, MAXSHELLCMDLEN == PAGE_SIZE.  This is
565f49915eSGarance A Drosehn  * significant because the caller has only mapped in one page of the
575f49915eSGarance A Drosehn  * file we're reading.  This code should be changed to know how to
585f49915eSGarance A Drosehn  * read in the second page, but I'm not doing that just yet...
595f49915eSGarance A Drosehn  */
605f49915eSGarance A Drosehn #if MAXSHELLCMDLEN > PAGE_SIZE
615f49915eSGarance A Drosehn #error "MAXSHELLCMDLEN is larger than a single page!"
625f49915eSGarance A Drosehn #endif
635f49915eSGarance A Drosehn 
645f49915eSGarance A Drosehn /**
655f49915eSGarance A Drosehn  * Shell interpreter image activator. An interpreter name beginning at
665f49915eSGarance A Drosehn  * imgp->args->begin_argv is the minimal successful exit requirement.
675f49915eSGarance A Drosehn  *
685f49915eSGarance A Drosehn  * If the given file is a shell-script, then the first line will start
695f49915eSGarance A Drosehn  * with the two characters `#!' (aka SHELLMAGIC), followed by the name
705f49915eSGarance A Drosehn  * of the shell-interpreter to run, followed by zero or more tokens.
715f49915eSGarance A Drosehn  *
725f49915eSGarance A Drosehn  * The interpreter is then started up such that it will see:
735f49915eSGarance A Drosehn  *    arg[0] -> The name of interpreter as specified after `#!' in the
745f49915eSGarance A Drosehn  *		first line of the script.  The interpreter name must
755f49915eSGarance A Drosehn  *		not be longer than MAXSHELLCMDLEN bytes.
765f49915eSGarance A Drosehn  *    arg[1] -> *If* there are any additional tokens on the first line,
775f49915eSGarance A Drosehn  *		then we add a new arg[1], which is a copy of the rest of
785f49915eSGarance A Drosehn  *		that line.  The copy starts at the first token after the
795f49915eSGarance A Drosehn  *		interpreter name.  We leave it to the interpreter to
805f49915eSGarance A Drosehn  *		parse the tokens in that value.
815f49915eSGarance A Drosehn  *    arg[x] -> the full pathname of the script.  This will either be
825f49915eSGarance A Drosehn  *		arg[2] or arg[1], depending on whether or not tokens
835f49915eSGarance A Drosehn  *		were found after the interpreter name.
845f49915eSGarance A Drosehn  *  arg[x+1] -> all the arguments that were specified on the original
855f49915eSGarance A Drosehn  *		command line.
865f49915eSGarance A Drosehn  *
875f49915eSGarance A Drosehn  * This processing is described in the execve(2) man page.
885f49915eSGarance A Drosehn  */
895f49915eSGarance A Drosehn 
905f49915eSGarance A Drosehn /*
915f49915eSGarance A Drosehn  * HISTORICAL NOTE: From 1993 to mid-2005, FreeBSD parsed out the tokens as
925f49915eSGarance A Drosehn  * found on the first line of the script, and setup each token as a separate
935f49915eSGarance A Drosehn  * value in arg[].  This extra processing did not match the behavior of other
945f49915eSGarance A Drosehn  * OS's, and caused a few subtle problems.  For one, it meant the kernel was
955f49915eSGarance A Drosehn  * deciding how those values should be parsed (wrt characters for quoting or
965f49915eSGarance A Drosehn  * comments, etc), while the interpreter might have other rules for parsing.
975f49915eSGarance A Drosehn  * It also meant the interpreter had no way of knowing which arguments came
985f49915eSGarance A Drosehn  * from the first line of the shell script, and which arguments were specified
995f49915eSGarance A Drosehn  * by the user on the command line.
1005f49915eSGarance A Drosehn  *
1015f49915eSGarance A Drosehn  * Only few things in the base system might depend on that non-standard
1025f49915eSGarance A Drosehn  * processing (mainly /bin/sh and /usr/bin/env).  And for programs which are
1035f49915eSGarance A Drosehn  * not in the base system, the "newer" behavior matches how NetBSD, OpenBSD,
1045f49915eSGarance A Drosehn  * Linux, Solaris, AIX, IRIX, and many other Unixes have set up the arg-list
1055f49915eSGarance A Drosehn  * for the interpreter.  So if a program can handle this behavior on those
1065f49915eSGarance A Drosehn  * other OS's, it should be able to handle it for FreeBSD too.
107cfefd687SGarrett Wollman  */
108d323ddf3SMatthew Dillon int
109c52007c2SDavid Greenman exec_shell_imgact(imgp)
110c52007c2SDavid Greenman 	struct image_params *imgp;
111cfefd687SGarrett Wollman {
112c52007c2SDavid Greenman 	const char *image_header = imgp->image_header;
1135f49915eSGarance A Drosehn 	const char *ihp, *interpb, *interpe, *maxp, *optb, *opte;
114ec217396SMaxim Sobolev 	int error, offset;
1156916a1daSMaxim Sobolev 	size_t length, clength;
1166916a1daSMaxim Sobolev 	struct vattr vattr;
117cfefd687SGarrett Wollman 
118cfefd687SGarrett Wollman 	/* a shell script? */
119e0c95ed9SBruce Evans 	if (((const short *) image_header)[0] != SHELLMAGIC)
120cfefd687SGarrett Wollman 		return(-1);
121cfefd687SGarrett Wollman 
122cfefd687SGarrett Wollman 	/*
123cfefd687SGarrett Wollman 	 * Don't allow a shell script to be the shell for a shell
124cfefd687SGarrett Wollman 	 *	script. :-)
125cfefd687SGarrett Wollman 	 */
126c52007c2SDavid Greenman 	if (imgp->interpreted)
127cfefd687SGarrett Wollman 		return(ENOEXEC);
128cfefd687SGarrett Wollman 
129c52007c2SDavid Greenman 	imgp->interpreted = 1;
130cfefd687SGarrett Wollman 
131cfefd687SGarrett Wollman 	/*
1326916a1daSMaxim Sobolev 	 * At this point we have the first page of the file mapped.
1336916a1daSMaxim Sobolev 	 * However, we don't know how far into the page the contents are
1346916a1daSMaxim Sobolev 	 * valid -- the actual file might be much shorter than the page.
1356916a1daSMaxim Sobolev 	 * So find out the file size.
1366916a1daSMaxim Sobolev  	 */
1376916a1daSMaxim Sobolev 	error = VOP_GETATTR(imgp->vp, &vattr, imgp->proc->p_ucred, curthread);
1386916a1daSMaxim Sobolev 	if (error)
1396916a1daSMaxim Sobolev 		return (error);
1406916a1daSMaxim Sobolev 
1415f49915eSGarance A Drosehn 	/*
1425f49915eSGarance A Drosehn 	 * Copy shell name and arguments from image_header into a string
1435f49915eSGarance A Drosehn 	 *	buffer.  Remember that the caller has mapped only the
1445f49915eSGarance A Drosehn 	 *	first page of the file into memory.
1455f49915eSGarance A Drosehn 	 */
1465f49915eSGarance A Drosehn 	clength = (vattr.va_size > PAGE_SIZE) ? PAGE_SIZE : vattr.va_size;
1475f49915eSGarance A Drosehn 
1485f49915eSGarance A Drosehn 	maxp = &image_header[clength];
1495f49915eSGarance A Drosehn 	ihp = &image_header[2];
1505f49915eSGarance A Drosehn #if KEEP_OLDCODE
1515f49915eSGarance A Drosehn 	/*
1525f49915eSGarance A Drosehn 	 * XXX - Temporarily provide a quick-and-dirty way to get the
1535f49915eSGarance A Drosehn 	 * older, non-standard option-parsing behavior, just in case
1545f49915eSGarance A Drosehn 	 * someone finds themselves in an emergency where they need it.
1555f49915eSGarance A Drosehn 	 * This will not be documented.  It is only for initial testing.
1565f49915eSGarance A Drosehn 	 */
1575f49915eSGarance A Drosehn 	if (*(const short *)ihp == OLD_MAGIC)
1585f49915eSGarance A Drosehn 		ihp += 2;
1595f49915eSGarance A Drosehn 	else
1605f49915eSGarance A Drosehn 		goto new_code;
1615f49915eSGarance A Drosehn 	interpb = ihp;
1625f49915eSGarance A Drosehn 
1636916a1daSMaxim Sobolev 	/*
164610ecfe0SMaxim Sobolev 	 * Figure out the number of bytes that need to be reserved in the
165610ecfe0SMaxim Sobolev 	 * argument string to copy the contents of the interpreter's command
166610ecfe0SMaxim Sobolev 	 * line into the argument string.
167cfefd687SGarrett Wollman 	 */
1685f49915eSGarance A Drosehn 	ihp = interpb;
169610ecfe0SMaxim Sobolev 	offset = 0;
1706916a1daSMaxim Sobolev 	while (ihp < &image_header[clength]) {
171610ecfe0SMaxim Sobolev 		/* Skip any whitespace */
172b4305f8dSMaxim Sobolev 		if ((*ihp == ' ') || (*ihp == '\t')) {
173610ecfe0SMaxim Sobolev 			ihp++;
174610ecfe0SMaxim Sobolev 			continue;
175610ecfe0SMaxim Sobolev 		}
176cfefd687SGarrett Wollman 
177610ecfe0SMaxim Sobolev 		/* End of line? */
178b4305f8dSMaxim Sobolev 		if ((*ihp == '\n') || (*ihp == '#') || (*ihp == '\0'))
179610ecfe0SMaxim Sobolev 			break;
180cfefd687SGarrett Wollman 
181610ecfe0SMaxim Sobolev 		/* Found a token */
1826916a1daSMaxim Sobolev 		do {
183610ecfe0SMaxim Sobolev 			offset++;
184610ecfe0SMaxim Sobolev 			ihp++;
1856916a1daSMaxim Sobolev 		} while ((*ihp != ' ') && (*ihp != '\t') && (*ihp != '\n') &&
1866916a1daSMaxim Sobolev 		    (*ihp != '#') && (*ihp != '\0') &&
1876916a1daSMaxim Sobolev 		    (ihp < &image_header[clength]));
188610ecfe0SMaxim Sobolev 		/* Include terminating nulls in the offset */
189610ecfe0SMaxim Sobolev 		offset++;
190610ecfe0SMaxim Sobolev 	}
191cfefd687SGarrett Wollman 
192610ecfe0SMaxim Sobolev 	/* If the script gives a null line as the interpreter, we bail */
193610ecfe0SMaxim Sobolev 	if (offset == 0)
194cfefd687SGarrett Wollman 		return (ENOEXEC);
195cfefd687SGarrett Wollman 
196610ecfe0SMaxim Sobolev 	/* Check that we aren't too big */
1976916a1daSMaxim Sobolev 	if (ihp == &image_header[MAXSHELLCMDLEN])
198610ecfe0SMaxim Sobolev 		return (ENAMETOOLONG);
199cfefd687SGarrett Wollman 
200cfefd687SGarrett Wollman 	/*
201610ecfe0SMaxim Sobolev 	 * The full path name of the original script file must be tagged
202610ecfe0SMaxim Sobolev 	 * onto the end, adjust the offset to deal with it.
203610ecfe0SMaxim Sobolev 	 *
204610ecfe0SMaxim Sobolev 	 * The original argv[0] is being replaced, set 'length' to the number
205610ecfe0SMaxim Sobolev 	 * of bytes being removed.  So 'offset' is the number of bytes being
206610ecfe0SMaxim Sobolev 	 * added and 'length' is the number of bytes being removed.
207cfefd687SGarrett Wollman 	 */
208610ecfe0SMaxim Sobolev 	offset += strlen(imgp->args->fname) + 1;	/* add fname */
209610ecfe0SMaxim Sobolev 	length = (imgp->args->argc == 0) ? 0 :
210610ecfe0SMaxim Sobolev 	    strlen(imgp->args->begin_argv) + 1;		/* bytes to delete */
211610ecfe0SMaxim Sobolev 
212610ecfe0SMaxim Sobolev 	if (offset - length > imgp->args->stringspace)
213610ecfe0SMaxim Sobolev 		return (E2BIG);
214610ecfe0SMaxim Sobolev 
215610ecfe0SMaxim Sobolev 	bcopy(imgp->args->begin_argv + length, imgp->args->begin_argv + offset,
216610ecfe0SMaxim Sobolev 	    imgp->args->endp - (imgp->args->begin_argv + length));
217610ecfe0SMaxim Sobolev 
218610ecfe0SMaxim Sobolev 	offset -= length;		/* calculate actual adjustment */
219610ecfe0SMaxim Sobolev 	imgp->args->begin_envv += offset;
220610ecfe0SMaxim Sobolev 	imgp->args->endp += offset;
221610ecfe0SMaxim Sobolev 	imgp->args->stringspace -= offset;
222610ecfe0SMaxim Sobolev 
223610ecfe0SMaxim Sobolev 	/*
224610ecfe0SMaxim Sobolev 	 * If there were no arguments then we've added one, otherwise
225610ecfe0SMaxim Sobolev 	 * decr argc remove old argv[0], incr argc for fname add, net 0
226610ecfe0SMaxim Sobolev 	 */
227610ecfe0SMaxim Sobolev 	if (imgp->args->argc == 0)
228610ecfe0SMaxim Sobolev 		imgp->args->argc = 1;
229610ecfe0SMaxim Sobolev 
230610ecfe0SMaxim Sobolev 	/*
231610ecfe0SMaxim Sobolev 	 * Loop through the interpreter name yet again, copying as
232610ecfe0SMaxim Sobolev 	 * we go.
233610ecfe0SMaxim Sobolev 	 */
2345f49915eSGarance A Drosehn 	ihp = interpb;
235610ecfe0SMaxim Sobolev 	offset = 0;
2366916a1daSMaxim Sobolev 	while (ihp < &image_header[clength]) {
237610ecfe0SMaxim Sobolev 		/* Skip whitespace */
238b4305f8dSMaxim Sobolev 		if ((*ihp == ' ') || (*ihp == '\t')) {
239610ecfe0SMaxim Sobolev 			ihp++;
240610ecfe0SMaxim Sobolev 			continue;
241cfefd687SGarrett Wollman 		}
242cfefd687SGarrett Wollman 
243610ecfe0SMaxim Sobolev 		/* End of line? */
244b4305f8dSMaxim Sobolev 		if ((*ihp == '\n') || (*ihp == '#') || (*ihp == '\0'))
245610ecfe0SMaxim Sobolev 			break;
246cfefd687SGarrett Wollman 
247610ecfe0SMaxim Sobolev 		/* Found a token, copy it */
2486916a1daSMaxim Sobolev 		do {
249610ecfe0SMaxim Sobolev 			imgp->args->begin_argv[offset++] = *ihp++;
2506916a1daSMaxim Sobolev 		} while ((*ihp != ' ') && (*ihp != '\t') && (*ihp != '\n') &&
2516916a1daSMaxim Sobolev 		    (*ihp != '#') && (*ihp != '\0') &&
2526916a1daSMaxim Sobolev 		    (ihp < &image_header[MAXSHELLCMDLEN]));
253610ecfe0SMaxim Sobolev 		imgp->args->begin_argv[offset++] = '\0';
254610ecfe0SMaxim Sobolev 		imgp->args->argc++;
255cfefd687SGarrett Wollman 	}
2565f49915eSGarance A Drosehn 	goto common_end;
2575f49915eSGarance A Drosehn new_code:
2585f49915eSGarance A Drosehn #endif
2595f49915eSGarance A Drosehn 	/*
2605f49915eSGarance A Drosehn 	 * Find the beginning and end of the interpreter_name.  If the
2615f49915eSGarance A Drosehn 	 * line does not include any interpreter, or if the name which
2625f49915eSGarance A Drosehn 	 * was found is too long, we bail out.
2635f49915eSGarance A Drosehn 	 */
2645f49915eSGarance A Drosehn 	while (ihp < maxp && ((*ihp == ' ') || (*ihp == '\t')))
2655f49915eSGarance A Drosehn 		ihp++;
2665f49915eSGarance A Drosehn 	interpb = ihp;
2675f49915eSGarance A Drosehn 	while (ihp < maxp && ((*ihp != ' ') && (*ihp != '\t') && (*ihp != '\n')
2685f49915eSGarance A Drosehn 	    && (*ihp != '\0')))
2695f49915eSGarance A Drosehn 		ihp++;
2705f49915eSGarance A Drosehn 	interpe = ihp;
2715f49915eSGarance A Drosehn 	if (interpb == interpe)
2725f49915eSGarance A Drosehn 		return (ENOEXEC);
2735f49915eSGarance A Drosehn 	if ((interpe - interpb) >= MAXSHELLCMDLEN)
2745f49915eSGarance A Drosehn 		return (ENAMETOOLONG);
275cfefd687SGarrett Wollman 
276610ecfe0SMaxim Sobolev 	/*
2775f49915eSGarance A Drosehn 	 * Find the beginning of the options (if any), and the end-of-line.
2785f49915eSGarance A Drosehn 	 * Then trim the trailing blanks off the value.  Note that some
2795f49915eSGarance A Drosehn 	 * other operating systems do *not* trim the trailing whitespace...
2805f49915eSGarance A Drosehn 	 */
2815f49915eSGarance A Drosehn 	while (ihp < maxp && ((*ihp == ' ') || (*ihp == '\t')))
2825f49915eSGarance A Drosehn 		ihp++;
2835f49915eSGarance A Drosehn 	optb = ihp;
2845f49915eSGarance A Drosehn 	while (ihp < maxp && ((*ihp != '\n') && (*ihp != '\0')))
2855f49915eSGarance A Drosehn 		ihp++;
2865f49915eSGarance A Drosehn 	opte = ihp;
2875f49915eSGarance A Drosehn 	while (--ihp > interpe && ((*ihp == ' ') || (*ihp == '\t')))
2885f49915eSGarance A Drosehn 		opte = ihp;
2895f49915eSGarance A Drosehn 
2905f49915eSGarance A Drosehn 	/*
2915f49915eSGarance A Drosehn 	 * We need to "pop" (remove) the present value of arg[0], and "push"
2925f49915eSGarance A Drosehn 	 * either two or three new values in the arg[] list.  To do this,
2935f49915eSGarance A Drosehn 	 * we first shift all the other values in the `begin_argv' area to
2945f49915eSGarance A Drosehn 	 * provide the exact amount of room for the values added.  Set up
2955f49915eSGarance A Drosehn 	 * `offset' as the number of bytes to be added to the `begin_argv'
2965f49915eSGarance A Drosehn 	 * area, and 'length' as the number of bytes being removed.
2975f49915eSGarance A Drosehn 	 */
2985f49915eSGarance A Drosehn 	offset = interpe - interpb + 1;			/* interpreter */
2995f49915eSGarance A Drosehn 	if (opte != optb)				/* options (if any) */
3005f49915eSGarance A Drosehn 		offset += opte - optb + 1;
3015f49915eSGarance A Drosehn 	offset += strlen(imgp->args->fname) + 1;	/* fname of script */
3025f49915eSGarance A Drosehn 	length = (imgp->args->argc == 0) ? 0 :
3035f49915eSGarance A Drosehn 	    strlen(imgp->args->begin_argv) + 1;		/* bytes to delete */
3045f49915eSGarance A Drosehn 
3055f49915eSGarance A Drosehn 	if (offset - length > imgp->args->stringspace)
3065f49915eSGarance A Drosehn 		return (E2BIG);
3075f49915eSGarance A Drosehn 
3085f49915eSGarance A Drosehn 	bcopy(imgp->args->begin_argv + length, imgp->args->begin_argv + offset,
3095f49915eSGarance A Drosehn 	    imgp->args->endp - (imgp->args->begin_argv + length));
3105f49915eSGarance A Drosehn 
3115f49915eSGarance A Drosehn 	offset -= length;		/* calculate actual adjustment */
3125f49915eSGarance A Drosehn 	imgp->args->begin_envv += offset;
3135f49915eSGarance A Drosehn 	imgp->args->endp += offset;
3145f49915eSGarance A Drosehn 	imgp->args->stringspace -= offset;
3155f49915eSGarance A Drosehn 
3165f49915eSGarance A Drosehn 	/*
3175f49915eSGarance A Drosehn 	 * If there was no arg[0] when we started, then the interpreter_name
3185f49915eSGarance A Drosehn 	 * is adding an argument (instead of replacing the arg[0] we started
3195f49915eSGarance A Drosehn 	 * with).  And we're always adding an argument when we include the
3205f49915eSGarance A Drosehn 	 * full pathname of the original script.
3215f49915eSGarance A Drosehn 	 */
3225f49915eSGarance A Drosehn 	if (imgp->args->argc == 0)
3235f49915eSGarance A Drosehn 		imgp->args->argc = 1;
3245f49915eSGarance A Drosehn 	imgp->args->argc++;
3255f49915eSGarance A Drosehn 
3265f49915eSGarance A Drosehn 	/*
3275f49915eSGarance A Drosehn 	 * The original arg[] list has been shifted appropriately.  Copy in
3285f49915eSGarance A Drosehn 	 * the interpreter name and options-string.
3295f49915eSGarance A Drosehn 	 */
3305f49915eSGarance A Drosehn 	length = interpe - interpb;
3315f49915eSGarance A Drosehn 	bcopy(interpb, imgp->args->buf, length);
3325f49915eSGarance A Drosehn 	*(imgp->args->buf + length) = '\0';
3335f49915eSGarance A Drosehn 	offset = length + 1;
3345f49915eSGarance A Drosehn 	if (opte != optb) {
3355f49915eSGarance A Drosehn 		length = opte - optb;
3365f49915eSGarance A Drosehn 		bcopy(optb, imgp->args->buf + offset, length);
3375f49915eSGarance A Drosehn 		*(imgp->args->buf + offset + length) = '\0';
3385f49915eSGarance A Drosehn 		offset += length + 1;
3395f49915eSGarance A Drosehn 		imgp->args->argc++;
3405f49915eSGarance A Drosehn 	}
3415f49915eSGarance A Drosehn 
3425f49915eSGarance A Drosehn #if KEEP_OLDCODE
3435f49915eSGarance A Drosehn common_end:
3445f49915eSGarance A Drosehn #endif
3455f49915eSGarance A Drosehn 	/*
346610ecfe0SMaxim Sobolev 	 * Finally, add the filename onto the end for the interpreter to
347610ecfe0SMaxim Sobolev 	 * use and copy the interpreter's name to imgp->interpreter_name
348610ecfe0SMaxim Sobolev 	 * for exec to use.
349610ecfe0SMaxim Sobolev 	 */
350610ecfe0SMaxim Sobolev 	error = copystr(imgp->args->fname, imgp->args->buf + offset,
351610ecfe0SMaxim Sobolev 	    imgp->args->stringspace, &length);
352cfefd687SGarrett Wollman 
353610ecfe0SMaxim Sobolev 	if (error == 0)
354610ecfe0SMaxim Sobolev 		error = copystr(imgp->args->begin_argv, imgp->interpreter_name,
355610ecfe0SMaxim Sobolev 		    MAXSHELLCMDLEN, &length);
356610ecfe0SMaxim Sobolev 
357610ecfe0SMaxim Sobolev 	return (error);
358cfefd687SGarrett Wollman }
35992d91f76SGarrett Wollman 
36092d91f76SGarrett Wollman /*
36192d91f76SGarrett Wollman  * Tell kern_execve.c about it, with a little help from the linker.
36292d91f76SGarrett Wollman  */
363820ca326SMatthew Dillon static struct execsw shell_execsw = { exec_shell_imgact, "#!" };
364aa855a59SPeter Wemm EXEC_SET(shell, shell_execsw);
365