1 /*
2  * Pager: Routines to create a "more" running out of a particular file
3  * descriptor.
4  *
5  * Copyright 1987, 1988 by MIT Student Information Processing Board
6  *
7  * Permission to use, copy, modify, and distribute this software and
8  * its documentation for any purpose is hereby granted, provided that
9  * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in
10  * advertising or publicity pertaining to distribution of the software
11  * without specific, written prior permission.  M.I.T. and the
12  * M.I.T. S.I.P.B. make no representations about the suitability of
13  * this software for any purpose.  It is provided "as is" without
14  * express or implied warranty.
15  */
16 
17 #if HAVE_SECURE_GETENV
18 #define _GNU_SOURCE
19 #endif
20 #ifdef HAVE_UNISTD_H
21 #include <unistd.h>
22 #endif
23 #ifdef HAVE_ERRNO_H
24 #include <errno.h>
25 #else
26 extern int errno;
27 #endif
28 
29 #include "ss_internal.h"
30 #include <stdio.h>
31 #include <sys/types.h>
32 #include <sys/file.h>
33 #include <signal.h>
34 #ifdef HAVE_SYS_PRCTL_H
35 #include <sys/prctl.h>
36 #else
37 #define PR_GET_DUMPABLE 3
38 #endif
39 #if (!defined(HAVE_PRCTL) && defined(linux))
40 #include <sys/syscall.h>
41 #endif
42 
43 static char MORE[] = "more";
44 extern char *getenv PROTOTYPE((const char *));
45 
ss_safe_getenv(const char * arg)46 char *ss_safe_getenv(const char *arg)
47 {
48 	if ((getuid() != geteuid()) || (getgid() != getegid()))
49 		return NULL;
50 #if HAVE_PRCTL
51 	if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
52 		return NULL;
53 #else
54 #if (defined(linux) && defined(SYS_prctl))
55 	if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
56 		return NULL;
57 #endif
58 #endif
59 
60 #if defined(HAVE_SECURE_GETENV)
61 	return secure_getenv(arg);
62 #elif defined(HAVE___SECURE_GETENV)
63 	return __secure_getenv(arg);
64 #else
65 	return getenv(arg);
66 #endif
67 }
68 
69 /*
70  * this needs a *lot* of work....
71  *
72  * run in same process
73  * handle SIGINT sensibly
74  * allow finer control -- put-page-break-here
75  */
76 
77 #ifndef NO_FORK
ss_pager_create(void)78 int ss_pager_create(void)
79 {
80 	int filedes[2];
81 
82 	if (pipe(filedes) != 0)
83 		return(-1);
84 
85 	switch(fork()) {
86 	case -1:
87 		return(-1);
88 	case 0:
89 		/*
90 		 * Child; dup read half to 0, close all but 0, 1, and 2
91 		 */
92 		if (dup2(filedes[0], 0) == -1)
93 			exit(1);
94 		ss_page_stdin();
95 	default:
96 		/*
97 		 * Parent:  close "read" side of pipe, return
98 		 * "write" side.
99 		 */
100 		(void) close(filedes[0]);
101 		return(filedes[1]);
102 	}
103 }
104 #else /* don't fork */
ss_pager_create()105 int ss_pager_create()
106 {
107     int fd;
108     fd = open("/dev/tty", O_WRONLY, 0);
109     return fd;
110 }
111 #endif
112 
write_all(int fd,char * buf,size_t count)113 static int write_all(int fd, char *buf, size_t count)
114 {
115 	ssize_t ret;
116 	int c = 0;
117 
118 	while (count > 0) {
119 		ret = write(fd, buf, count);
120 		if (ret < 0) {
121 			if ((errno == EAGAIN) || (errno == EINTR))
122 				continue;
123 			return -1;
124 		}
125 		count -= ret;
126 		buf += ret;
127 		c += ret;
128 	}
129 	return c;
130 }
131 
ss_page_stdin(void)132 void ss_page_stdin(void)
133 {
134 	int i;
135 	sigset_t mask;
136 
137 	for (i = 3; i < 32; i++)
138 		(void) close(i);
139 	(void) signal(SIGINT, SIG_DFL);
140 	sigprocmask(SIG_BLOCK, 0, &mask);
141 	sigdelset(&mask, SIGINT);
142 	sigprocmask(SIG_SETMASK, &mask, 0);
143 	if (_ss_pager_name == (char *)NULL) {
144 		if ((_ss_pager_name = ss_safe_getenv("PAGER")) == (char *)NULL)
145 			_ss_pager_name = MORE;
146 	}
147 	(void) execlp(_ss_pager_name, _ss_pager_name, (char *) NULL);
148 	{
149 		/* minimal recovery if pager program isn't found */
150 		char buf[80];
151 		register int n;
152 		while ((n = read(0, buf, 80)) > 0)
153 			write_all(1, buf, n);
154 	}
155 	exit(errno);
156 }
157