| /* |
| * Pager: Routines to create a "more" running out of a particular file |
| * descriptor. |
| * |
| * Copyright 1987, 1988 by MIT Student Information Processing Board |
| * |
| * Permission to use, copy, modify, and distribute this software and |
| * its documentation for any purpose is hereby granted, provided that |
| * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in |
| * advertising or publicity pertaining to distribution of the software |
| * without specific, written prior permission. M.I.T. and the |
| * M.I.T. S.I.P.B. make no representations about the suitability of |
| * this software for any purpose. It is provided "as is" without |
| * express or implied warranty. |
| */ |
| |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #ifdef HAVE_ERRNO_H |
| #include <errno.h> |
| #else |
| extern int errno; |
| #endif |
| |
| #include "ss_internal.h" |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/file.h> |
| #include <signal.h> |
| #ifdef HAVE_SYS_PRCTL_H |
| #include <sys/prctl.h> |
| #else |
| #define PR_GET_DUMPABLE 3 |
| #endif |
| #if (!defined(HAVE_PRCTL) && defined(linux)) |
| #include <sys/syscall.h> |
| #endif |
| |
| static char MORE[] = "more"; |
| extern char *_ss_pager_name; |
| extern char *getenv PROTOTYPE((const char *)); |
| |
| char *ss_safe_getenv(const char *arg) |
| { |
| if ((getuid() != geteuid()) || (getgid() != getegid())) |
| return NULL; |
| #if HAVE_PRCTL |
| if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) |
| return NULL; |
| #else |
| #if (defined(linux) && defined(SYS_prctl)) |
| if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) |
| return NULL; |
| #endif |
| #endif |
| |
| #ifdef HAVE___SECURE_GETENV |
| return __secure_getenv(arg); |
| #else |
| return getenv(arg); |
| #endif |
| } |
| |
| /* |
| * this needs a *lot* of work.... |
| * |
| * run in same process |
| * handle SIGINT sensibly |
| * allow finer control -- put-page-break-here |
| */ |
| |
| #ifndef NO_FORK |
| int ss_pager_create(void) |
| { |
| int filedes[2]; |
| |
| if (pipe(filedes) != 0) |
| return(-1); |
| |
| switch(fork()) { |
| case -1: |
| return(-1); |
| case 0: |
| /* |
| * Child; dup read half to 0, close all but 0, 1, and 2 |
| */ |
| if (dup2(filedes[0], 0) == -1) |
| exit(1); |
| ss_page_stdin(); |
| default: |
| /* |
| * Parent: close "read" side of pipe, return |
| * "write" side. |
| */ |
| (void) close(filedes[0]); |
| return(filedes[1]); |
| } |
| } |
| #else /* don't fork */ |
| int ss_pager_create() |
| { |
| int fd; |
| fd = open("/dev/tty", O_WRONLY, 0); |
| return fd; |
| } |
| #endif |
| |
| static int write_all(int fd, char *buf, size_t count) |
| { |
| ssize_t ret; |
| int c = 0; |
| |
| while (count > 0) { |
| ret = write(fd, buf, count); |
| if (ret < 0) { |
| if ((errno == EAGAIN) || (errno == EINTR)) |
| continue; |
| return -1; |
| } |
| count -= ret; |
| buf += ret; |
| c += ret; |
| } |
| return c; |
| } |
| |
| void ss_page_stdin() |
| { |
| int i; |
| sigset_t mask; |
| |
| for (i = 3; i < 32; i++) |
| (void) close(i); |
| (void) signal(SIGINT, SIG_DFL); |
| sigprocmask(SIG_BLOCK, 0, &mask); |
| sigdelset(&mask, SIGINT); |
| sigprocmask(SIG_SETMASK, &mask, 0); |
| if (_ss_pager_name == (char *)NULL) { |
| if ((_ss_pager_name = ss_safe_getenv("PAGER")) == (char *)NULL) |
| _ss_pager_name = MORE; |
| } |
| (void) execlp(_ss_pager_name, _ss_pager_name, (char *) NULL); |
| { |
| /* minimal recovery if pager program isn't found */ |
| char buf[80]; |
| register int n; |
| while ((n = read(0, buf, 80)) > 0) |
| write_all(1, buf, n); |
| } |
| exit(errno); |
| } |