Start converting to GPL v3+ (ref: ticket #23900)
[screen-lua.git] / src / fileio.c
blobf47c40a7040a054c887f8cb601c29008d31ae874
1 /* Copyright (c) 1993-2002
2 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
4 * Copyright (c) 1987 Oliver Laumann
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3, or (at your option)
9 * any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program (see the file COPYING); if not, see
18 * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
21 ****************************************************************
24 #include <sys/types.h>
25 #include <fcntl.h>
26 #include <sys/stat.h>
27 #include <pwd.h>
29 #ifndef SIGINT
30 # include <signal.h>
31 #endif
33 #include "config.h"
34 #include "screen.h"
35 #include "extern.h"
37 extern struct display *display, *displays;
38 extern struct win *fore;
39 extern struct layer *flayer;
40 extern int real_uid, eff_uid;
41 extern int real_gid, eff_gid;
42 extern char *extra_incap, *extra_outcap;
43 extern char *home, *RcFileName;
44 extern char SockPath[], *SockName;
45 #ifdef COPY_PASTE
46 extern char *BufferFile;
47 #endif
48 extern int hardcopy_append;
49 extern char *hardcopydir;
51 static char *CatExtra __P((char *, char *));
52 static char *findrcfile __P((char *));
55 char *rc_name = "";
56 int rc_recursion = 0;
58 static char *
59 CatExtra(str1, str2)
60 register char *str1, *str2;
62 register char *cp;
63 register int len1, len2, add_colon;
65 len1 = strlen(str1);
66 if (len1 == 0)
67 return str2;
68 add_colon = (str1[len1 - 1] != ':');
69 if (str2)
71 len2 = strlen(str2);
72 if ((cp = realloc(str2, (unsigned) len1 + len2 + add_colon + 1)) == NULL)
73 Panic(0, strnomem);
74 bcopy(cp, cp + len1 + add_colon, len2 + 1);
76 else
78 if (len1 == 0)
79 return 0;
80 if ((cp = malloc((unsigned) len1 + add_colon + 1)) == NULL)
81 Panic(0, strnomem);
82 cp[len1 + add_colon] = '\0';
84 bcopy(str1, cp, len1);
85 if (add_colon)
86 cp[len1] = ':';
88 return cp;
91 static char *
92 findrcfile(rcfile)
93 char *rcfile;
95 char buf[256];
96 char *p;
98 if (rcfile)
100 char *rcend = rindex(rc_name, '/');
101 if (*rcfile != '/' && rcend && (rcend - rc_name) + strlen(rcfile) + 2 < sizeof(buf))
103 strncpy(buf, rc_name, rcend - rc_name + 1);
104 strcpy(buf + (rcend - rc_name) + 1, rcfile);
105 if (access(buf, R_OK) == 0)
106 return SaveStr(buf);
108 debug1("findrcfile: you specified '%s'\n", rcfile);
109 return SaveStr(rcfile);
111 debug("findrcfile: you specified nothing...\n");
112 if ((p = getenv("SCREENRC")) != NULL && *p != '\0')
114 debug1(" $SCREENRC has: '%s'\n", p);
115 return SaveStr(p);
117 else
119 debug(" ...nothing in $SCREENRC, defaulting $HOME/.screenrc\n");
120 if (strlen(home) > sizeof(buf) - 12)
121 Panic(0, "Rc: home too large");
122 sprintf(buf, "%s/.screenrc", home);
123 return SaveStr(buf);
128 * this will be called twice:
129 * 1) rcfilename = "/etc/screenrc"
130 * 2) rcfilename = RcFileName
133 StartRc(rcfilename, nopanic)
134 char *rcfilename;
135 int nopanic;
137 register int argc, len;
138 register char *p, *cp;
139 char buf[2048];
140 char *args[MAXARGS];
141 int argl[MAXARGS];
142 FILE *fp;
143 char *oldrc_name = rc_name;
145 /* always fix termcap/info capabilities */
146 extra_incap = CatExtra("TF", extra_incap);
148 /* Special settings for vt100 and others */
149 if (display && (!strncmp(D_termname, "vt", 2) || !strncmp(D_termname, "xterm", 5)))
150 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);
152 rc_name = findrcfile(rcfilename);
154 if ((fp = secfopen(rc_name, "r")) == NULL)
156 if (!rc_recursion && RcFileName && !strcmp(RcFileName, rc_name))
159 * User explicitly gave us that name,
160 * this is the only case, where we get angry, if we can't read
161 * the file.
163 debug3("StartRc: '%s','%s', '%s'\n", RcFileName, rc_name, rcfilename);
164 if (!nopanic) Panic(0, "Unable to open \"%s\".", rc_name);
165 /* possibly NOTREACHED */
167 debug1("StartRc: '%s' no good. ignored\n", rc_name);
168 Free(rc_name);
169 rc_name = oldrc_name;
170 return 1;
172 while (fgets(buf, sizeof buf, fp) != NULL)
174 if ((p = rindex(buf, '\n')) != NULL)
175 *p = '\0';
176 if ((argc = Parse(buf, sizeof buf, args, argl)) == 0)
177 continue;
178 if (strcmp(args[0], "echo") == 0)
180 if (!display)
181 continue;
182 if (argc < 2 || (argc == 3 && strcmp(args[1], "-n")) || argc > 3)
184 Msg(0, "%s: 'echo [-n] \"string\"' expected.", rc_name);
185 continue;
187 AddStr(args[argc - 1]);
188 if (argc != 3)
190 AddStr("\r\n");
191 Flush();
194 else if (strcmp(args[0], "sleep") == 0)
196 if (!display)
197 continue;
198 debug("sleeeeeeep\n");
199 if (argc != 2)
201 Msg(0, "%s: sleep: one numeric argument expected.", rc_name);
202 continue;
204 DisplaySleep1000(1000 * atoi(args[1]), 1);
206 #ifdef TERMINFO
207 else if (!strcmp(args[0], "termcapinfo") || !strcmp(args[0], "terminfo"))
208 #else
209 else if (!strcmp(args[0], "termcapinfo") || !strcmp(args[0], "termcap"))
210 #endif
212 if (!display)
213 continue;
214 if (argc < 3 || argc > 4)
216 Msg(0, "%s: %s: incorrect number of arguments.", rc_name, args[0]);
217 continue;
219 for (p = args[1]; p && *p; p = cp)
221 if ((cp = index(p, '|')) != 0)
222 *cp++ = '\0';
223 len = strlen(p);
224 if (p[len - 1] == '*')
226 if (!(len - 1) || !strncmp(p, D_termname, len - 1))
227 break;
229 else if (!strcmp(p, D_termname))
230 break;
232 if (!(p && *p))
233 continue;
234 extra_incap = CatExtra(args[2], extra_incap);
235 if (argc == 4)
236 extra_outcap = CatExtra(args[3], extra_outcap);
238 else if (!strcmp(args[0], "source"))
240 if (rc_recursion <= 10)
242 rc_recursion++;
243 (void)StartRc(args[1], 0);
244 rc_recursion--;
248 fclose(fp);
249 Free(rc_name);
250 rc_name = oldrc_name;
251 return 0;
254 void
255 FinishRc(rcfilename)
256 char *rcfilename;
258 char buf[2048];
259 FILE *fp;
260 char *oldrc_name = rc_name;
262 rc_name = findrcfile(rcfilename);
264 if ((fp = secfopen(rc_name, "r")) == NULL)
266 if (rc_recursion)
267 Msg(errno, "%s: source %s", oldrc_name, rc_name);
268 else if (RcFileName && !strcmp(RcFileName, rc_name))
271 * User explicitly gave us that name,
272 * this is the only case, where we get angry, if we can't read
273 * the file.
275 debug3("FinishRc:'%s','%s','%s'\n", RcFileName, rc_name, rcfilename);
276 Panic(0, "Unable to open \"%s\".", rc_name);
277 /* NOTREACHED */
279 debug1("FinishRc: '%s' no good. ignored\n", rc_name);
280 Free(rc_name);
281 rc_name = oldrc_name;
282 return;
285 debug("finishrc is going...\n");
286 while (fgets(buf, sizeof buf, fp) != NULL)
287 RcLine(buf, sizeof buf);
288 (void) fclose(fp);
289 Free(rc_name);
290 rc_name = oldrc_name;
293 void
294 do_source(rcfilename)
295 char *rcfilename;
297 if (rc_recursion > 10)
299 Msg(0, "%s: source: recursion limit reached", rc_name);
300 return;
302 /* Tilde prefix support courtesy <hesso@pool.math.tu-berlin.de>,
303 * taken from a Debian patch. */
304 if (*rcfilename == '~')
306 char rcfilename_tilde_exp[MAXPATHLEN+1];
307 char *slash_position = strchr(rcfilename, '/');
308 if (slash_position == rcfilename+1)
310 char *home = getenv("HOME");
311 if (!home)
313 Msg(0, "%s: source: tilde expansion failed", rc_name);
314 return;
316 snprintf(rcfilename_tilde_exp, MAXPATHLEN, "%s/%s", home, rcfilename+2);
318 else if (slash_position)
320 struct passwd *p;
321 *slash_position = 0;
322 p = getpwnam(rcfilename+1);
323 if (!p)
325 Msg(0, "%s: source: tilde expansion failed for user %s", rc_name, rcfilename+1);
326 return;
328 snprintf(rcfilename_tilde_exp, MAXPATHLEN, "%s/%s", p->pw_dir, slash_position+1);
330 else
332 Msg(0, "%s: source: illegal tilde expression.", rc_name);
333 return;
335 rc_recursion++;
336 FinishRc(rcfilename_tilde_exp);
337 rc_recursion--;
339 else
341 rc_recursion++;
342 FinishRc(rcfilename);
343 rc_recursion--;
349 * Running a Command Line in the environment determined by the display.
350 * The fore window is taken from the display as well as the user.
351 * This is bad when we run detached.
353 void
354 RcLine(ubuf, ubufl)
355 char *ubuf;
356 int ubufl;
358 char *args[MAXARGS];
359 int argl[MAXARGS];
360 #ifdef MULTIUSER
361 extern struct acluser *EffectiveAclUser; /* acl.c */
362 extern struct acluser *users; /* acl.c */
363 #endif
365 if (display)
367 fore = D_fore;
368 flayer = D_forecv->c_layer;
370 else
371 flayer = fore ? fore->w_savelayer : 0;
372 if (Parse(ubuf, ubufl, args, argl) <= 0)
373 return;
374 #ifdef MULTIUSER
375 if (!display)
377 /* the session owner does it, when there is no display here */
378 EffectiveAclUser = users;
379 debug("RcLine: WARNING, no display no user! Session owner executes command\n");
381 #endif
382 DoCommand(args, argl);
383 #ifdef MULTIUSER
384 EffectiveAclUser = 0;
385 #endif
389 * needs display for copybuffer access and termcap dumping
391 void
392 WriteFile(user, fn, dump)
393 struct acluser *user;
394 char *fn;
395 int dump;
397 /* dump==0: create .termcap,
398 * dump==1: hardcopy,
399 * #ifdef COPY_PASTE
400 * dump==2: BUFFERFILE
401 * #endif COPY_PASTE
402 * dump==1: scrollback,
404 register int i, j, k;
405 register char *p;
406 register FILE *f;
407 char fnbuf[1024];
408 char *mode = "w";
409 #ifdef COPY_PASTE
410 int public = 0;
411 # ifdef _MODE_T
412 mode_t old_umask;
413 # else
414 int old_umask;
415 # endif
416 # ifdef HAVE_LSTAT
417 struct stat stb, stb2;
418 int fd, exists = 0;
419 # endif
420 #endif
422 switch (dump)
424 case DUMP_TERMCAP:
425 if (fn == 0)
427 i = SockName - SockPath;
428 if (i > (int)sizeof(fnbuf) - 9)
429 i = 0;
430 strncpy(fnbuf, SockPath, i);
431 strcpy(fnbuf + i, ".termcap");
432 fn = fnbuf;
434 break;
435 case DUMP_HARDCOPY:
436 case DUMP_SCROLLBACK:
437 if (fn == 0)
439 if (fore == 0)
440 return;
441 if (hardcopydir && *hardcopydir && strlen(hardcopydir) < sizeof(fnbuf) - 21)
442 sprintf(fnbuf, "%s/hardcopy.%d", hardcopydir, fore->w_number);
443 else
444 sprintf(fnbuf, "hardcopy.%d", fore->w_number);
445 fn = fnbuf;
447 if (hardcopy_append && !access(fn, W_OK))
448 mode = "a";
449 break;
450 #ifdef COPY_PASTE
451 case DUMP_EXCHANGE:
452 if (fn == 0)
454 strncpy(fnbuf, BufferFile, sizeof(fnbuf) - 1);
455 fnbuf[sizeof(fnbuf) - 1] = 0;
456 fn = fnbuf;
458 public = !strcmp(fn, DEFAULT_BUFFERFILE);
459 # ifdef HAVE_LSTAT
460 exists = !lstat(fn, &stb);
461 if (public && exists && (S_ISLNK(stb.st_mode) || stb.st_nlink > 1))
463 Msg(0, "No write to links, please.");
464 return;
466 # endif
467 break;
468 #endif
471 debug2("WriteFile(%d) %s\n", dump, fn);
472 if (UserContext() > 0)
474 debug("Writefile: usercontext\n");
475 #ifdef COPY_PASTE
476 if (dump == DUMP_EXCHANGE && public)
478 old_umask = umask(0);
479 # ifdef HAVE_LSTAT
480 if (exists)
482 if ((fd = open(fn, O_WRONLY, 0666)) >= 0)
484 if (fstat(fd, &stb2) == 0 && stb.st_dev == stb2.st_dev && stb.st_ino == stb2.st_ino)
485 ftruncate(fd, 0);
486 else
488 close(fd);
489 fd = -1;
493 else
494 fd = open(fn, O_WRONLY|O_CREAT|O_EXCL, 0666);
495 f = fd >= 0 ? fdopen(fd, mode) : 0;
496 # else
497 f = fopen(fn, mode);
498 # endif
499 umask(old_umask);
501 else
502 #endif /* COPY_PASTE */
503 f = fopen(fn, mode);
504 if (f == NULL)
506 debug2("WriteFile: fopen(%s,\"%s\") failed\n", fn, mode);
507 UserReturn(0);
509 else
511 switch (dump)
513 case DUMP_HARDCOPY:
514 case DUMP_SCROLLBACK:
515 if (!fore)
516 break;
517 if (*mode == 'a')
519 putc('>', f);
520 for (j = fore->w_width - 2; j > 0; j--)
521 putc('=', f);
522 fputs("<\n", f);
524 if (dump == DUMP_SCROLLBACK)
526 for (i = 0; i < fore->w_histheight; i++)
528 p = (char *)(WIN(i)->image);
529 for (k = fore->w_width - 1; k >= 0 && p[k] == ' '; k--)
531 for (j = 0; j <= k; j++)
532 putc(p[j], f);
533 putc('\n', f);
536 for (i = 0; i < fore->w_height; i++)
538 p = (char *)fore->w_mlines[i].image;
539 for (k = fore->w_width - 1; k >= 0 && p[k] == ' '; k--)
541 for (j = 0; j <= k; j++)
542 putc(p[j], f);
543 putc('\n', f);
545 break;
546 case DUMP_TERMCAP:
547 if ((p = index(MakeTermcap(fore->w_aflag), '=')) != NULL)
549 fputs(++p, f);
550 putc('\n', f);
552 break;
553 #ifdef COPY_PASTE
554 case DUMP_EXCHANGE:
555 p = user->u_plop.buf;
556 for (i = user->u_plop.len; i-- > 0; p++)
557 if (*p == '\r' && (i == 0 || p[1] != '\n'))
558 putc('\n', f);
559 else
560 putc(*p, f);
561 break;
562 #endif
564 (void) fclose(f);
565 UserReturn(1);
568 if (UserStatus() <= 0)
569 Msg(0, "Cannot open \"%s\"", fn);
570 else if (display && !*rc_name)
572 switch (dump)
574 case DUMP_TERMCAP:
575 Msg(0, "Termcap entry written to \"%s\".", fn);
576 break;
577 case DUMP_HARDCOPY:
578 case DUMP_SCROLLBACK:
579 Msg(0, "Screen image %s to \"%s\".",
580 (*mode == 'a') ? "appended" : "written", fn);
581 break;
582 #ifdef COPY_PASTE
583 case DUMP_EXCHANGE:
584 Msg(0, "Copybuffer written to \"%s\".", fn);
585 #endif
590 #ifdef COPY_PASTE
593 * returns an allocated buffer which holds a copy of the file named fn.
594 * lenp (if nonzero) points to a location, where the buffer size should be
595 * stored.
597 char *
598 ReadFile(fn, lenp)
599 char *fn;
600 int *lenp;
602 int i, l, size;
603 char c, *bp, *buf;
604 struct stat stb;
606 ASSERT(lenp);
607 debug1("ReadFile(%s)\n", fn);
608 if ((i = secopen(fn, O_RDONLY, 0)) < 0)
610 Msg(errno, "no %s -- no slurp", fn);
611 return NULL;
613 if (fstat(i, &stb))
615 Msg(errno, "no good %s -- no slurp", fn);
616 close(i);
617 return NULL;
619 size = stb.st_size;
620 if ((buf = malloc(size)) == NULL)
622 close(i);
623 Msg(0, strnomem);
624 return NULL;
626 errno = 0;
627 if ((l = read(i, buf, size)) != size)
629 if (l < 0)
630 l = 0;
631 Msg(errno, "Got only %d bytes from %s", l, fn);
632 close(i);
634 else
636 if (read(i, &c, 1) > 0)
637 Msg(0, "Slurped only %d characters (of %d) into buffer - try again",
638 l, size);
639 else
640 Msg(0, "Slurped %d characters into buffer", l);
642 close(i);
643 *lenp = l;
644 for (bp = buf; l-- > 0; bp++)
645 if (*bp == '\n' && (bp == buf || bp[-1] != '\r'))
646 *bp = '\r';
647 return buf;
650 void
651 KillBuffers()
653 if (UserContext() > 0)
654 UserReturn(unlink(BufferFile) ? errno : 0);
655 errno = UserStatus();
656 Msg(errno, "%s %sremoved", BufferFile, errno ? "not " : "");
658 #endif /* COPY_PASTE */
662 * (Almost) secure open and fopen...
665 FILE *
666 secfopen(name, mode)
667 char *name;
668 char *mode;
670 FILE *fi;
671 #ifndef USE_SETEUID
672 int flags, fd;
673 #endif
675 debug2("secfopen(%s, %s)\n", name, mode);
676 #ifdef USE_SETEUID
677 xseteuid(real_uid);
678 xsetegid(real_gid);
679 fi = fopen(name, mode);
680 xseteuid(eff_uid);
681 xsetegid(eff_gid);
682 return fi;
683 #else
684 if (eff_uid == real_uid)
685 return fopen(name, mode);
686 if (mode[0] && mode[1] == '+')
687 flags = O_RDWR;
688 else
689 flags = (mode[0] == 'r') ? O_RDONLY : O_WRONLY;
690 if (mode[0] == 'w')
691 flags |= O_CREAT | O_TRUNC;
692 else if (mode[0] == 'a')
693 flags |= O_CREAT | O_APPEND;
694 else if (mode[0] != 'r')
696 errno = EINVAL;
697 return 0;
699 if ((fd = secopen(name, flags, 0666)) < 0)
700 return 0;
701 if ((fi = fdopen(fd, mode)) == 0)
703 close(fd);
704 return 0;
706 return fi;
707 #endif
712 secopen(name, flags, mode)
713 char *name;
714 int flags;
715 int mode;
717 int fd;
718 #ifndef USE_SETEUID
719 int q;
720 struct stat stb;
721 #endif
723 debug3("secopen(%s, 0x%x, 0%03o)\n", name, flags, mode);
724 #ifdef USE_SETEUID
725 xseteuid(real_uid);
726 xsetegid(real_gid);
727 fd = open(name, flags, mode);
728 xseteuid(eff_uid);
729 xsetegid(eff_gid);
730 return fd;
731 #else
732 if (eff_uid == real_uid)
733 return open(name, flags, mode);
734 /* Truncation/creation is done in UserContext */
735 if ((flags & O_TRUNC) || ((flags & O_CREAT) && access(name, F_OK)))
737 if (UserContext() > 0)
739 if ((fd = open(name, flags, mode)) >= 0)
741 close(fd);
742 UserReturn(0);
744 if (errno == 0)
745 errno = EACCES;
746 UserReturn(errno);
748 if ((q = UserStatus()))
750 if (q > 0)
751 errno = q;
752 return -1;
755 if (access(name, F_OK))
756 return -1;
757 if ((fd = open(name, flags & ~(O_TRUNC | O_CREAT), 0)) < 0)
758 return -1;
759 debug("open successful\n");
760 if (fstat(fd, &stb))
762 close(fd);
763 return -1;
765 debug("fstat successful\n");
766 if (stb.st_uid != real_uid)
768 switch (flags & (O_RDONLY | O_WRONLY | O_RDWR))
770 case O_RDONLY:
771 q = 0004;
772 break;
773 case O_WRONLY:
774 q = 0002;
775 break;
776 default:
777 q = 0006;
778 break;
780 if ((stb.st_mode & q) != q)
782 debug1("secopen: permission denied (%03o)\n", stb.st_mode & 07777);
783 close(fd);
784 errno = EACCES;
785 return -1;
788 debug1("secopen ok - returning %d\n", fd);
789 return fd;
790 #endif
795 printpipe(p, cmd)
796 struct win *p;
797 char *cmd;
799 int pi[2];
800 if (pipe(pi))
802 WMsg(p, errno, "printing pipe");
803 return -1;
805 switch (fork())
807 case -1:
808 WMsg(p, errno, "printing fork");
809 return -1;
810 case 0:
811 display = p->w_pdisplay;
812 displays = 0;
813 #ifdef DEBUG
814 if (dfp && dfp != stderr)
815 fclose(dfp);
816 #endif
817 close(0);
818 dup(pi[0]);
819 closeallfiles(0);
820 if (setgid(real_gid) || setuid(real_uid))
821 Panic(errno, "printpipe setuid");
822 #ifdef SIGPIPE
823 signal(SIGPIPE, SIG_DFL);
824 #endif
825 execl("/bin/sh", "sh", "-c", cmd, (char *)0);
826 Panic(errno, "/bin/sh");
827 default:
828 break;
830 close(pi[0]);
831 return pi[1];
835 readpipe(cmdv)
836 char **cmdv;
838 int pi[2];
840 if (pipe(pi))
842 Msg(errno, "pipe");
843 return -1;
845 switch (fork())
847 case -1:
848 Msg(errno, "fork");
849 return -1;
850 case 0:
851 displays = 0;
852 #ifdef DEBUG
853 if (dfp && dfp != stderr)
854 fclose(dfp);
855 #endif
856 close(1);
857 if (dup(pi[1]) != 1)
859 close(pi[1]);
860 Panic(0, "dup");
862 closeallfiles(1);
863 if (setgid(real_gid) || setuid(real_uid))
865 close(1);
866 Panic(errno, "setuid/setgid");
868 #ifdef SIGPIPE
869 signal(SIGPIPE, SIG_DFL);
870 #endif
871 execvp(*cmdv, cmdv);
872 close(1);
873 Panic(errno, *cmdv);
874 default:
875 break;
877 close(pi[1]);
878 return pi[0];