Deleted Added
full compact
1/*
2 * Copyright (c) 1993, David Greenman
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $Id: kern_exec.c,v 1.85 1998/08/24 08:39:38 dfr Exp $
26 * $Id: kern_exec.c,v 1.86 1998/09/04 08:06:55 dfr Exp $
27 */
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/sysproto.h>
32#include <sys/signalvar.h>
33#include <sys/kernel.h>
34#include <sys/mount.h>
35#include <sys/filedesc.h>
36#include <sys/fcntl.h>
37#include <sys/acct.h>
38#include <sys/exec.h>
39#include <sys/imgact.h>
40#include <sys/imgact_elf.h>
41#include <sys/wait.h>
42#include <sys/proc.h>
43#include <sys/pioctl.h>
44#include <sys/malloc.h>
45#include <sys/namei.h>
46#include <sys/sysent.h>
47#include <sys/shm.h>
48#include <sys/sysctl.h>
49#include <sys/vnode.h>
50#include <sys/buf.h>
51
52#include <vm/vm.h>
53#include <vm/vm_param.h>
54#include <vm/vm_prot.h>
55#include <sys/lock.h>
56#include <vm/pmap.h>
57#include <vm/vm_page.h>
58#include <vm/vm_map.h>
59#include <vm/vm_kern.h>
60#include <vm/vm_extern.h>
61#include <vm/vm_object.h>
62#include <vm/vm_zone.h>
63#include <vm/vm_pager.h>
64
65#include <machine/reg.h>
66
67static long *exec_copyout_strings __P((struct image_params *));
68
69static struct ps_strings *ps_strings = PS_STRINGS;
70SYSCTL_INTPTR(_kern, KERN_PS_STRINGS, ps_strings, 0, &ps_strings, 0, "");
71
72static caddr_t usrstack = (caddr_t)USRSTACK;
73SYSCTL_INTPTR(_kern, KERN_USRSTACK, usrstack, 0, &usrstack, 0, "");
74/*
74 * execsw_set is constructed for us by the linker. Each of the items
75 * is a pointer to a `const struct execsw', hence the double pointer here.
75 * Each of the items is a pointer to a `const struct execsw', hence the
76 * double pointer here.
77 */
77static const struct execsw **execsw =
78 (const struct execsw **)&execsw_set.ls_items[0];
78static const struct execsw **execsw;
79
80#ifndef _SYS_SYSPROTO_H_
81struct execve_args {
82 char *fname;
83 char **argv;
84 char **envv;
85};
86#endif
87
88/*
89 * execve() system call.
90 */
91int
92execve(p, uap)
93 struct proc *p;
94 register struct execve_args *uap;
95{
96 struct nameidata nd, *ndp;
97 long *stack_base;
98 int error, len, i;
99 struct image_params image_params, *imgp;
100 struct vattr attr;
101
102 imgp = &image_params;
103
104 /*
105 * Initialize part of the common data
106 */
107 imgp->proc = p;
108 imgp->uap = uap;
109 imgp->attr = &attr;
110 imgp->argc = imgp->envc = 0;
111 imgp->argv0 = NULL;
112 imgp->entry_addr = 0;
113 imgp->vmspace_destroyed = 0;
114 imgp->interpreted = 0;
115 imgp->interpreter_name[0] = '\0';
116 imgp->auxargs = NULL;
117 imgp->vp = NULL;
118 imgp->firstpage = NULL;
119
120 /*
121 * Allocate temporary demand zeroed space for argument and
122 * environment strings
123 */
124 imgp->stringbase = (char *)kmem_alloc_wait(exec_map, ARG_MAX + PAGE_SIZE);
125 if (imgp->stringbase == NULL) {
126 error = ENOMEM;
127 goto exec_fail;
128 }
129 imgp->stringp = imgp->stringbase;
130 imgp->stringspace = ARG_MAX;
131 imgp->image_header = imgp->stringbase + ARG_MAX;
132
133 /*
134 * Translate the file name. namei() returns a vnode pointer
135 * in ni_vp amoung other things.
136 */
137 ndp = &nd;
138 NDINIT(ndp, LOOKUP, LOCKLEAF | FOLLOW | SAVENAME,
139 UIO_USERSPACE, uap->fname, p);
140
141interpret:
142
143 error = namei(ndp);
144 if (error) {
145 kmem_free_wakeup(exec_map, (vm_offset_t)imgp->stringbase,
146 ARG_MAX + PAGE_SIZE);
147 goto exec_fail;
148 }
149
150 imgp->vp = ndp->ni_vp;
151
152 /*
153 * Check file permissions (also 'opens' file)
154 */
155 error = exec_check_permissions(imgp);
156 if (error) {
157 VOP_UNLOCK(imgp->vp, 0, p);
158 goto exec_fail_dealloc;
159 }
160
161 error = exec_map_first_page(imgp);
162 VOP_UNLOCK(imgp->vp, 0, p);
163 if (error)
164 goto exec_fail_dealloc;
165
166 /*
167 * Loop through list of image activators, calling each one.
168 * If there is no match, the activator returns -1. If there
169 * is a match, but there was an error during the activation,
170 * the error is returned. Otherwise 0 means success. If the
171 * image is interpreted, loop back up and try activating
172 * the interpreter.
173 */
174 for (i = 0; execsw[i]; ++i) {
175 if (execsw[i]->ex_imgact)
176 error = (*execsw[i]->ex_imgact)(imgp);
177 else
178 continue;
179 if (error == -1)
180 continue;
181 if (error)
182 goto exec_fail_dealloc;
183 if (imgp->interpreted) {
184 exec_unmap_first_page(imgp);
185 /* free old vnode and name buffer */
186 vrele(ndp->ni_vp);
187 zfree(namei_zone, ndp->ni_cnd.cn_pnbuf);
188 /* set new name to that of the interpreter */
189 NDINIT(ndp, LOOKUP, LOCKLEAF | FOLLOW | SAVENAME,
190 UIO_SYSSPACE, imgp->interpreter_name, p);
191 goto interpret;
192 }
193 break;
194 }
195 /* If we made it through all the activators and none matched, exit. */
196 if (error == -1) {
197 error = ENOEXEC;
198 goto exec_fail_dealloc;
199 }
200
201 /*
202 * Copy out strings (args and env) and initialize stack base
203 */
204 stack_base = exec_copyout_strings(imgp);
205 p->p_vmspace->vm_minsaddr = (char *)stack_base;
206
207 /*
208 * If custom stack fixup routine present for this process
209 * let it do the stack setup.
210 * Else stuff argument count as first item on stack
211 */
212 if (p->p_sysent->sv_fixup)
213 (*p->p_sysent->sv_fixup)(&stack_base, imgp);
214 else
215 suword(--stack_base, imgp->argc);
216
217 /*
218 * For security and other reasons, the file descriptor table cannot
219 * be shared after an exec.
220 */
221 if (p->p_fd->fd_refcnt > 1) {
222 struct filedesc *tmp;
223
224 tmp = fdcopy(p);
225 fdfree(p);
226 p->p_fd = tmp;
227 }
228
229 /* close files on exec */
230 fdcloseexec(p);
231
232 /* reset caught signals */
233 execsigs(p);
234
235 /* name this process - nameiexec(p, ndp) */
236 len = min(ndp->ni_cnd.cn_namelen,MAXCOMLEN);
237 bcopy(ndp->ni_cnd.cn_nameptr, p->p_comm, len);
238 p->p_comm[len] = 0;
239
240 /*
241 * mark as execed, wakeup the process that vforked (if any) and tell
242 * it that it now has its own resources back
243 */
244 p->p_flag |= P_EXEC;
245 if (p->p_pptr && (p->p_flag & P_PPWAIT)) {
246 p->p_flag &= ~P_PPWAIT;
247 wakeup((caddr_t)p->p_pptr);
248 }
249
250 /*
251 * Implement image setuid/setgid.
252 *
253 * Don't honor setuid/setgid if the filesystem prohibits it or if
254 * the process is being traced.
255 */
256 if ((attr.va_mode & VSUID && p->p_ucred->cr_uid != attr.va_uid ||
257 attr.va_mode & VSGID && p->p_ucred->cr_gid != attr.va_gid) &&
258 (imgp->vp->v_mount->mnt_flag & MNT_NOSUID) == 0 &&
259 (p->p_flag & P_TRACED) == 0) {
260 /*
261 * Turn off syscall tracing for set-id programs, except for
262 * root.
263 */
264 if (p->p_tracep && suser(p->p_ucred, &p->p_acflag)) {
265 p->p_traceflag = 0;
266 vrele(p->p_tracep);
267 p->p_tracep = NULL;
268 }
269 /*
270 * Set the new credentials.
271 */
272 p->p_ucred = crcopy(p->p_ucred);
273 if (attr.va_mode & VSUID)
274 p->p_ucred->cr_uid = attr.va_uid;
275 if (attr.va_mode & VSGID)
276 p->p_ucred->cr_gid = attr.va_gid;
277 setsugid(p);
278 } else {
279 if (p->p_ucred->cr_uid == p->p_cred->p_ruid &&
280 p->p_ucred->cr_gid == p->p_cred->p_rgid)
281 p->p_flag &= ~P_SUGID;
282 }
283
284 /*
285 * Implement correct POSIX saved-id behavior.
286 */
287 p->p_cred->p_svuid = p->p_ucred->cr_uid;
288 p->p_cred->p_svgid = p->p_ucred->cr_gid;
289
290 /*
291 * Store the vp for use in procfs
292 */
293 if (p->p_textvp) /* release old reference */
294 vrele(p->p_textvp);
295 VREF(ndp->ni_vp);
296 p->p_textvp = ndp->ni_vp;
297
298 /*
299 * If tracing the process, trap to debugger so breakpoints
300 * can be set before the program executes.
301 */
302 STOPEVENT(p, S_EXEC, 0);
303
304 if (p->p_flag & P_TRACED)
305 psignal(p, SIGTRAP);
306
307 /* clear "fork but no exec" flag, as we _are_ execing */
308 p->p_acflag &= ~AFORK;
309
310 /* Set entry address */
311 setregs(p, imgp->entry_addr, (u_long)(uintptr_t)stack_base);
312
313exec_fail_dealloc:
314
315 /*
316 * free various allocated resources
317 */
318 if (imgp->firstpage)
319 exec_unmap_first_page(imgp);
320
321 if (imgp->stringbase != NULL)
322 kmem_free_wakeup(exec_map, (vm_offset_t)imgp->stringbase,
323 ARG_MAX + PAGE_SIZE);
324
325 if (ndp->ni_vp) {
326 vrele(ndp->ni_vp);
327 zfree(namei_zone, ndp->ni_cnd.cn_pnbuf);
328 }
329
330 if (error == 0)
331 return (0);
332
333exec_fail:
334 if (imgp->vmspace_destroyed) {
335 /* sorry, no more process anymore. exit gracefully */
336 exit1(p, W_EXITCODE(0, SIGABRT));
337 /* NOT REACHED */
338 return(0);
339 } else {
340 return(error);
341 }
342}
343
344int
345exec_map_first_page(imgp)
346 struct image_params *imgp;
347{
348 int s, rv, i;
349 int initial_pagein;
350 vm_page_t ma[VM_INITIAL_PAGEIN];
351 vm_object_t object;
352
353
354 if (imgp->firstpage) {
355 exec_unmap_first_page(imgp);
356 }
357
358 object = imgp->vp->v_object;
359 s = splvm();
360
361 ma[0] = vm_page_grab(object, 0, VM_ALLOC_NORMAL | VM_ALLOC_RETRY);
362
363 if ((ma[0]->valid & VM_PAGE_BITS_ALL) != VM_PAGE_BITS_ALL) {
364 initial_pagein = VM_INITIAL_PAGEIN;
365 if (initial_pagein > object->size)
366 initial_pagein = object->size;
367 for (i = 1; i < initial_pagein; i++) {
368 if (ma[i] = vm_page_lookup(object, i)) {
369 if ((ma[i]->flags & PG_BUSY) || ma[i]->busy)
370 break;
371 if (ma[i]->valid)
372 break;
373 vm_page_busy(ma[i]);
374 } else {
375 ma[i] = vm_page_alloc(object, i, VM_ALLOC_NORMAL);
376 if (ma[i] == NULL)
377 break;
378 }
379 }
380 initial_pagein = i;
381
382 rv = vm_pager_get_pages(object, ma, initial_pagein, 0);
383 ma[0] = vm_page_lookup(object, 0);
384
385 if ((rv != VM_PAGER_OK) || (ma[0] == NULL) || (ma[0]->valid == 0)) {
386 if (ma[0]) {
387 vm_page_protect(ma[0], VM_PROT_NONE);
388 vm_page_free(ma[0]);
389 }
390 splx(s);
391 return EIO;
392 }
393 }
394
395 vm_page_wire(ma[0]);
396 vm_page_wakeup(ma[0]);
397 splx(s);
398
399 pmap_kenter((vm_offset_t) imgp->image_header, VM_PAGE_TO_PHYS(ma[0]));
400 imgp->firstpage = ma[0];
401
402 return 0;
403}
404
405void
406exec_unmap_first_page(imgp)
407 struct image_params *imgp;
408{
409 if (imgp->firstpage) {
410 pmap_kremove((vm_offset_t) imgp->image_header);
411 vm_page_unwire(imgp->firstpage);
412 imgp->firstpage = NULL;
413 }
414}
415
416/*
417 * Destroy old address space, and allocate a new stack
418 * The new stack is only SGROWSIZ large because it is grown
419 * automatically in trap.c.
420 */
421int
422exec_new_vmspace(imgp)
423 struct image_params *imgp;
424{
425 int error;
426 struct vmspace *vmspace = imgp->proc->p_vmspace;
427 caddr_t stack_addr = (caddr_t) (USRSTACK - SGROWSIZ);
428 vm_map_t map = &vmspace->vm_map;
429
430 imgp->vmspace_destroyed = 1;
431
432 /*
433 * Blow away entire process VM, if address space not shared,
434 * otherwise, create a new VM space so that other threads are
435 * not disrupted
436 */
437 if (vmspace->vm_refcnt == 1) {
438 if (vmspace->vm_shm)
439 shmexit(imgp->proc);
440 pmap_remove_pages(&vmspace->vm_pmap, 0, USRSTACK);
441 vm_map_remove(map, 0, USRSTACK);
442 } else {
443 vmspace_exec(imgp->proc);
444 vmspace = imgp->proc->p_vmspace;
445 map = &vmspace->vm_map;
446 }
447
448 /* Allocate a new stack */
449 error = vm_map_insert(&vmspace->vm_map, NULL, 0,
450 (vm_offset_t) stack_addr, (vm_offset_t) USRSTACK,
451 VM_PROT_ALL, VM_PROT_ALL, 0);
452 if (error)
453 return (error);
454
455 vmspace->vm_ssize = SGROWSIZ >> PAGE_SHIFT;
456
457 /* Initialize maximum stack address */
458 vmspace->vm_maxsaddr = (char *)USRSTACK - MAXSSIZ;
459
460 return(0);
461}
462
463/*
464 * Copy out argument and environment strings from the old process
465 * address space into the temporary string buffer.
466 */
467int
468exec_extract_strings(imgp)
469 struct image_params *imgp;
470{
471 char **argv, **envv;
472 char *argp, *envp;
473 int error;
474 size_t length;
475
476 /*
477 * extract arguments first
478 */
479
480 argv = imgp->uap->argv;
481
482 if (argv) {
483 argp = (caddr_t) (intptr_t) fuword(argv);
484 if (argp == (caddr_t) -1)
485 return (EFAULT);
486 if (argp)
487 argv++;
488 if (imgp->argv0)
489 argp = imgp->argv0;
490 if (argp) {
491 do {
492 if (argp == (caddr_t) -1)
493 return (EFAULT);
494 if ((error = copyinstr(argp, imgp->stringp,
495 imgp->stringspace, &length))) {
496 if (error == ENAMETOOLONG)
497 return(E2BIG);
498 return (error);
499 }
500 imgp->stringspace -= length;
501 imgp->stringp += length;
502 imgp->argc++;
503 } while ((argp = (caddr_t) (intptr_t) fuword(argv++)));
504 }
505 }
506
507 /*
508 * extract environment strings
509 */
510
511 envv = imgp->uap->envv;
512
513 if (envv) {
514 while ((envp = (caddr_t) (intptr_t) fuword(envv++))) {
515 if (envp == (caddr_t) -1)
516 return (EFAULT);
517 if ((error = copyinstr(envp, imgp->stringp,
518 imgp->stringspace, &length))) {
519 if (error == ENAMETOOLONG)
520 return(E2BIG);
521 return (error);
522 }
523 imgp->stringspace -= length;
524 imgp->stringp += length;
525 imgp->envc++;
526 }
527 }
528
529 return (0);
530}
531
532/*
533 * Copy strings out to the new process address space, constructing
534 * new arg and env vector tables. Return a pointer to the base
535 * so that it can be used as the initial stack pointer.
536 */
537long *
538exec_copyout_strings(imgp)
539 struct image_params *imgp;
540{
541 int argc, envc;
542 char **vectp;
543 char *stringp, *destp;
544 long *stack_base;
545 struct ps_strings *arginfo;
546 int szsigcode;
547
548 /*
549 * Calculate string base and vector table pointers.
550 * Also deal with signal trampoline code for this exec type.
551 */
552 arginfo = PS_STRINGS;
553 szsigcode = *(imgp->proc->p_sysent->sv_szsigcode);
554 destp = (caddr_t)arginfo - szsigcode - SPARE_USRSPACE -
555 roundup((ARG_MAX - imgp->stringspace), sizeof(char *));
556
557 /*
558 * install sigcode
559 */
560 if (szsigcode)
561 copyout(imgp->proc->p_sysent->sv_sigcode,
562 ((caddr_t)arginfo - szsigcode), szsigcode);
563
564 /*
565 * If we have a valid auxargs ptr, prepare some room
566 * on the stack.
567 */
568 if (imgp->auxargs)
569 /*
570 * The '+ 2' is for the null pointers at the end of each of the
571 * arg and env vector sets, and 'AT_COUNT*2' is room for the
572 * ELF Auxargs data.
573 */
574 vectp = (char **)(destp - (imgp->argc + imgp->envc + 2 +
575 AT_COUNT*2) * sizeof(char*));
576 else
577 /*
578 * The '+ 2' is for the null pointers at the end of each of the
579 * arg and env vector sets
580 */
581 vectp = (char **)
582 (destp - (imgp->argc + imgp->envc + 2) * sizeof(char*));
583
584 /*
585 * vectp also becomes our initial stack base
586 */
587 stack_base = (long *)vectp;
588
589 stringp = imgp->stringbase;
590 argc = imgp->argc;
591 envc = imgp->envc;
592
593 /*
594 * Copy out strings - arguments and environment.
595 */
596 copyout(stringp, destp, ARG_MAX - imgp->stringspace);
597
598 /*
599 * Fill in "ps_strings" struct for ps, w, etc.
600 */
601 suword(&arginfo->ps_argvstr, (long)(intptr_t)vectp);
602 suword(&arginfo->ps_nargvstr, argc);
603
604 /*
605 * Fill in argument portion of vector table.
606 */
607 for (; argc > 0; --argc) {
608 suword(vectp++, (long)(intptr_t)destp);
609 while (*stringp++ != 0)
610 destp++;
611 destp++;
612 }
613
614 /* a null vector table pointer seperates the argp's from the envp's */
615 suword(vectp++, 0);
616
617 suword(&arginfo->ps_envstr, (long)(intptr_t)vectp);
618 suword(&arginfo->ps_nenvstr, envc);
619
620 /*
621 * Fill in environment portion of vector table.
622 */
623 for (; envc > 0; --envc) {
624 suword(vectp++, (long)(intptr_t)destp);
625 while (*stringp++ != 0)
626 destp++;
627 destp++;
628 }
629
630 /* end of vector table is a null pointer */
631 suword(vectp, 0);
632
633 return (stack_base);
634}
635
636/*
637 * Check permissions of file to execute.
638 * Return 0 for success or error code on failure.
639 */
640int
641exec_check_permissions(imgp)
642 struct image_params *imgp;
643{
644 struct proc *p = imgp->proc;
645 struct vnode *vp = imgp->vp;
646 struct vattr *attr = imgp->attr;
647 int error;
648
649 /* Get file attributes */
650 error = VOP_GETATTR(vp, attr, p->p_ucred, p);
651 if (error)
652 return (error);
653
654 /*
655 * 1) Check if file execution is disabled for the filesystem that this
656 * file resides on.
657 * 2) Insure that at least one execute bit is on - otherwise root
658 * will always succeed, and we don't want to happen unless the
659 * file really is executable.
660 * 3) Insure that the file is a regular file.
661 */
662 if ((vp->v_mount->mnt_flag & MNT_NOEXEC) ||
663 ((attr->va_mode & 0111) == 0) ||
664 (attr->va_type != VREG)) {
665 return (EACCES);
666 }
667
668 /*
669 * Zero length files can't be exec'd
670 */
671 if (attr->va_size == 0)
672 return (ENOEXEC);
673
674 /*
675 * Check for execute permission to file based on current credentials.
676 */
677 error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p);
678 if (error)
679 return (error);
680
681 /*
682 * Check number of open-for-writes on the file and deny execution
683 * if there are any.
684 */
685 if (vp->v_writecount)
686 return (ETXTBSY);
687
688 /*
689 * Call filesystem specific open routine (which does nothing in the
690 * general case).
691 */
692 error = VOP_OPEN(vp, FREAD, p->p_ucred, p);
693 if (error)
694 return (error);
695
696 return (0);
697}
698
699/*
700 * Exec handler registration
701 */
702int
703exec_register(execsw_arg)
704 const struct execsw *execsw_arg;
705{
706 const struct execsw **es, **xs, **newexecsw;
707 int count = 2; /* New slot and trailing NULL */
708
709 if (execsw)
710 for (es = execsw; *es; es++)
711 count++;
712 newexecsw = malloc(count * sizeof(*es), M_TEMP, M_WAITOK);
713 if (newexecsw == NULL)
714 return ENOMEM;
715 xs = newexecsw;
716 if (execsw)
717 for (es = execsw; *es; es++)
718 *xs++ = *es;
719 *xs++ = execsw_arg;
720 *xs = NULL;
721 if (execsw)
722 free(execsw, M_TEMP);
723 execsw = newexecsw;
724 return 0;
725}
726
727int
728exec_unregister(execsw_arg)
729 const struct execsw *execsw_arg;
730{
731 const struct execsw **es, **xs, **newexecsw;
732 int count = 1;
733
734 if (execsw == NULL)
735 panic("unregister with no handlers left?\n");
736
737 for (es = execsw; *es; es++) {
738 if (*es == execsw_arg)
739 break;
740 }
741 if (*es == NULL)
742 return ENOENT;
743 for (es = execsw; *es; es++)
744 if (*es != execsw_arg)
745 count++;
746 newexecsw = malloc(count * sizeof(*es), M_TEMP, M_WAITOK);
747 if (newexecsw == NULL)
748 return ENOMEM;
749 xs = newexecsw;
750 for (es = execsw; *es; es++)
751 if (*es != execsw_arg)
752 *xs++ = *es;
753 *xs = NULL;
754 if (execsw)
755 free(execsw, M_TEMP);
756 execsw = newexecsw;
757 return 0;
758}