Bring back --with-sys-screenrc configure flag.
[screen-lua.git] / src / fileio.c
blob88fbf641b3fdf44fea5a416394f6ddb451fd2399
1 /* Copyright (c) 2008, 2009
2 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
4 * Micah Cowan (micah@cowan.name)
5 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
6 * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
7 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
8 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
9 * Copyright (c) 1987 Oliver Laumann
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3, or (at your option)
14 * any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program (see the file COPYING); if not, see
23 * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
26 ****************************************************************
29 #include <sys/types.h>
30 #include <fcntl.h>
31 #include <sys/stat.h>
32 #include <pwd.h>
34 #ifndef SIGINT
35 # include <signal.h>
36 #endif
38 #include "config.h"
39 #include "screen.h"
40 #include "extern.h"
42 extern struct display *display, *displays;
43 extern struct win *fore;
44 extern struct layer *flayer;
45 extern int real_uid, eff_uid;
46 extern int real_gid, eff_gid;
47 extern char *extra_incap, *extra_outcap;
48 extern char *home, *RcFileName;
49 extern char SockPath[], *SockName;
50 #ifdef COPY_PASTE
51 extern char *BufferFile;
52 #endif
53 extern int hardcopy_append;
54 extern char *hardcopydir;
56 static char *CatExtra __P((char *, char *));
57 static char *findrcfile __P((char *));
60 char *rc_name = "";
61 int rc_recursion = 0;
63 static char *
64 CatExtra(str1, str2)
65 register char *str1, *str2;
67 register char *cp;
68 register int len1, len2, add_colon;
70 len1 = strlen(str1);
71 if (len1 == 0)
72 return str2;
73 add_colon = (str1[len1 - 1] != ':');
74 if (str2)
76 len2 = strlen(str2);
77 if ((cp = realloc(str2, (unsigned) len1 + len2 + add_colon + 1)) == NULL)
78 Panic(0, "%s", strnomem);
79 bcopy(cp, cp + len1 + add_colon, len2 + 1);
81 else
83 if (len1 == 0)
84 return 0;
85 if ((cp = malloc((unsigned) len1 + add_colon + 1)) == NULL)
86 Panic(0, "%s", strnomem);
87 cp[len1 + add_colon] = '\0';
89 bcopy(str1, cp, len1);
90 if (add_colon)
91 cp[len1] = ':';
93 return cp;
96 static char *
97 findrcfile(rcfile)
98 char *rcfile;
100 char buf[256];
101 char *p;
103 /* Tilde prefix support courtesy <hesso@pool.math.tu-berlin.de>,
104 * taken from a Debian patch. */
105 if (rcfile && *rcfile == '~')
107 static char rcfilename_tilde_exp[MAXPATHLEN+1];
108 char *slash_position = strchr(rcfile, '/');
109 if (slash_position == rcfile+1)
111 char *home = getenv("HOME");
112 if (!home)
114 Msg(0, "%s: source: tilde expansion failed", rc_name);
115 return NULL;
117 snprintf(rcfilename_tilde_exp, MAXPATHLEN, "%s/%s", home, rcfile+2);
119 else if (slash_position)
121 struct passwd *p;
122 *slash_position = 0;
123 p = getpwnam(rcfile+1);
124 if (!p)
126 Msg(0, "%s: source: tilde expansion failed for user %s", rc_name, rcfile+1);
127 return NULL;
129 snprintf(rcfilename_tilde_exp, MAXPATHLEN, "%s/%s", p->pw_dir, slash_position+1);
131 else
133 Msg(0, "%s: source: illegal tilde expression.", rc_name);
134 return NULL;
136 rcfile = rcfilename_tilde_exp;
139 if (rcfile)
141 char *rcend = rindex(rc_name, '/');
142 if (*rcfile != '/' && rcend && (rcend - rc_name) + strlen(rcfile) + 2 < sizeof(buf))
144 strncpy(buf, rc_name, rcend - rc_name + 1);
145 strcpy(buf + (rcend - rc_name) + 1, rcfile);
146 if (access(buf, R_OK) == 0)
147 return SaveStr(buf);
149 debug1("findrcfile: you specified '%s'\n", rcfile);
150 return SaveStr(rcfile);
152 debug("findrcfile: you specified nothing...\n");
153 if ((p = getenv("SCREENRC")) != NULL && *p != '\0')
155 debug1(" $SCREENRC has: '%s'\n", p);
156 return SaveStr(p);
158 else
160 debug(" ...nothing in $SCREENRC, defaulting $HOME/.screenrc\n");
161 if (strlen(home) > sizeof(buf) - 12)
162 Panic(0, "Rc: home too large");
163 sprintf(buf, "%s/.screenrc", home);
164 return SaveStr(buf);
169 * this will be called twice:
170 * 1) rcfilename = "/etc/screenrc"
171 * 2) rcfilename = RcFileName
174 StartRc(rcfilename, nopanic)
175 char *rcfilename;
176 int nopanic;
178 register int argc, len;
179 register char *p, *cp;
180 char buf[2048];
181 char *args[MAXARGS];
182 int argl[MAXARGS];
183 FILE *fp;
184 char *oldrc_name = rc_name;
186 /* always fix termcap/info capabilities */
187 extra_incap = CatExtra("TF", extra_incap);
189 /* Special settings for vt100 and others */
190 if (display && (!strncmp(D_termname, "vt", 2) || !strncmp(D_termname, "xterm", 5)))
191 extra_incap = CatExtra("xn:f0=\033Op:f1=\033Oq:f2=\033Or:f3=\033Os:f4=\033Ot:f5=\033Ou:f6=\033Ov:f7=\033Ow:f8=\033Ox:f9=\033Oy:f.=\033On:f,=\033Ol:fe=\033OM:f+=\033Ok:f-=\033Om:f*=\033Oj:f/=\033Oo:fq=\033OX", extra_incap);
193 rc_name = findrcfile(rcfilename);
195 if (rc_name == NULL || (fp = secfopen(rc_name, "r")) == NULL)
197 const char *rc_nonnull = rc_name ? rc_name : rcfilename;
198 if (!rc_recursion && RcFileName && !strcmp(RcFileName, rc_nonnull))
201 * User explicitly gave us that name,
202 * this is the only case, where we get angry, if we can't read
203 * the file.
205 debug3("StartRc: '%s','%s', '%s'\n", RcFileName, rc_name ? rc_name : "(null)", rcfilename);
206 if (!nopanic) Panic(0, "Unable to open \"%s\".", rc_nonnull);
207 /* possibly NOTREACHED */
209 debug1("StartRc: '%s' no good. ignored\n", rc_nonnull);
210 if (rc_name)
211 Free(rc_name);
212 rc_name = oldrc_name;
213 return 1;
215 while (fgets(buf, sizeof buf, fp) != NULL)
217 if ((p = rindex(buf, '\n')) != NULL)
218 *p = '\0';
219 if ((argc = Parse(buf, sizeof buf, args, argl)) == 0)
220 continue;
221 if (strcmp(args[0], "echo") == 0)
223 if (!display)
224 continue;
225 if (argc < 2 || (argc == 3 && strcmp(args[1], "-n")) || argc > 3)
227 Msg(0, "%s: 'echo [-n] \"string\"' expected.", rc_name);
228 continue;
230 AddStr(args[argc - 1]);
231 if (argc != 3)
233 AddStr("\r\n");
234 Flush(0);
237 else if (strcmp(args[0], "sleep") == 0)
239 if (!display)
240 continue;
241 debug("sleeeeeeep\n");
242 if (argc != 2)
244 Msg(0, "%s: sleep: one numeric argument expected.", rc_name);
245 continue;
247 DisplaySleep1000(1000 * atoi(args[1]), 1);
249 #ifdef TERMINFO
250 else if (!strcmp(args[0], "termcapinfo") || !strcmp(args[0], "terminfo"))
251 #else
252 else if (!strcmp(args[0], "termcapinfo") || !strcmp(args[0], "termcap"))
253 #endif
255 if (!display)
256 continue;
257 if (argc < 3 || argc > 4)
259 Msg(0, "%s: %s: incorrect number of arguments.", rc_name, args[0]);
260 continue;
262 for (p = args[1]; p && *p; p = cp)
264 if ((cp = index(p, '|')) != 0)
265 *cp++ = '\0';
266 len = strlen(p);
267 if (p[len - 1] == '*')
269 if (!(len - 1) || !strncmp(p, D_termname, len - 1))
270 break;
272 else if (!strcmp(p, D_termname))
273 break;
275 if (!(p && *p))
276 continue;
277 extra_incap = CatExtra(args[2], extra_incap);
278 if (argc == 4)
279 extra_outcap = CatExtra(args[3], extra_outcap);
281 else if (!strcmp(args[0], "source"))
283 if (rc_recursion <= 10)
285 rc_recursion++;
286 (void)StartRc(args[1], 0);
287 rc_recursion--;
291 fclose(fp);
292 Free(rc_name);
293 rc_name = oldrc_name;
294 return 0;
297 void
298 FinishRc(rcfilename)
299 char *rcfilename;
301 char buf[2048];
302 FILE *fp;
303 char *oldrc_name = rc_name;
305 rc_name = findrcfile(rcfilename);
307 if (rc_name == NULL || (fp = secfopen(rc_name, "r")) == NULL)
309 const char *rc_nonnull = rc_name ? rc_name : rcfilename;
310 if (rc_recursion)
311 Msg(errno, "%s: source %s", oldrc_name, rc_nonnull);
312 else if (RcFileName && !strcmp(RcFileName, rc_nonnull))
315 * User explicitly gave us that name,
316 * this is the only case, where we get angry, if we can't read
317 * the file.
319 debug3("FinishRc:'%s','%s','%s'\n", RcFileName, rc_name ? rc_name : "(null)", rcfilename);
320 Panic(0, "Unable to open \"%s\".", rc_nonnull);
321 /* NOTREACHED */
323 debug1("FinishRc: '%s' no good. ignored\n", rc_nonnull);
324 if (rc_name)
325 Free(rc_name);
326 rc_name = oldrc_name;
327 return;
330 debug("finishrc is going...\n");
331 while (fgets(buf, sizeof buf, fp) != NULL)
332 RcLine(buf, sizeof buf);
333 (void) fclose(fp);
334 Free(rc_name);
335 rc_name = oldrc_name;
338 void
339 do_source(rcfilename)
340 char *rcfilename;
342 if (rc_recursion > 10)
344 Msg(0, "%s: source: recursion limit reached", rc_name);
345 return;
347 rc_recursion++;
348 FinishRc(rcfilename);
349 rc_recursion--;
354 * Running a Command Line in the environment determined by the display.
355 * The fore window is taken from the display as well as the user.
356 * This is bad when we run detached.
358 void
359 RcLine(ubuf, ubufl)
360 char *ubuf;
361 int ubufl;
363 char *args[MAXARGS];
364 int argl[MAXARGS];
365 #ifdef MULTIUSER
366 extern struct acluser *EffectiveAclUser; /* acl.c */
367 extern struct acluser *users; /* acl.c */
368 #endif
370 if (display)
372 fore = D_fore;
373 flayer = D_forecv->c_layer;
375 else
376 flayer = fore ? fore->w_savelayer : 0;
377 if (Parse(ubuf, ubufl, args, argl) <= 0)
378 return;
379 #ifdef MULTIUSER
380 if (!display)
382 /* the session owner does it, when there is no display here */
383 EffectiveAclUser = users;
384 debug("RcLine: WARNING, no display no user! Session owner executes command\n");
386 #endif
387 DoCommand(args, argl);
388 #ifdef MULTIUSER
389 EffectiveAclUser = 0;
390 #endif
394 * needs display for copybuffer access and termcap dumping
396 void
397 WriteFile(user, fn, dump)
398 struct acluser *user;
399 char *fn;
400 int dump;
402 /* dump==0: create .termcap,
403 * dump==1: hardcopy,
404 * #ifdef COPY_PASTE
405 * dump==2: BUFFERFILE
406 * #endif COPY_PASTE
407 * dump==1: scrollback,
409 register int i, j, k;
410 register char *p;
411 register FILE *f;
412 char fnbuf[1024];
413 char *mode = "w";
414 #ifdef COPY_PASTE
415 int public = 0;
416 # ifdef _MODE_T
417 mode_t old_umask;
418 # else
419 int old_umask;
420 # endif
421 # ifdef HAVE_LSTAT
422 struct stat stb, stb2;
423 int fd, exists = 0;
424 # endif
425 #endif
427 switch (dump)
429 case DUMP_TERMCAP:
430 if (fn == 0)
432 i = SockName - SockPath;
433 if (i > (int)sizeof(fnbuf) - 9)
434 i = 0;
435 strncpy(fnbuf, SockPath, i);
436 strcpy(fnbuf + i, ".termcap");
437 fn = fnbuf;
439 break;
440 case DUMP_HARDCOPY:
441 case DUMP_SCROLLBACK:
442 if (fn == 0)
444 if (fore == 0)
445 return;
446 if (hardcopydir && *hardcopydir && strlen(hardcopydir) < sizeof(fnbuf) - 21)
447 sprintf(fnbuf, "%s/hardcopy.%d", hardcopydir, fore->w_number);
448 else
449 sprintf(fnbuf, "hardcopy.%d", fore->w_number);
450 fn = fnbuf;
452 if (hardcopy_append && !access(fn, W_OK))
453 mode = "a";
454 break;
455 #ifdef COPY_PASTE
456 case DUMP_EXCHANGE:
457 if (fn == 0)
459 strncpy(fnbuf, BufferFile, sizeof(fnbuf) - 1);
460 fnbuf[sizeof(fnbuf) - 1] = 0;
461 fn = fnbuf;
463 public = !strcmp(fn, DEFAULT_BUFFERFILE);
464 # ifdef HAVE_LSTAT
465 exists = !lstat(fn, &stb);
466 if (public && exists && (S_ISLNK(stb.st_mode) || stb.st_nlink > 1))
468 Msg(0, "No write to links, please.");
469 return;
471 # endif
472 break;
473 #endif
476 debug2("WriteFile(%d) %s\n", dump, fn);
477 if (UserContext() > 0)
479 debug("Writefile: usercontext\n");
480 #ifdef COPY_PASTE
481 if (dump == DUMP_EXCHANGE && public)
483 old_umask = umask(0);
484 # ifdef HAVE_LSTAT
485 if (exists)
487 if ((fd = open(fn, O_WRONLY, 0666)) >= 0)
489 if (fstat(fd, &stb2) == 0 && stb.st_dev == stb2.st_dev && stb.st_ino == stb2.st_ino)
490 ftruncate(fd, 0);
491 else
493 close(fd);
494 fd = -1;
498 else
499 fd = open(fn, O_WRONLY|O_CREAT|O_EXCL, 0666);
500 f = fd >= 0 ? fdopen(fd, mode) : 0;
501 # else
502 f = fopen(fn, mode);
503 # endif
504 umask(old_umask);
506 else
507 #endif /* COPY_PASTE */
508 f = fopen(fn, mode);
509 if (f == NULL)
511 debug2("WriteFile: fopen(%s,\"%s\") failed\n", fn, mode);
512 UserReturn(0);
514 else
516 switch (dump)
518 case DUMP_HARDCOPY:
519 case DUMP_SCROLLBACK:
520 if (!fore)
521 break;
522 if (*mode == 'a')
524 putc('>', f);
525 for (j = fore->w_width - 2; j > 0; j--)
526 putc('=', f);
527 fputs("<\n", f);
529 if (dump == DUMP_SCROLLBACK)
531 #ifdef COPY_PASTE
532 for (i = 0; i < fore->w_histheight; i++)
534 p = (char *)(WIN(i)->image);
535 for (k = fore->w_width - 1; k >= 0 && p[k] == ' '; k--)
537 for (j = 0; j <= k; j++)
538 putc(p[j], f);
539 putc('\n', f);
541 #endif
543 for (i = 0; i < fore->w_height; i++)
545 p = (char *)fore->w_mlines[i].image;
546 for (k = fore->w_width - 1; k >= 0 && p[k] == ' '; k--)
548 for (j = 0; j <= k; j++)
549 putc(p[j], f);
550 putc('\n', f);
552 break;
553 case DUMP_TERMCAP:
554 if ((p = index(MakeTermcap(fore->w_aflag), '=')) != NULL)
556 fputs(++p, f);
557 putc('\n', f);
559 break;
560 #ifdef COPY_PASTE
561 case DUMP_EXCHANGE:
562 p = user->u_plop.buf;
563 for (i = user->u_plop.len; i-- > 0; p++)
564 if (*p == '\r' && (i == 0 || p[1] != '\n'))
565 putc('\n', f);
566 else
567 putc(*p, f);
568 break;
569 #endif
571 (void) fclose(f);
572 UserReturn(1);
575 if (UserStatus() <= 0)
576 Msg(0, "Cannot open \"%s\"", fn);
577 else if (display && !*rc_name)
579 switch (dump)
581 case DUMP_TERMCAP:
582 Msg(0, "Termcap entry written to \"%s\".", fn);
583 break;
584 case DUMP_HARDCOPY:
585 case DUMP_SCROLLBACK:
586 Msg(0, "Screen image %s to \"%s\".",
587 (*mode == 'a') ? "appended" : "written", fn);
588 break;
589 #ifdef COPY_PASTE
590 case DUMP_EXCHANGE:
591 Msg(0, "Copybuffer written to \"%s\".", fn);
592 #endif
597 #ifdef COPY_PASTE
600 * returns an allocated buffer which holds a copy of the file named fn.
601 * lenp (if nonzero) points to a location, where the buffer size should be
602 * stored.
604 char *
605 ReadFile(fn, lenp)
606 char *fn;
607 int *lenp;
609 int i, l, size;
610 char c, *bp, *buf;
611 struct stat stb;
613 ASSERT(lenp);
614 debug1("ReadFile(%s)\n", fn);
615 if ((i = secopen(fn, O_RDONLY, 0)) < 0)
617 Msg(errno, "no %s -- no slurp", fn);
618 return NULL;
620 if (fstat(i, &stb))
622 Msg(errno, "no good %s -- no slurp", fn);
623 close(i);
624 return NULL;
626 size = stb.st_size;
627 if ((buf = malloc(size)) == NULL)
629 close(i);
630 Msg(0, "%s", strnomem);
631 return NULL;
633 errno = 0;
634 if ((l = read(i, buf, size)) != size)
636 if (l < 0)
637 l = 0;
638 Msg(errno, "Got only %d bytes from %s", l, fn);
639 close(i);
641 else
643 if (read(i, &c, 1) > 0)
644 Msg(0, "Slurped only %d characters (of %d) into buffer - try again",
645 l, size);
646 else
647 Msg(0, "Slurped %d characters into buffer", l);
649 close(i);
650 *lenp = l;
651 for (bp = buf; l-- > 0; bp++)
652 if (*bp == '\n' && (bp == buf || bp[-1] != '\r'))
653 *bp = '\r';
654 return buf;
657 void
658 KillBuffers()
660 if (UserContext() > 0)
661 UserReturn(unlink(BufferFile) ? errno : 0);
662 errno = UserStatus();
663 Msg(errno, "%s %sremoved", BufferFile, errno ? "not " : "");
665 #endif /* COPY_PASTE */
669 * (Almost) secure open and fopen...
672 FILE *
673 secfopen(name, mode)
674 char *name;
675 char *mode;
677 FILE *fi;
678 #ifndef USE_SETEUID
679 int flags, fd;
680 #endif
682 debug2("secfopen(%s, %s)\n", name, mode);
683 #ifdef USE_SETEUID
684 xseteuid(real_uid);
685 xsetegid(real_gid);
686 fi = fopen(name, mode);
687 xseteuid(eff_uid);
688 xsetegid(eff_gid);
689 return fi;
690 #else
691 if (eff_uid == real_uid)
692 return fopen(name, mode);
693 if (mode[0] && mode[1] == '+')
694 flags = O_RDWR;
695 else
696 flags = (mode[0] == 'r') ? O_RDONLY : O_WRONLY;
697 if (mode[0] == 'w')
698 flags |= O_CREAT | O_TRUNC;
699 else if (mode[0] == 'a')
700 flags |= O_CREAT | O_APPEND;
701 else if (mode[0] != 'r')
703 errno = EINVAL;
704 return 0;
706 if ((fd = secopen(name, flags, 0666)) < 0)
707 return 0;
708 if ((fi = fdopen(fd, mode)) == 0)
710 close(fd);
711 return 0;
713 return fi;
714 #endif
719 secopen(name, flags, mode)
720 char *name;
721 int flags;
722 int mode;
724 int fd;
725 #ifndef USE_SETEUID
726 int q;
727 struct stat stb;
728 #endif
730 debug3("secopen(%s, 0x%x, 0%03o)\n", name, flags, mode);
731 #ifdef USE_SETEUID
732 xseteuid(real_uid);
733 xsetegid(real_gid);
734 fd = open(name, flags, mode);
735 xseteuid(eff_uid);
736 xsetegid(eff_gid);
737 return fd;
738 #else
739 if (eff_uid == real_uid)
740 return open(name, flags, mode);
741 /* Truncation/creation is done in UserContext */
742 if ((flags & O_TRUNC) || ((flags & O_CREAT) && access(name, F_OK)))
744 if (UserContext() > 0)
746 if ((fd = open(name, flags, mode)) >= 0)
748 close(fd);
749 UserReturn(0);
751 if (errno == 0)
752 errno = EACCES;
753 UserReturn(errno);
755 if ((q = UserStatus()))
757 if (q > 0)
758 errno = q;
759 return -1;
762 if (access(name, F_OK))
763 return -1;
764 if ((fd = open(name, flags & ~(O_TRUNC | O_CREAT), 0)) < 0)
765 return -1;
766 debug("open successful\n");
767 if (fstat(fd, &stb))
769 close(fd);
770 return -1;
772 debug("fstat successful\n");
773 if (stb.st_uid != real_uid)
775 switch (flags & (O_RDONLY | O_WRONLY | O_RDWR))
777 case O_RDONLY:
778 q = 0004;
779 break;
780 case O_WRONLY:
781 q = 0002;
782 break;
783 default:
784 q = 0006;
785 break;
787 if ((stb.st_mode & q) != q)
789 debug1("secopen: permission denied (%03o)\n", stb.st_mode & 07777);
790 close(fd);
791 errno = EACCES;
792 return -1;
795 debug1("secopen ok - returning %d\n", fd);
796 return fd;
797 #endif
802 printpipe(p, cmd)
803 struct win *p;
804 char *cmd;
806 int pi[2];
807 if (pipe(pi))
809 WMsg(p, errno, "printing pipe");
810 return -1;
812 switch (fork())
814 case -1:
815 WMsg(p, errno, "printing fork");
816 return -1;
817 case 0:
818 display = p->w_pdisplay;
819 displays = 0;
820 #ifdef DEBUG
821 if (dfp && dfp != stderr)
822 fclose(dfp);
823 #endif
824 close(0);
825 dup(pi[0]);
826 closeallfiles(0);
827 if (setgid(real_gid) || setuid(real_uid))
828 Panic(errno, "printpipe setuid");
829 #ifdef SIGPIPE
830 signal(SIGPIPE, SIG_DFL);
831 #endif
832 execl("/bin/sh", "sh", "-c", cmd, (char *)0);
833 Panic(errno, "/bin/sh");
834 default:
835 break;
837 close(pi[0]);
838 return pi[1];
842 readpipe(cmdv)
843 char **cmdv;
845 int pi[2];
847 if (pipe(pi))
849 Msg(errno, "pipe");
850 return -1;
852 switch (fork())
854 case -1:
855 Msg(errno, "fork");
856 return -1;
857 case 0:
858 displays = 0;
859 #ifdef DEBUG
860 if (dfp && dfp != stderr)
861 fclose(dfp);
862 #endif
863 close(1);
864 if (dup(pi[1]) != 1)
866 close(pi[1]);
867 Panic(0, "dup");
869 closeallfiles(1);
870 if (setgid(real_gid) || setuid(real_uid))
872 close(1);
873 Panic(errno, "setuid/setgid");
875 #ifdef SIGPIPE
876 signal(SIGPIPE, SIG_DFL);
877 #endif
878 execvp(*cmdv, cmdv);
879 close(1);
880 Panic(errno, "%s", *cmdv);
881 default:
882 break;
884 close(pi[1]);
885 return pi[0];