2010-09-23 17:01:41 +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
under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( 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
General Public License for more details .
You should have received a copy of the GNU General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <errno.h>
# include <inttypes.h>
# include <fcntl.h>
# include <linux/limits.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/select.h>
# include <sys/time.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
2010-09-25 17:39:38 +04:00
# include <getopt.h>
2010-09-26 17:50:14 +04:00
# include <sys/inotify.h>
2010-09-23 17:01:41 +04:00
# include "missing.h"
# include "util.h"
# include "set.h"
# include "sd-daemon.h"
# include "ioprio.h"
# include "readahead-common.h"
2010-09-25 17:39:38 +04:00
static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX ;
2010-09-28 23:46:14 +04:00
static ReadaheadShared * shared = NULL ;
2010-09-23 17:01:41 +04:00
static int unpack_file ( FILE * pack ) {
char fn [ PATH_MAX ] ;
int r = 0 , fd = - 1 ;
bool any = false ;
struct stat st ;
assert ( pack ) ;
if ( ! fgets ( fn , sizeof ( fn ) , pack ) )
return 0 ;
char_array_0 ( fn ) ;
truncate_nl ( fn ) ;
2011-01-05 01:42:58 +03:00
if ( ( fd = open ( fn , O_RDONLY | O_CLOEXEC | O_NOATIME | O_NOCTTY | O_NOFOLLOW ) ) < 0 ) {
if ( errno ! = ENOENT )
log_warning ( " open(%s) failed: %m " , fn ) ;
} else if ( file_verify ( fd , fn , arg_file_size_max , & st ) < = 0 ) {
2010-09-23 17:01:41 +04:00
close_nointr_nofail ( fd ) ;
fd = - 1 ;
}
for ( ; ; ) {
uint32_t b , c ;
if ( fread ( & b , sizeof ( b ) , 1 , pack ) ! = 1 | |
fread ( & c , sizeof ( c ) , 1 , pack ) ! = 1 ) {
log_error ( " Premature end of pack file. " ) ;
r = - EIO ;
goto finish ;
}
if ( b = = 0 & & c = = 0 )
break ;
if ( c < = b ) {
log_error ( " Invalid pack file. " ) ;
r = - EIO ;
goto finish ;
}
log_debug ( " %s: page %u to %u " , fn , b , c ) ;
any = true ;
if ( fd > = 0 )
2010-09-24 14:54:05 +04:00
if ( posix_fadvise ( fd , b * PAGE_SIZE , ( c - b ) * PAGE_SIZE , POSIX_FADV_WILLNEED ) < 0 ) {
log_warning ( " posix_fadvise() failed: %m " ) ;
2010-09-23 17:01:41 +04:00
goto finish ;
}
}
if ( ! any & & fd > = 0 ) {
/* if no range is encoded in the pack file this is
* intended to mean that the whole file shall be
* read */
2010-09-24 14:54:05 +04:00
if ( posix_fadvise ( fd , 0 , st . st_size , POSIX_FADV_WILLNEED ) < 0 ) {
log_warning ( " posix_fadvise() failed: %m " ) ;
2010-09-23 17:01:41 +04:00
goto finish ;
}
}
finish :
if ( fd > = 0 )
close_nointr_nofail ( fd ) ;
return r ;
}
static int replay ( const char * root ) {
2010-09-27 05:24:39 +04:00
FILE * pack = NULL ;
2010-09-23 17:01:41 +04:00
char line [ LINE_MAX ] ;
int r = 0 ;
char * pack_fn = NULL , c ;
2010-09-25 15:47:31 +04:00
bool on_ssd , ready = false ;
2010-09-23 17:01:41 +04:00
int prio ;
2010-09-26 17:50:14 +04:00
int inotify_fd = - 1 ;
2010-09-23 17:01:41 +04:00
assert ( root ) ;
2010-09-25 16:35:34 +04:00
write_one_line_file ( " /proc/self/oom_score_adj " , " 1000 " ) ;
2010-10-26 23:28:39 +04:00
bump_request_nr ( root ) ;
2010-09-25 16:35:34 +04:00
2010-09-23 17:01:41 +04:00
if ( asprintf ( & pack_fn , " %s/.readahead " , root ) < 0 ) {
log_error ( " Out of memory " ) ;
r = - ENOMEM ;
goto finish ;
}
if ( ( ! ( pack = fopen ( pack_fn , " re " ) ) ) ) {
2011-01-05 01:42:58 +03:00
if ( errno = = ENOENT )
2010-09-23 17:01:41 +04:00
log_debug ( " No pack file found. " ) ;
else {
log_error ( " Failed to open pack file: %m " ) ;
r = - errno ;
}
goto finish ;
}
2010-09-29 05:11:35 +04:00
posix_fadvise ( fileno ( pack ) , 0 , 0 , POSIX_FADV_WILLNEED ) ;
2010-09-26 17:50:14 +04:00
if ( ( inotify_fd = open_inotify ( ) ) < 0 ) {
r = inotify_fd ;
goto finish ;
}
2010-09-23 17:01:41 +04:00
if ( ! ( fgets ( line , sizeof ( line ) , pack ) ) ) {
log_error ( " Premature end of pack file. " ) ;
r = - EIO ;
goto finish ;
}
char_array_0 ( line ) ;
if ( ! streq ( line , CANONICAL_HOST " \n " ) ) {
log_debug ( " Pack file host type mismatch. " ) ;
goto finish ;
}
if ( ( c = getc ( pack ) ) = = EOF ) {
log_debug ( " Premature end of pack file. " ) ;
r = - EIO ;
goto finish ;
}
/* We do not retest SSD here, so that we can start replaying
* before udev is up . */
on_ssd = c = = ' S ' ;
log_debug ( " On SSD: %s " , yes_no ( on_ssd ) ) ;
if ( on_ssd )
prio = IOPRIO_PRIO_VALUE ( IOPRIO_CLASS_IDLE , 0 ) ;
else
prio = IOPRIO_PRIO_VALUE ( IOPRIO_CLASS_RT , 7 ) ;
if ( ioprio_set ( IOPRIO_WHO_PROCESS , getpid ( ) , prio ) < 0 )
log_warning ( " Failed to set IDLE IO priority class: %m " ) ;
2010-09-25 15:47:31 +04:00
sd_notify ( 0 , " STATUS=Replaying readahead data " ) ;
2010-09-23 17:01:41 +04:00
log_debug ( " Replaying... " ) ;
2010-09-26 17:50:14 +04:00
if ( access ( " /dev/.systemd/readahead/noreplay " , F_OK ) > = 0 ) {
log_debug ( " Got termination request " ) ;
goto done ;
}
2010-09-23 17:01:41 +04:00
while ( ! feof ( pack ) & & ! ferror ( pack ) ) {
2010-09-26 17:50:14 +04:00
uint8_t inotify_buffer [ sizeof ( struct inotify_event ) + FILENAME_MAX ] ;
2010-09-23 17:01:41 +04:00
int k ;
2010-09-26 17:50:14 +04:00
ssize_t n ;
if ( ( n = read ( inotify_fd , & inotify_buffer , sizeof ( inotify_buffer ) ) ) < 0 ) {
if ( errno ! = EINTR & & errno ! = EAGAIN ) {
log_error ( " Failed to read inotify event: %m " ) ;
r = - errno ;
goto finish ;
}
} else {
struct inotify_event * e = ( struct inotify_event * ) inotify_buffer ;
while ( n > 0 ) {
size_t step ;
if ( ( e - > mask & IN_CREATE ) & & streq ( e - > name , " noreplay " ) ) {
log_debug ( " Got termination request " ) ;
goto done ;
}
step = sizeof ( struct inotify_event ) + e - > len ;
assert ( step < = ( size_t ) n ) ;
e = ( struct inotify_event * ) ( ( uint8_t * ) e + step ) ;
n - = step ;
}
}
2010-09-23 17:01:41 +04:00
if ( ( k = unpack_file ( pack ) ) < 0 ) {
r = k ;
goto finish ;
}
2010-09-25 15:47:31 +04:00
if ( ! ready ) {
/* We delay the ready notification until we
* queued at least one read */
sd_notify ( 0 , " READY=1 " ) ;
ready = true ;
}
2010-09-23 17:01:41 +04:00
}
2010-09-26 17:50:14 +04:00
done :
2010-09-25 15:47:31 +04:00
if ( ! ready )
sd_notify ( 0 , " READY=1 " ) ;
2010-09-23 17:01:41 +04:00
if ( ferror ( pack ) ) {
log_error ( " Failed to read pack file. " ) ;
r = - EIO ;
goto finish ;
}
log_debug ( " Done. " ) ;
finish :
if ( pack )
fclose ( pack ) ;
2010-09-26 17:50:14 +04:00
if ( inotify_fd > = 0 )
close_nointr_nofail ( inotify_fd ) ;
2010-09-23 17:01:41 +04:00
free ( pack_fn ) ;
return r ;
}
2010-09-25 17:39:38 +04:00
static int help ( void ) {
printf ( " %s [OPTIONS...] [DIRECTORY] \n \n "
" Replay collected read-ahead data on early boot. \n \n "
" -h --help Show this help \n "
" --max-file-size=BYTES Maximum size of files to read ahead \n " ,
program_invocation_short_name ) ;
return 0 ;
}
static int parse_argv ( int argc , char * argv [ ] ) {
enum {
ARG_FILE_SIZE_MAX
} ;
static const struct option options [ ] = {
{ " help " , no_argument , NULL , ' h ' } ,
{ " file-size-max " , required_argument , NULL , ARG_FILE_SIZE_MAX } ,
{ NULL , 0 , NULL , 0 }
} ;
int c ;
assert ( argc > = 0 ) ;
assert ( argv ) ;
while ( ( c = getopt_long ( argc , argv , " h " , options , NULL ) ) > = 0 ) {
switch ( c ) {
case ' h ' :
help ( ) ;
return 0 ;
case ARG_FILE_SIZE_MAX : {
unsigned long long ull ;
if ( safe_atollu ( optarg , & ull ) < 0 | | ull < = 0 ) {
log_error ( " Failed to parse maximum file size %s. " , optarg ) ;
return - EINVAL ;
}
arg_file_size_max = ( off_t ) ull ;
break ;
}
case ' ? ' :
return - EINVAL ;
default :
log_error ( " Unknown option code %c " , c ) ;
return - EINVAL ;
}
}
if ( optind ! = argc & &
optind ! = argc - 1 ) {
help ( ) ;
return - EINVAL ;
}
return 1 ;
}
2010-09-23 17:01:41 +04:00
int main ( int argc , char * argv [ ] ) {
2010-09-25 17:39:38 +04:00
int r ;
2010-09-24 13:12:10 +04:00
2010-09-23 19:44:33 +04:00
log_set_target ( LOG_TARGET_SYSLOG_OR_KMSG ) ;
2010-09-23 17:01:41 +04:00
log_parse_environment ( ) ;
log_open ( ) ;
2010-09-25 17:39:38 +04:00
if ( ( r = parse_argv ( argc , argv ) ) < = 0 )
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS ;
2010-09-25 15:32:54 +04:00
if ( ! enough_ram ( ) ) {
log_info ( " Disabling readahead replay due to low memory. " ) ;
return 0 ;
}
2011-02-13 17:46:05 +03:00
if ( running_in_vm ( ) ) {
log_info ( " Disabling readahead replay due to execution in virtual machine. " ) ;
return 0 ;
}
2010-09-28 23:46:14 +04:00
if ( ! ( shared = shared_get ( ) ) )
return 1 ;
shared - > replay = getpid ( ) ;
__sync_synchronize ( ) ;
2010-09-25 17:39:38 +04:00
if ( replay ( optind < argc ? argv [ optind ] : " / " ) < 0 )
2010-09-23 17:01:41 +04:00
return 1 ;
return 0 ;
}