2009-03-04 15:54:55 +03:00
/*
2009-03-06 12:48:46 +03:00
* switchroot . c - switch to new root directory and start init .
2009-03-04 15:54:55 +03:00
*
2009-06-19 14:09:14 +04:00
* Copyright 2002 - 2009 Red Hat , Inc . All rights reserved .
2009-03-04 15:54:55 +03:00
*
* This program 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 .
*
* This program 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 this program . If not , see < http : //www.gnu.org/licenses/>.
*
2009-03-06 12:48:46 +03:00
* Authors :
2009-06-19 14:09:14 +04:00
* Peter Jones < pjones @ redhat . com >
2009-03-06 12:48:46 +03:00
* Jeremy Katz < katzj @ redhat . com >
2009-03-04 15:54:55 +03:00
*/
# include <sys/mount.h>
# include <sys/types.h>
# include <sys/stat.h>
2009-03-06 13:36:32 +03:00
# include <sys/param.h>
2009-03-06 12:48:46 +03:00
# include <fcntl.h>
2009-03-04 15:54:55 +03:00
# include <stdio.h>
2009-03-06 12:48:46 +03:00
# include <stdlib.h>
# include <unistd.h>
2009-03-04 15:54:55 +03:00
# include <string.h>
# include <errno.h>
# include <ctype.h>
2009-03-08 17:15:18 +03:00
# include <dirent.h>
2009-06-19 14:09:14 +04:00
# include <err.h>
2009-03-04 15:54:55 +03:00
2009-03-06 12:48:46 +03:00
# ifndef MS_MOVE
# define MS_MOVE 8192
2009-03-04 15:54:55 +03:00
# endif
2009-06-19 14:09:14 +04:00
/* remove all files/directories below dirName -- don't cross mountpoints */
static int recursiveRemove ( char * dirName )
{
struct stat rb ;
DIR * dir ;
int rc = - 1 ;
int dfd ;
if ( ! ( dir = opendir ( dirName ) ) ) {
warn ( " failed to open %s " , dirName ) ;
goto done ;
}
2009-03-04 15:54:55 +03:00
2009-06-19 14:09:14 +04:00
dfd = dirfd ( dir ) ;
2009-03-04 15:54:55 +03:00
2009-06-19 14:09:14 +04:00
if ( fstat ( dfd , & rb ) ) {
warn ( " failed to stat %s " , dirName ) ;
goto done ;
}
while ( 1 ) {
struct dirent * d ;
errno = 0 ;
if ( ! ( d = readdir ( dir ) ) ) {
if ( errno ) {
warn ( " failed to read %s " , dirName ) ;
goto done ;
}
break ; /* end of directory */
}
if ( ! strcmp ( d - > d_name , " . " ) | | ! strcmp ( d - > d_name , " .. " ) )
continue ;
if ( d - > d_type = = DT_DIR ) {
struct stat sb ;
if ( fstatat ( dfd , d - > d_name , & sb , AT_SYMLINK_NOFOLLOW ) ) {
warn ( " failed to stat %s/%s " , dirName , d - > d_name ) ;
continue ;
}
/* remove subdirectories if device is same as dir */
if ( sb . st_dev = = rb . st_dev ) {
char subdir [ strlen ( dirName ) +
strlen ( d - > d_name ) + 2 ] ;
sprintf ( subdir , " %s/%s " , dirName , d - > d_name ) ;
recursiveRemove ( subdir ) ;
} else
continue ;
}
if ( unlinkat ( dfd , d - > d_name ,
d - > d_type = = DT_DIR ? AT_REMOVEDIR : 0 ) )
warn ( " failed to unlink %s/%s " , dirName , d - > d_name ) ;
}
rc = 0 ; /* success */
done :
if ( dir )
closedir ( dir ) ;
return rc ;
}
2009-03-08 17:15:18 +03:00
2009-03-07 07:32:37 +03:00
static int switchroot ( const char * newroot )
2009-03-04 15:54:55 +03:00
{
2009-03-06 12:48:46 +03:00
/* Don't try to unmount the old "/", there's no way to do it. */
const char * umounts [ ] = { " /dev " , " /proc " , " /sys " , NULL } ;
int i ;
for ( i = 0 ; umounts [ i ] ! = NULL ; i + + ) {
2009-03-06 13:36:32 +03:00
char newmount [ PATH_MAX ] ;
2009-06-19 14:09:14 +04:00
snprintf ( newmount , sizeof ( newmount ) , " %s%s " , newroot , umounts [ i ] ) ;
2009-03-06 13:36:32 +03:00
if ( mount ( umounts [ i ] , newmount , NULL , MS_MOVE , NULL ) < 0 ) {
2009-06-19 14:09:14 +04:00
warn ( " failed to mount moving %s to %s " ,
2009-03-06 13:36:32 +03:00
umounts [ i ] , newmount ) ;
2009-06-19 14:09:14 +04:00
warnx ( " forcing unmount of %s " , umounts [ i ] ) ;
2009-03-06 12:48:46 +03:00
umount2 ( umounts [ i ] , MNT_FORCE ) ;
}
}
2009-06-19 14:09:14 +04:00
if ( chdir ( newroot ) ) {
warn ( " failed to change directory to %s " , newroot ) ;
return - 1 ;
2009-03-07 07:32:37 +03:00
}
2009-06-19 14:09:14 +04:00
2009-03-08 17:15:18 +03:00
recursiveRemove ( " / " ) ;
2009-06-19 14:09:14 +04:00
2009-03-06 12:48:46 +03:00
if ( mount ( newroot , " / " , NULL , MS_MOVE , NULL ) < 0 ) {
2009-06-19 14:09:14 +04:00
warn ( " failed to mount moving %s to / " , newroot ) ;
2009-03-07 07:32:37 +03:00
return - 1 ;
2009-03-06 12:48:46 +03:00
}
if ( chroot ( " . " ) ) {
2009-06-19 14:09:14 +04:00
warn ( " failed to change root " ) ;
return - 1 ;
2009-03-06 12:48:46 +03:00
}
2009-06-19 14:09:14 +04:00
return 0 ;
2009-03-04 15:54:55 +03:00
}
2009-03-06 12:48:46 +03:00
static void usage ( FILE * output )
2009-03-04 15:54:55 +03:00
{
2009-06-19 14:09:14 +04:00
fprintf ( output , " usage: %s <newrootdir> <init> <args to init> \n " ,
program_invocation_short_name ) ;
exit ( output = = stderr ? EXIT_FAILURE : EXIT_SUCCESS ) ;
}
static void version ( void )
{
fprintf ( stdout , " %s from %s \n " , program_invocation_short_name ,
PACKAGE_STRING ) ;
exit ( EXIT_SUCCESS ) ;
2009-03-04 15:54:55 +03:00
}
2009-03-06 12:48:46 +03:00
int main ( int argc , char * argv [ ] )
2009-03-04 15:54:55 +03:00
{
2009-06-19 14:09:14 +04:00
char * newroot , * init , * * initargs ;
2009-05-18 15:57:51 +04:00
2009-06-19 14:09:14 +04:00
if ( argv [ 1 ] & & ( ! strcmp ( argv [ 1 ] , " --help " ) | | ! strcmp ( argv [ 1 ] , " -h " ) ) )
usage ( stdout ) ;
if ( argv [ 1 ] & & ( ! strcmp ( argv [ 1 ] , " --version " ) | | ! strcmp ( argv [ 1 ] , " -V " ) ) )
version ( ) ;
if ( argc < 3 )
2009-03-06 12:48:46 +03:00
usage ( stderr ) ;
2009-06-19 14:09:14 +04:00
newroot = argv [ 1 ] ;
init = argv [ 2 ] ;
initargs = & argv [ 2 ] ;
if ( ! * newroot | | ! * init )
usage ( stderr ) ;
if ( switchroot ( newroot ) )
errx ( EXIT_FAILURE , " failed. Sorry. " ) ;
if ( access ( init , X_OK ) )
warn ( " cannot access %s " , init ) ;
2009-05-18 15:57:51 +04:00
/* get session leader */
setsid ( ) ;
2009-06-19 14:09:14 +04:00
2009-05-18 15:57:51 +04:00
/* set controlling terminal */
2009-06-19 14:09:14 +04:00
if ( ioctl ( 0 , TIOCSCTTY , 1 ) )
warn ( " failed to TIOCSCTTY " ) ;
2009-05-18 15:57:51 +04:00
2009-06-19 14:09:14 +04:00
execv ( init , initargs ) ;
err ( EXIT_FAILURE , " failed to execute %s " , init ) ;
2009-03-04 15:54:55 +03:00
}
2009-03-06 12:48:46 +03:00