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
10  *
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.
15  *
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.
20  *
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  * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
25  *
26  ****************************************************************
27  */
28 
29 #include <sys/types.h>
30 #include <fcntl.h>
31 #include <sys/stat.h>
32 #include <pwd.h>
33 
34 #ifndef SIGINT
35 # include <signal.h>
36 #endif
37 
38 #include "config.h"
39 #include "screen.h"
40 #include "extern.h"
41 
42 extern struct display *display, *displays;
43 extern struct win *fore;
44 extern struct layer *flayer;
45 extern int ServerSocket;
46 extern int real_uid, eff_uid;
47 extern int real_gid, eff_gid;
48 extern char *extra_incap, *extra_outcap;
49 extern char *home, *RcFileName;
50 extern char SockPath[], *SockName;
51 #ifdef COPY_PASTE
52 extern char *BufferFile;
53 #endif
54 extern int hardcopy_append;
55 extern char *hardcopydir;
56 
57 static char *CatExtra __P((char *, char *));
58 static char *findrcfile __P((char *));
59 
60 
61 char *rc_name = "";
62 int rc_recursion = 0;
63 
CatExtra(register char * str1,register char * str2)64 static char * CatExtra(register char *str1, register char *str2) {
65   register char *cp;
66   register int len1, len2, add_colon;
67 
68   len1 = strlen(str1);
69   if (len1 == 0)
70     return str2;
71   add_colon = (str1[len1 - 1] != ':');
72   if (str2) {
73     len2 = strlen(str2);
74     if ((cp = realloc(str2, (unsigned) len1 + len2 + add_colon + 1)) == NULL)
75       Panic(0, "%s", strnomem);
76     bcopy(cp, cp + len1 + add_colon, len2 + 1);
77   }
78   else {
79     if ((cp = malloc((unsigned) len1 + add_colon + 1)) == NULL)
80       Panic(0, "%s", strnomem);
81     cp[len1 + add_colon] = '\0';
82   }
83   bcopy(str1, cp, len1);
84   if (add_colon)
85     cp[len1] = ':';
86   return cp;
87 }
88 
findrcfile(char * rcfile)89 static char *findrcfile(char *rcfile) {
90   char buf[256];
91   char *p;
92 
93   /* Tilde prefix support courtesy <hesso@pool.math.tu-berlin.de>,
94    * taken from a Debian patch. */
95   if (rcfile && *rcfile == '~') {
96     static char rcfilename_tilde_exp[MAXPATHLEN+1];
97     char *slash_position = strchr(rcfile, '/');
98 
99     if (slash_position == rcfile+1) {
100       char *home = getenv("HOME");
101       if (!home) {
102         Msg(0, "%s: source: tilde expansion failed", rc_name);
103         return NULL;
104       }
105       snprintf(rcfilename_tilde_exp, MAXPATHLEN, "%s/%s", home, rcfile+2);
106     }
107     else if (slash_position) {
108       struct passwd *p;
109       *slash_position = 0;
110       p = getpwnam(rcfile+1);
111       if (!p){
112         Msg(0, "%s: source: tilde expansion failed for user %s", rc_name, rcfile+1);
113         return NULL;
114       }
115       snprintf(rcfilename_tilde_exp, MAXPATHLEN, "%s/%s", p->pw_dir, slash_position+1);
116     }
117     else {
118       Msg(0, "%s: source: illegal tilde expression.", rc_name);
119       return NULL;
120     }
121     rcfile = rcfilename_tilde_exp;
122   }
123 
124   if (rcfile) {
125     char *rcend = rindex(rc_name, '/');
126     if (*rcfile != '/' && rcend && (rcend - rc_name) + strlen(rcfile) + 2 < sizeof(buf)) {
127       strncpy(buf, rc_name, rcend - rc_name + 1);
128       strcpy(buf + (rcend - rc_name) + 1, rcfile);
129       if (access(buf, R_OK) == 0)
130         return SaveStr(buf);
131     }
132     debug1("findrcfile: you specified '%s'\n", rcfile);
133     return SaveStr(rcfile);
134   }
135 
136   debug("findrcfile: you specified nothing...\n");
137   if ((p = getenv("SCREENRC")) != NULL && *p != '\0') {
138     debug1("  $SCREENRC has: '%s'\n", p);
139     return SaveStr(p);
140   }
141   else {
142     debug("  ...nothing in $SCREENRC, defaulting $HOME/.screenrc\n");
143     if (strlen(home) > sizeof(buf) - 12)
144       Panic(0, "Rc: home too large");
145     sprintf(buf, "%s/.screenrc", home);
146     return SaveStr(buf);
147   }
148 }
149 
150 /*
151  * this will be called twice:
152  * 1) rcfilename = "/etc/screenrc"
153  * 2) rcfilename = RcFileName
154  */
StartRc(char * rcfilename,int nopanic)155 int StartRc(char *rcfilename, int nopanic) {
156   register int argc, len;
157   register char *p, *cp;
158   char buf[2048];
159   char *args[MAXARGS];
160   int argl[MAXARGS];
161   FILE *fp;
162   char *oldrc_name = rc_name;
163 
164   /* always fix termcap/info capabilities */
165   extra_incap = CatExtra("TF", extra_incap);
166 
167   /* Special settings for vt100 and others */
168   if (display && (!strncmp(D_termname, "vt", 2) || !strncmp(D_termname, "xterm", 5)))
169     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);
170 
171   rc_name = findrcfile(rcfilename);
172   if (rc_name == NULL || (fp = secfopen(rc_name, "r")) == NULL) {
173     const char *rc_nonnull = rc_name ? rc_name : rcfilename;
174     if (!rc_recursion && RcFileName && !strcmp(RcFileName, rc_nonnull)) {
175       /*
176        * User explicitly gave us that name,
177        * this is the only case, where we get angry, if we can't read
178        * the file.
179        */
180       debug3("StartRc: '%s','%s', '%s'\n", RcFileName, rc_name ? rc_name : "(null)", rcfilename);
181       if (!nopanic) Panic(0, "Unable to open \"%s\".", rc_nonnull);
182       /* possibly NOTREACHED */
183     }
184 
185     debug1("StartRc: '%s' no good. ignored\n", rc_nonnull);
186     if (rc_name)
187       Free(rc_name);
188     rc_name = oldrc_name;
189     return 1;
190   }
191   while (fgets(buf, sizeof buf, fp) != NULL) {
192     if ((p = rindex(buf, '\n')) != NULL)
193       *p = '\0';
194 
195     if ((argc = Parse(buf, sizeof buf, args, argl)) == 0)
196       continue;
197 
198     if (strcmp(args[0], "echo") == 0) {
199       if (!display)
200         continue;
201       if (argc < 2 || (argc == 3 && strcmp(args[1], "-n")) || argc > 3) {
202         Msg(0, "%s: 'echo [-n] \"string\"' expected.", rc_name);
203         continue;
204       }
205       AddStr(args[argc - 1]);
206       if (argc != 3) {
207         AddStr("\r\n");
208         Flush(0);
209       }
210     }
211 
212     else if (strcmp(args[0], "sleep") == 0) {
213       if (!display)
214         continue;
215       debug("sleeeeeeep\n");
216       if (argc != 2) {
217         Msg(0, "%s: sleep: one numeric argument expected.", rc_name);
218         continue;
219       }
220       DisplaySleep1000(1000 * atoi(args[1]), 1);
221     }
222 #ifdef TERMINFO
223     else if (!strcmp(args[0], "termcapinfo") || !strcmp(args[0], "terminfo")) {
224 #else
225     else if (!strcmp(args[0], "termcapinfo") || !strcmp(args[0], "termcap")) {
226 #endif
227       if (!display)
228         continue;
229       if (argc < 3 || argc > 4) {
230         Msg(0, "%s: %s: incorrect number of arguments.", rc_name, args[0]);
231         continue;
232       }
233 
234       for (p = args[1]; p && *p; p = cp) {
235         if ((cp = index(p, '|')) != 0)
236           *cp++ = '\0';
237         len = strlen(p);
238         if (p[len - 1] == '*') {
239           if (!(len - 1) || !strncmp(p, D_termname, len - 1))
240             break;
241         }
242         else if (!strcmp(p, D_termname))
243           break;
244       }
245       if (!(p && *p))
246         continue;
247       extra_incap = CatExtra(args[2], extra_incap);
248       if (argc == 4)
249         extra_outcap = CatExtra(args[3], extra_outcap);
250     }
251     else if (!strcmp(args[0], "source")) {
252       if (rc_recursion <= 10) {
253         rc_recursion++;
254         (void)StartRc(args[1], 0);
255         rc_recursion--;
256       }
257     }
258   }
259   fclose(fp);
260   Free(rc_name);
261   rc_name = oldrc_name;
262   return 0;
263 }
264 
265 void FinishRc(char *rcfilename) {
266   char buf[2048];
267   FILE *fp;
268   char *oldrc_name = rc_name;
269 
270   rc_name = findrcfile(rcfilename);
271 
272   if (rc_name == NULL || (fp = secfopen(rc_name, "r")) == NULL) {
273     const char *rc_nonnull = rc_name ? rc_name : rcfilename;
274     if (rc_recursion)
275       Msg(errno, "%s: source %s", oldrc_name, rc_nonnull);
276     else if (RcFileName && !strcmp(RcFileName, rc_nonnull)) {
277       /*
278        * User explicitly gave us that name,
279        * this is the only case, where we get angry, if we can't read
280        * the file.
281        */
282        debug3("FinishRc:'%s','%s','%s'\n", RcFileName, rc_name ? rc_name : "(null)", rcfilename);
283        Panic(0, "Unable to open \"%s\".", rc_nonnull);
284        /* NOTREACHED */
285     }
286     debug1("FinishRc: '%s' no good. ignored\n", rc_nonnull);
287     if (rc_name)
288       Free(rc_name);
289     rc_name = oldrc_name;
290     return;
291   }
292 
293   debug("finishrc is going...\n");
294   while (fgets(buf, sizeof buf, fp) != NULL)
295     RcLine(buf, sizeof buf);
296   (void) fclose(fp);
297   Free(rc_name);
298   rc_name = oldrc_name;
299 }
300 
301 void do_source(char *rcfilename) {
302   if (rc_recursion > 10) {
303     Msg(0, "%s: source: recursion limit reached", rc_name);
304     return;
305   }
306   rc_recursion++;
307   FinishRc(rcfilename);
308   rc_recursion--;
309 }
310 
311 
312 /*
313  * Running a Command Line in the environment determined by the display.
314  * The fore window is taken from the display as well as the user.
315  * This is bad when we run detached.
316  */
317 void RcLine(char *ubuf, int ubufl) {
318   char *args[MAXARGS];
319   int argl[MAXARGS];
320 #ifdef MULTIUSER
321   extern struct acluser *EffectiveAclUser;      /* acl.c */
322   extern struct acluser *users;                 /* acl.c */
323 #endif
324 
325   if (display) {
326     fore = D_fore;
327     flayer = D_forecv->c_layer;
328   }
329   else
330     flayer = fore ? fore->w_savelayer : 0;
331   if (Parse(ubuf, ubufl, args, argl) <= 0)
332     return;
333 
334 #ifdef MULTIUSER
335   if (!display) {
336       /* the session owner does it, when there is no display here */
337       EffectiveAclUser = users;
338       debug("RcLine: WARNING, no display no user! Session owner executes command\n");
339   }
340 #endif
341 
342   DoCommand(args, argl);
343 
344 #ifdef MULTIUSER
345   EffectiveAclUser = 0;
346 #endif
347 }
348 
349 /*  needs display for copybuffer access and termcap dumping  */
350 void WriteFile(struct acluser *user, char *fn, int dump) {
351   /* dump==0:	create .termcap,
352    * dump==1:	hardcopy,
353    * #ifdef COPY_PASTE
354    * dump==2:	BUFFERFILE
355    * #endif COPY_PASTE
356    * dump==1:	scrollback,
357    */
358   register int i, j, k;
359   register char *p;
360   register FILE *f;
361   char fnbuf[1024];
362   char *mode = "w";
363 
364 #ifdef COPY_PASTE
365   int public = 0;
366 # ifdef HAVE_LSTAT
367   struct stat stb, stb2;
368   int fd, exists = 0;
369 # endif
370 #endif
371 
372   switch (dump) {
373     case DUMP_TERMCAP:
374       if (fn == 0) {
375         i = SockName - SockPath;
376         if (i > (int)sizeof(fnbuf) - 9)
377           i = 0;
378         strncpy(fnbuf, SockPath, i);
379         strcpy(fnbuf + i, ".termcap");
380         fn = fnbuf;
381       }
382       break;
383 
384     case DUMP_HARDCOPY:
385     case DUMP_SCROLLBACK:
386       if (fn == 0) {
387         if (fore == 0)
388           return;
389         if (hardcopydir && *hardcopydir && strlen(hardcopydir) < sizeof(fnbuf) - 21)
390           sprintf(fnbuf, "%s/hardcopy.%d", hardcopydir, fore->w_number);
391         else
392           sprintf(fnbuf, "hardcopy.%d", fore->w_number);
393         fn = fnbuf;
394       }
395       if (hardcopy_append && !access(fn, W_OK))
396         mode = "a";
397       break;
398 
399 #ifdef COPY_PASTE
400     case DUMP_EXCHANGE:
401       if (fn == 0) {
402         strncpy(fnbuf, BufferFile, sizeof(fnbuf) - 1);
403         fnbuf[sizeof(fnbuf) - 1] = 0;
404         fn = fnbuf;
405       }
406       public = !strcmp(fn, DEFAULT_BUFFERFILE);
407 # ifdef HAVE_LSTAT
408       exists = !lstat(fn, &stb);
409       if (public && exists && (S_ISLNK(stb.st_mode) || stb.st_nlink > 1)) {
410         Msg(0, "No write to links, please.");
411         return;
412       }
413 # endif
414       break;
415 #endif
416   }
417 
418   debug2("WriteFile(%d) %s\n", dump, fn);
419   if (UserContext() > 0) {
420     debug("Writefile: usercontext\n");
421 #ifdef COPY_PASTE
422     if (dump == DUMP_EXCHANGE && public) {
423 # ifdef HAVE_LSTAT
424       if (exists) {
425         if ((fd = open(fn, O_WRONLY, 0666)) >= 0) {
426           if (fstat(fd, &stb2) == 0 && stb.st_dev == stb2.st_dev && stb.st_ino == stb2.st_ino)
427             ftruncate(fd, 0);
428           else {
429             close(fd);
430             fd = -1;
431           }
432         }
433       }
434       else
435         fd = open(fn, O_WRONLY|O_CREAT|O_EXCL, 0666);
436       f = fd >= 0 ? fdopen(fd, mode) : 0;
437 # else
438       f = fopen(fn, mode);
439 # endif
440     }
441     else
442 #endif /* COPY_PASTE */
443       f = fopen(fn, mode);
444         if (f == NULL) {
445           debug2("WriteFile: fopen(%s,\"%s\") failed\n", fn, mode);
446           UserReturn(0);
447         }
448         else {
449           switch (dump) {
450             case DUMP_HARDCOPY:
451             case DUMP_SCROLLBACK:
452               if (!fore)
453                 break;
454               if (*mode == 'a') {
455                 putc('>', f);
456                 for (j = fore->w_width - 2; j > 0; j--)
457                   putc('=', f);
458                   fputs("<\n", f);
459               }
460               if (dump == DUMP_SCROLLBACK) {
461 #ifdef COPY_PASTE
462                 for (i = fore->w_histheight - fore->w_scrollback_height; i < fore->w_histheight; i++) {
463                   p = (char *)(WIN(i)->image);
464                     for (k = fore->w_width - 1; k >= 0 && p[k] == ' '; k--)
465                       ;
466                     for (j = 0; j <= k; j++)
467                       putc(p[j], f);
468                       putc('\n', f);
469                 }
470 #endif
471               }
472               for (i = 0; i < fore->w_height; i++) {
473                 p = (char *)fore->w_mlines[i].image;
474                 for (k = fore->w_width - 1; k >= 0 && p[k] == ' '; k--)
475                   ;
476                 for (j = 0; j <= k; j++)
477                   putc(p[j], f);
478                   putc('\n', f);
479                 }
480                 break;
481 
482             case DUMP_TERMCAP:
483               DumpTermcap(fore->w_aflag, f);
484 	      break;
485 
486 #ifdef COPY_PASTE
487             case DUMP_EXCHANGE:
488               p = user->u_plop.buf;
489               for (i = user->u_plop.len; i-- > 0; p++)
490                 if (*p == '\r' && (i == 0 || p[1] != '\n'))
491                   putc('\n', f);
492                 else
493                   putc(*p, f);
494               break;
495 #endif
496         }
497         (void) fclose(f);
498         UserReturn(1);
499       }
500     }
501   if (UserStatus() <= 0)
502     Msg(0, "Cannot open \"%s\"", fn);
503   else if (display && !*rc_name) {
504     switch (dump) {
505       case DUMP_TERMCAP:
506         Msg(0, "Termcap entry written to \"%s\".", fn);
507         break;
508       case DUMP_HARDCOPY:
509       case DUMP_SCROLLBACK:
510         Msg(0, "Screen image %s to \"%s\".", (*mode == 'a') ? "appended" : "written", fn);
511         break;
512 #ifdef COPY_PASTE
513       case DUMP_EXCHANGE:
514         Msg(0, "Copybuffer written to \"%s\".", fn);
515 #endif
516     }
517   }
518 }
519 
520 #ifdef COPY_PASTE
521 
522 /*
523  * returns an allocated buffer which holds a copy of the file named fn.
524  * lenp (if nonzero) points to a location, where the buffer size should be
525  * stored.
526  */
527 char *ReadFile(char *fn, int *lenp) {
528   int i, l, size;
529   char c, *bp, *buf;
530   struct stat stb;
531 
532   ASSERT(lenp);
533   debug1("ReadFile(%s)\n", fn);
534 
535   if ((i = secopen(fn, O_RDONLY, 0)) < 0) {
536     Msg(errno, "no %s -- no slurp", fn);
537     return NULL;
538   }
539 
540   if (fstat(i, &stb)) {
541     Msg(errno, "no good %s -- no slurp", fn);
542     close(i);
543     return NULL;
544   }
545   size = stb.st_size;
546 
547   if ((buf = malloc(size)) == NULL) {
548     close(i);
549     Msg(0, "%s", strnomem);
550     return NULL;
551   }
552   errno = 0;
553 
554   if ((l = read(i, buf, size)) != size) {
555     if (l < 0)
556       l = 0;
557     Msg(errno, "Got only %d bytes from %s", l, fn);
558   }
559   else {
560     if (read(i, &c, 1) > 0)
561       Msg(0, "Slurped only %d characters (of %d) into buffer - try again", l, size);
562     else
563       Msg(0, "Slurped %d characters into buffer", l);
564   }
565   close(i);
566   *lenp = l;
567   for (bp = buf; l-- > 0; bp++)
568     if (*bp == '\n' && (bp == buf || bp[-1] != '\r'))
569       *bp = '\r';
570   return buf;
571 }
572 
573 void KillBuffers() {
574   if (UserContext() > 0)
575     UserReturn(unlink(BufferFile) ? errno : 0);
576   errno = UserStatus();
577   Msg(errno, "%s %sremoved", BufferFile, errno ? "not " : "");
578 }
579 #endif	/* COPY_PASTE */
580 
581 
582 /* (Almost) secure open and fopen...  */
583 
584 FILE *secfopen(char *name, char *mode) {
585   FILE *fi;
586 #ifndef USE_SETEUID
587   int flags, fd;
588 #endif
589 
590   debug2("secfopen(%s, %s)\n", name, mode);
591 #ifdef USE_SETEUID
592   xseteuid(real_uid);
593   xsetegid(real_gid);
594   fi = fopen(name, mode);
595   xseteuid(eff_uid);
596   xsetegid(eff_gid);
597   return fi;
598 
599 #else
600   if (eff_uid == real_uid)
601     return fopen(name, mode);
602   if (mode[0] && mode[1] == '+')
603     flags = O_RDWR;
604   else
605     flags = (mode[0] == 'r') ? O_RDONLY : O_WRONLY;
606   if (mode[0] == 'w')
607     flags |= O_CREAT | O_TRUNC;
608   else if (mode[0] == 'a')
609     flags |= O_CREAT | O_APPEND;
610   else if (mode[0] != 'r') {
611     errno = EINVAL;
612     return 0;
613   }
614   if ((fd = secopen(name, flags, 0666)) < 0)
615     return 0;
616   if ((fi = fdopen(fd, mode)) == 0) {
617     close(fd);
618     return 0;
619   }
620   return fi;
621 #endif
622 }
623 
624 
625 int secopen(char *name, int flags, int mode) {
626   int fd;
627 #ifndef USE_SETEUID
628   int q;
629   struct stat stb;
630 #endif
631 
632   debug3("secopen(%s, 0x%x, 0%03o)\n", name, flags, mode);
633 #ifdef USE_SETEUID
634   xseteuid(real_uid);
635   xsetegid(real_gid);
636   fd = open(name, flags, mode);
637   xseteuid(eff_uid);
638   xsetegid(eff_gid);
639   return fd;
640 #else
641   if (eff_uid == real_uid)
642     return open(name, flags, mode);
643   /* Truncation/creation is done in UserContext */
644   if ((flags & O_TRUNC) || ((flags & O_CREAT) && access(name, F_OK))) {
645     if (UserContext() > 0) {
646       if ((fd = open(name, flags, mode)) >= 0) {
647         close(fd);
648         UserReturn(0);
649       }
650       if (errno == 0)
651         errno = EACCES;
652       UserReturn(errno);
653     }
654     if ((q = UserStatus())) {
655       if (q > 0)
656         errno = q;
657       return -1;
658     }
659   }
660   if (access(name, F_OK))
661     return -1;
662   if ((fd = open(name, flags & ~(O_TRUNC | O_CREAT), 0)) < 0)
663     return -1;
664   debug("open successful\n");
665   if (fstat(fd, &stb)) {
666     close(fd);
667     return -1;
668   }
669   debug("fstat successful\n");
670   if (stb.st_uid != real_uid) {
671     switch (flags & (O_RDONLY | O_WRONLY | O_RDWR)) {
672       case O_RDONLY:
673         q = 0004;
674         break;
675       case O_WRONLY:
676         q = 0002;
677         break;
678       default:
679         q = 0006;
680         break;
681     }
682     if ((stb.st_mode & q) != q) {
683       debug1("secopen: permission denied (%03o)\n", stb.st_mode & 07777);
684       close(fd);
685       errno = EACCES;
686       return -1;
687     }
688   }
689   debug1("secopen ok - returning %d\n", fd);
690   return fd;
691 #endif
692 }
693 
694 
695 int printpipe(struct win *p, char *cmd) {
696   int pi[2];
697   if (pipe(pi)) {
698     WMsg(p, errno, "printing pipe");
699     return -1;
700   }
701   switch (fork()) {
702     case -1:
703       WMsg(p, errno, "printing fork");
704       return -1;
705     case 0:
706       display = p->w_pdisplay;
707       displays = 0;
708       ServerSocket = -1;
709 #ifdef DEBUG
710       if (dfp && dfp != stderr)
711         fclose(dfp);
712 #endif
713       close(0);
714       dup(pi[0]);
715       closeallfiles(0);
716       if (setgid(real_gid) || setuid(real_uid))
717         Panic(errno, "printpipe setuid");
718       eff_uid = real_uid;
719       eff_gid = real_gid;
720 
721 #ifdef SIGPIPE
722       signal(SIGPIPE, SIG_DFL);
723 #endif
724       execl("/bin/sh", "sh", "-c", cmd, (char *)0);
725       Panic(errno, "/bin/sh");
726     default:
727       break;
728   }
729   close(pi[0]);
730   return pi[1];
731 }
732 
733 int readpipe(char **cmdv) {
734   int pi[2];
735 
736   if (pipe(pi)) {
737     Msg(errno, "pipe");
738     return -1;
739   }
740 
741   switch (fork()) {
742   case -1:
743     Msg(errno, "fork");
744     return -1;
745   case 0:
746     displays = 0;
747     ServerSocket = -1;
748 #ifdef DEBUG
749     if (dfp && dfp != stderr)
750       fclose(dfp);
751 #endif
752     close(1);
753     if (dup(pi[1]) != 1) {
754       close(pi[1]);
755       Panic(0, "dup");
756     }
757     closeallfiles(1);
758 
759     if (setgid(real_gid) || setuid(real_uid)) {
760       close(1);
761       Panic(errno, "setuid/setgid");
762     }
763     eff_uid = real_uid;
764     eff_gid = real_gid;
765 #ifdef SIGPIPE
766     signal(SIGPIPE, SIG_DFL);
767 #endif
768     execvp(*cmdv, cmdv);
769     close(1);
770     Panic(errno, "%s", *cmdv);
771   default:
772     break;
773   }
774   close(pi[1]);
775   return pi[0];
776 }
777