pager.c revision 1ad3174af5213fa7029944cc19723cda08f221d3
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#include "config.h"
18#if HAVE_SECURE_GETENV
19#define _GNU_SOURCE
20#endif
21#ifdef HAVE_UNISTD_H
22#include <unistd.h>
23#endif
24#ifdef HAVE_ERRNO_H
25#include <errno.h>
26#else
27extern int errno;
28#endif
29
30#include "ss_internal.h"
31#include <stdio.h>
32#include <sys/types.h>
33#include <sys/file.h>
34#include <signal.h>
35#ifdef HAVE_SYS_PRCTL_H
36#include <sys/prctl.h>
37#else
38#define PR_GET_DUMPABLE 3
39#endif
40#if (!defined(HAVE_PRCTL) && defined(linux))
41#include <sys/syscall.h>
42#endif
43
44static char MORE[] = "more";
45extern char *_ss_pager_name;
46extern char *getenv PROTOTYPE((const char *));
47
48char *ss_safe_getenv(const char *arg)
49{
50	if ((getuid() != geteuid()) || (getgid() != getegid()))
51		return NULL;
52#if HAVE_PRCTL
53	if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
54		return NULL;
55#else
56#if (defined(linux) && defined(SYS_prctl))
57	if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
58		return NULL;
59#endif
60#endif
61
62#if defined(HAVE_SECURE_GETENV)
63	return secure_getenv(arg);
64#elif defined(HAVE___SECURE_GETENV)
65	return __secure_getenv(arg);
66#else
67	return getenv(arg);
68#endif
69}
70
71/*
72 * this needs a *lot* of work....
73 *
74 * run in same process
75 * handle SIGINT sensibly
76 * allow finer control -- put-page-break-here
77 */
78
79#ifndef NO_FORK
80int ss_pager_create(void)
81{
82	int filedes[2];
83
84	if (pipe(filedes) != 0)
85		return(-1);
86
87	switch(fork()) {
88	case -1:
89		return(-1);
90	case 0:
91		/*
92		 * Child; dup read half to 0, close all but 0, 1, and 2
93		 */
94		if (dup2(filedes[0], 0) == -1)
95			exit(1);
96		ss_page_stdin();
97	default:
98		/*
99		 * Parent:  close "read" side of pipe, return
100		 * "write" side.
101		 */
102		(void) close(filedes[0]);
103		return(filedes[1]);
104	}
105}
106#else /* don't fork */
107int ss_pager_create()
108{
109    int fd;
110    fd = open("/dev/tty", O_WRONLY, 0);
111    return fd;
112}
113#endif
114
115static int write_all(int fd, char *buf, size_t count)
116{
117	ssize_t ret;
118	int c = 0;
119
120	while (count > 0) {
121		ret = write(fd, buf, count);
122		if (ret < 0) {
123			if ((errno == EAGAIN) || (errno == EINTR))
124				continue;
125			return -1;
126		}
127		count -= ret;
128		buf += ret;
129		c += ret;
130	}
131	return c;
132}
133
134void ss_page_stdin()
135{
136	int i;
137	sigset_t mask;
138
139	for (i = 3; i < 32; i++)
140		(void) close(i);
141	(void) signal(SIGINT, SIG_DFL);
142	sigprocmask(SIG_BLOCK, 0, &mask);
143	sigdelset(&mask, SIGINT);
144	sigprocmask(SIG_SETMASK, &mask, 0);
145	if (_ss_pager_name == (char *)NULL) {
146		if ((_ss_pager_name = ss_safe_getenv("PAGER")) == (char *)NULL)
147			_ss_pager_name = MORE;
148	}
149	(void) execlp(_ss_pager_name, _ss_pager_name, (char *) NULL);
150	{
151		/* minimal recovery if pager program isn't found */
152		char buf[80];
153		register int n;
154		while ((n = read(0, buf, 80)) > 0)
155			write_all(1, buf, n);
156	}
157	exit(errno);
158}
159