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 ) {
2014-11-28 21:29:59 +03:00
log_error_errno ( errno , " Internal pager failed: %m " ) ;
2011-09-27 01:02:56 +04:00
_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 ( ) ;
2014-11-28 21:57:32 +03:00
if ( pipe ( fd ) < 0 )
return log_error_errno ( errno , " Failed to create pager pipe: %m " ) ;
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 ;
2014-11-28 21:29:59 +03:00
log_error_errno ( errno , " Failed to fork pager: %m " ) ;
2014-03-24 06:22:44 +04:00
safe_close_pair ( 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 ) {
2014-02-12 06:30:10 +04:00
const char * less_opts ;
2011-07-07 04:34:35 +04:00
dup2 ( fd [ 0 ] , STDIN_FILENO ) ;
2014-03-24 06:22:44 +04:00
safe_close_pair ( fd ) ;
2011-07-07 04:34:35 +04:00
2014-02-12 06:30:10 +04:00
less_opts = getenv ( " SYSTEMD_LESS " ) ;
if ( ! less_opts )
less_opts = " FRSXMK " ;
2013-03-07 23:44:35 +04:00
if ( jump_to_end )
2014-02-12 06:30:10 +04:00
less_opts = strappenda ( less_opts , " +G " ) ;
setenv ( " LESS " , less_opts , 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 */
2014-11-28 21:57:32 +03:00
if ( dup2 ( fd [ 1 ] , STDOUT_FILENO ) < 0 )
return log_error_errno ( errno , " Failed to duplicate pager pipe: %m " ) ;
2011-07-07 04:34:35 +04:00
2014-03-24 06:22:44 +04:00
safe_close_pair ( 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 ) ;
2014-11-09 17:51:04 +03:00
( void ) wait_for_terminate ( pager_pid , NULL ) ;
2011-07-07 04:34:35 +04:00
pager_pid = 0 ;
}
2012-10-19 01:34:37 +04:00
bool pager_have ( void ) {
return pager_pid > 0 ;
}
2014-07-21 02:23:53 +04:00
int show_man_page ( const char * desc , bool null_stdio ) {
const char * args [ 4 ] = { " man " , NULL , NULL , NULL } ;
char * e = NULL ;
pid_t pid ;
size_t k ;
int r ;
siginfo_t status ;
k = strlen ( desc ) ;
if ( desc [ k - 1 ] = = ' ) ' )
e = strrchr ( desc , ' ( ' ) ;
if ( e ) {
char * page = NULL , * section = NULL ;
page = strndupa ( desc , e - desc ) ;
section = strndupa ( e + 1 , desc + k - e - 2 ) ;
args [ 1 ] = section ;
args [ 2 ] = page ;
} else
args [ 1 ] = desc ;
pid = fork ( ) ;
2014-11-28 21:57:32 +03:00
if ( pid < 0 )
return log_error_errno ( errno , " Failed to fork: %m " ) ;
2014-07-21 02:23:53 +04:00
if ( pid = = 0 ) {
/* Child */
if ( null_stdio ) {
r = make_null_stdio ( ) ;
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to kill stdio: %m " ) ;
2014-07-21 02:23:53 +04:00
_exit ( EXIT_FAILURE ) ;
}
}
execvp ( args [ 0 ] , ( char * * ) args ) ;
2014-11-28 21:29:59 +03:00
log_error_errno ( errno , " Failed to execute man: %m " ) ;
2014-07-21 02:23:53 +04:00
_exit ( EXIT_FAILURE ) ;
}
r = wait_for_terminate ( pid , & status ) ;
if ( r < 0 )
return r ;
log_debug ( " Exit code %i status %i " , status . si_code , status . si_status ) ;
return status . si_status ;
}