2011-07-07 04:34:35 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
2012-04-12 02:20:58 +04:00
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
2011-07-07 04:34:35 +04:00
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
2012-04-12 02:20:58 +04:00
Lesser General Public License for more details .
2011-07-07 04:34:35 +04:00
2012-04-12 02:20:58 +04:00
You should have received a copy of the GNU Lesser General Public License
2011-07-07 04:34:35 +04:00
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <sys/types.h>
2011-09-27 01:02:56 +04:00
# include <fcntl.h>
2011-07-07 04:34:35 +04:00
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <sys/prctl.h>
# include "pager.h"
# include "util.h"
# include "macro.h"
static pid_t pager_pid = 0 ;
2013-12-16 20:53:53 +04:00
noreturn static void pager_fallback ( void ) {
2011-09-27 01:02:56 +04:00
ssize_t n ;
2013-11-08 00:06:44 +04:00
2011-09-27 01:02:56 +04:00
do {
n = splice ( STDIN_FILENO , NULL , STDOUT_FILENO , NULL , 64 * 1024 , 0 ) ;
} while ( n > 0 ) ;
2013-11-08 00:06:44 +04:00
2011-09-27 01:02:56 +04:00
if ( n < 0 ) {
log_error ( " Internal pager failed: %m " ) ;
_exit ( EXIT_FAILURE ) ;
}
2013-11-08 00:06:44 +04:00
2011-09-27 01:02:56 +04:00
_exit ( EXIT_SUCCESS ) ;
}
2013-03-07 23:44:35 +04:00
int pager_open ( bool jump_to_end ) {
2011-07-07 04:34:35 +04:00
int fd [ 2 ] ;
const char * pager ;
pid_t parent_pid ;
2012-10-19 01:34:37 +04:00
int r ;
2011-07-07 04:34:35 +04:00
if ( pager_pid > 0 )
2012-10-19 01:34:37 +04:00
return 1 ;
2011-07-07 04:34:35 +04:00
if ( ( pager = getenv ( " SYSTEMD_PAGER " ) ) | | ( pager = getenv ( " PAGER " ) ) )
if ( ! * pager | | streq ( pager , " cat " ) )
2012-10-19 01:34:37 +04:00
return 0 ;
2011-07-07 04:34:35 +04:00
2012-10-19 01:59:41 +04:00
if ( ! on_tty ( ) )
2012-10-19 01:34:37 +04:00
return 0 ;
2011-07-25 06:58:02 +04:00
2011-07-07 04:34:35 +04:00
/* Determine and cache number of columns before we spawn the
* pager so that we get the value from the actual tty */
columns ( ) ;
if ( pipe ( fd ) < 0 ) {
log_error ( " Failed to create pager pipe: %m " ) ;
2012-10-19 01:34:37 +04:00
return - errno ;
2011-07-07 04:34:35 +04:00
}
parent_pid = getpid ( ) ;
pager_pid = fork ( ) ;
if ( pager_pid < 0 ) {
2012-10-19 01:34:37 +04:00
r = - errno ;
2011-07-07 04:34:35 +04:00
log_error ( " Failed to fork pager: %m " ) ;
close_pipe ( fd ) ;
2012-10-19 01:34:37 +04:00
return r ;
2011-07-07 04:34:35 +04:00
}
/* In the child start the pager */
if ( pager_pid = = 0 ) {
dup2 ( fd [ 0 ] , STDIN_FILENO ) ;
close_pipe ( fd ) ;
2013-03-07 23:44:35 +04:00
if ( jump_to_end )
2013-03-08 00:42:25 +04:00
setenv ( " LESS " , " FRSXMK+G " , 1 ) ;
2013-03-07 23:44:35 +04:00
else
2013-03-08 00:42:25 +04:00
setenv ( " LESS " , " FRSXMK " , 1 ) ;
2011-07-07 04:34:35 +04:00
/* Make sure the pager goes away when the parent dies */
if ( prctl ( PR_SET_PDEATHSIG , SIGTERM ) < 0 )
_exit ( EXIT_FAILURE ) ;
/* Check whether our parent died before we were able
* to set the death signal */
if ( getppid ( ) ! = parent_pid )
_exit ( EXIT_SUCCESS ) ;
if ( pager ) {
execlp ( pager , pager , NULL ) ;
execl ( " /bin/sh " , " sh " , " -c " , pager , NULL ) ;
}
/* Debian's alternatives command for pagers is
* called ' pager ' . Note that we do not call
* sensible - pagers here , since that is just a
* shell script that implements a logic that
* is similar to this one anyway , but is
* Debian - specific . */
execlp ( " pager " , " pager " , NULL ) ;
execlp ( " less " , " less " , NULL ) ;
execlp ( " more " , " more " , NULL ) ;
2011-09-27 01:02:56 +04:00
pager_fallback ( ) ;
/* not reached */
2011-07-07 04:34:35 +04:00
}
/* Return in the parent */
2012-07-20 11:06:26 +04:00
if ( dup2 ( fd [ 1 ] , STDOUT_FILENO ) < 0 ) {
2011-07-07 04:34:35 +04:00
log_error ( " Failed to duplicate pager pipe: %m " ) ;
2012-10-19 01:34:37 +04:00
return - errno ;
2012-07-20 11:06:26 +04:00
}
2011-07-07 04:34:35 +04:00
close_pipe ( fd ) ;
2012-10-19 01:34:37 +04:00
return 1 ;
2011-07-07 04:34:35 +04:00
}
void pager_close ( void ) {
if ( pager_pid < = 0 )
return ;
/* Inform pager that we are done */
fclose ( stdout ) ;
kill ( pager_pid , SIGCONT ) ;
wait_for_terminate ( pager_pid , NULL ) ;
pager_pid = 0 ;
}
2012-10-19 01:34:37 +04:00
bool pager_have ( void ) {
return pager_pid > 0 ;
}