2004-01-20 21:32:43 +03:00
/*
* Guillaume Cottenceau ( gc @ mandrakesoft . com )
*
* Copyright 2000 MandrakeSoft
*
* This software may be freely redistributed under the terms of the GNU
* public license .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
/*
* Portions from Erik Troan ( ewt @ redhat . com )
*
* Copyright 1996 Red Hat Software
*
*/
# ifndef INIT_HEADERS
# include "init-libc-headers.h"
# else
# include INIT_HEADERS
# endif
# include "config-stage1.h"
# if defined(__powerpc__)
# define TIOCSCTTY 0x540
# endif
char * env [ ] = {
2005-01-21 16:52:04 +03:00
" PATH=/usr/bin:/bin:/sbin:/usr/sbin:/usr/X11R6/bin " ,
" LD_LIBRARY_PATH=/lib:/usr/lib:/usr/X11R6/lib " ,
2004-01-20 21:32:43 +03:00
" HOME=/ " ,
" TERM=linux " ,
" TERMINFO=/etc/terminfo " ,
NULL
} ;
2005-01-21 16:52:04 +03:00
char * * myenv = NULL ;
2004-01-20 21:32:43 +03:00
/*
* this needs to handle the following cases :
*
2005-01-21 16:52:04 +03:00
* 1 ) run from a CD root filesystem
* 2 ) run from a read only nfs rooted filesystem
* 3 ) run from a floppy
* 4 ) run from a floppy that ' s been loaded into a ramdisk
2004-01-20 21:32:43 +03:00
*/
int klog_pid ;
2007-03-03 00:48:18 +03:00
void fatal_error ( const char * msg )
2004-01-20 21:32:43 +03:00
{
2004-03-19 20:02:37 +03:00
printf ( " FATAL ERROR IN INIT: %s : %s \n \n I can't recover from this, please reboot manually and send bugreport. \n " , msg , strerror ( errno ) ) ;
2004-01-20 21:32:43 +03:00
while ( 1 ) ;
}
void print_error ( char * msg )
{
printf ( " E: %s \n " , msg ) ;
}
void print_warning ( char * msg )
{
printf ( " W: %s \n " , msg ) ;
}
/* fork to:
* ( 1 ) watch / proc / kmsg and copy the stuff to / dev / tty4
* ( 2 ) listens to / dev / log and copy also this stuff ( log from programs )
*/
void doklog ( )
{
2007-03-03 00:48:18 +03:00
int in , out , i , ii ;
2004-01-20 21:32:43 +03:00
int log ;
char buf [ 1024 ] ;
/* open kernel message logger */
in = open ( " /proc/kmsg " , O_RDONLY , 0 ) ;
if ( in < 0 ) {
print_error ( " could not open /proc/kmsg " ) ;
return ;
}
if ( ( log = open ( " /tmp/syslog " , O_WRONLY | O_CREAT , 0644 ) ) < 0 ) {
2004-03-19 20:02:37 +03:00
perror ( " /tmp/syslog " ) ;
2004-01-20 21:32:43 +03:00
print_error ( " error opening /tmp/syslog " ) ;
sleep ( 5 ) ;
return ;
}
if ( ( klog_pid = fork ( ) ) ) {
close ( in ) ;
close ( log ) ;
return ;
} else {
close ( 0 ) ;
close ( 1 ) ;
close ( 2 ) ;
}
out = open ( " /dev/tty4 " , O_WRONLY , 0 ) ;
2004-03-19 20:02:37 +03:00
if ( out < 0 ) {
perror ( " can't open /dev/tty4 " ) ;
2004-01-20 21:32:43 +03:00
print_warning ( " couldn't open tty for syslog -- still using /tmp/syslog \n " ) ;
2004-03-19 20:02:37 +03:00
}
2004-01-20 21:32:43 +03:00
/* disable on-console syslog output */
syslog ( 8 , NULL , 1 ) ;
while ( 1 ) {
2004-11-30 21:12:23 +03:00
i = read ( in , buf , sizeof ( buf ) ) ;
if ( i > 0 ) {
if ( out > = 0 )
2007-03-03 00:48:18 +03:00
ii = write ( out , buf , i ) ;
ii = write ( log , buf , i ) ;
2004-01-20 21:32:43 +03:00
}
}
}
2005-01-21 16:52:04 +03:00
void grab_env ( int fd )
{
char buf [ PIPE_BUF ] ;
char * p = buf ;
char * * ep ;
int i ;
if ( ( i = read ( fd , buf , sizeof ( buf ) ) ) < 0 ) {
print_error ( " failed to read env from pipe " ) ;
myenv = env ;
return ;
}
close ( fd ) ;
buf [ i ] = ' \0 ' ;
if ( ( ep = myenv = malloc ( sizeof ( char * ) * 32 ) ) = = NULL ) {
print_error ( " can't malloc env " ) ;
myenv = env ;
return ;
}
do {
* ep + + = p ;
p + = strlen ( p ) ;
} while ( + + p < buf + i ) ;
* ep = NULL ;
}
2004-01-20 21:32:43 +03:00
# define LOOP_CLR_FD 0x4C01
void del_loop ( char * device )
{
int fd ;
if ( ( fd = open ( device , O_RDONLY , 0 ) ) < 0 ) {
printf ( " del_loop open failed \n " ) ;
return ;
}
if ( ioctl ( fd , LOOP_CLR_FD , 0 ) < 0 ) {
printf ( " del_loop ioctl failed " ) ;
return ;
}
close ( fd ) ;
}
2004-11-30 17:44:10 +03:00
/*
initramfs cleaner
*/
static int nuke ( const char * what ) ;
static int nuke_dirent ( int len , const char * dir , const char * name , dev_t me )
{
int bytes = len + strlen ( name ) + 2 ;
char path [ bytes ] ;
int xlen ;
struct stat st ;
xlen = snprintf ( path , bytes , " %s/%s " , dir , name ) ;
/* assert(xlen < bytes); */
if ( lstat ( path , & st ) )
return ENOENT ; /* Return 0 since already gone? */
if ( st . st_dev ! = me )
return 0 ; /* DO NOT recurse down mount points!!!!! */
return nuke ( path ) ;
}
/* Wipe the contents of a directory, but not the directory itself */
static int nuke_dir ( const char * what )
{
int len = strlen ( what ) ;
DIR * dir ;
struct dirent * d ;
int err = 0 ;
struct stat st ;
if ( lstat ( what , & st ) )
return errno ;
if ( ! S_ISDIR ( st . st_mode ) )
return ENOTDIR ;
if ( ! ( dir = opendir ( what ) ) ) {
/* EACCES means we can't read it. Might be empty and removable;
if not , the rmdir ( ) in nuke ( ) will trigger an error . */
return ( errno = = EACCES ) ? 0 : errno ;
}
while ( ( d = readdir ( dir ) ) ) {
/* Skip . and .. */
if ( d - > d_name [ 0 ] = = ' . ' & &
( d - > d_name [ 1 ] = = ' \0 ' | |
( d - > d_name [ 1 ] = = ' . ' & & d - > d_name [ 2 ] = = ' \0 ' ) ) )
continue ;
err = nuke_dirent ( len , what , d - > d_name , st . st_dev ) ;
if ( err ) {
closedir ( dir ) ;
return err ;
}
}
closedir ( dir ) ;
return 0 ;
}
static int nuke ( const char * what )
{
int rv ;
int err = 0 ;
rv = unlink ( what ) ;
if ( rv < 0 ) {
if ( errno = = EISDIR ) {
/* It's a directory. */
err = nuke_dir ( what ) ;
if ( ! err ) err = rmdir ( what ) ? errno : err ;
} else {
err = errno ;
}
}
if ( err ) {
errno = err ;
fatal_error ( what ) ;
}
2007-03-03 00:48:18 +03:00
return 0 ;
2004-11-30 17:44:10 +03:00
}
2004-01-20 21:32:43 +03:00
struct filesystem
{
char * dev ;
char * name ;
char * fs ;
int mounted ;
} ;
/* attempt to unmount all filesystems in /proc/mounts */
void unmount_filesystems ( void )
{
int fd , size ;
char buf [ 65535 ] ; /* this should be big enough */
char * p ;
struct filesystem fs [ 500 ] ;
int numfs = 0 ;
int i , nb ;
printf ( " unmounting filesystems... \n " ) ;
fd = open ( " /proc/mounts " , O_RDONLY , 0 ) ;
if ( fd < 1 ) {
print_error ( " failed to open /proc/mounts " ) ;
sleep ( 2 ) ;
return ;
}
size = read ( fd , buf , sizeof ( buf ) - 1 ) ;
buf [ size ] = ' \0 ' ;
close ( fd ) ;
p = buf ;
while ( * p ) {
fs [ numfs ] . mounted = 1 ;
fs [ numfs ] . dev = p ;
while ( * p ! = ' ' ) p + + ;
* p + + = ' \0 ' ;
fs [ numfs ] . name = p ;
while ( * p ! = ' ' ) p + + ;
* p + + = ' \0 ' ;
fs [ numfs ] . fs = p ;
while ( * p ! = ' ' ) p + + ;
* p + + = ' \0 ' ;
while ( * p ! = ' \n ' ) p + + ;
p + + ;
if ( strcmp ( fs [ numfs ] . name , " / " ) ! = 0 ) numfs + + ; /* skip if root, no need to take initrd root in account */
}
/* Pixel's ultra-optimized sorting algorithm:
multiple passes trying to umount everything until nothing moves
anymore ( a . k . a holy shotgun method ) */
do {
nb = 0 ;
for ( i = 0 ; i < numfs ; i + + ) {
/*printf("trying with %s\n", fs[i].name);*/
if ( fs [ i ] . mounted & & umount ( fs [ i ] . name ) = = 0 ) {
if ( strncmp ( fs [ i ] . dev + sizeof ( " /dev/ " ) - 1 , " loop " ,
sizeof ( " loop " ) - 1 ) = = 0 )
del_loop ( fs [ i ] . dev ) ;
printf ( " \t %s \n " , fs [ i ] . name ) ;
fs [ i ] . mounted = 0 ;
nb + + ;
}
}
} while ( nb ) ;
for ( i = nb = 0 ; i < numfs ; i + + )
if ( fs [ i ] . mounted ) {
printf ( " \t %s umount failed \n " , fs [ i ] . name ) ;
if ( strcmp ( fs [ i ] . fs , " ext2 " ) = = 0 ) nb + + ; /* don't count not-ext2 umount failed */
}
if ( nb ) {
printf ( " failed to umount some filesystems \n " ) ;
while ( 1 ) ;
}
}
2004-11-22 16:47:29 +03:00
# define MAJOR(dev) ((dev)>>8)
# define MINOR(dev) ((dev) & 0xff)
# define MKDEV(ma,mi) ((ma)<<8 | (mi))
2004-01-20 21:32:43 +03:00
# define DEV_PERM 00400|00200|00040|00020
# define CHR_DEV 0020000|DEV_PERM
# define BLK_DEV 0060000|DEV_PERM
2004-11-30 17:44:10 +03:00
# define RAMFS_MAGIC 0x858458f6
# define TMPFS_MAGIC 0x01021994
2004-01-20 21:32:43 +03:00
int main ( int argc , char * * argv )
{
2004-11-30 17:44:10 +03:00
struct stat rst , cst , ist ;
struct statfs sfs ;
2004-01-20 21:32:43 +03:00
pid_t installpid , childpid ;
int wait_status ;
int fd ;
2005-01-21 16:52:04 +03:00
int fds [ 2 ] ;
2004-11-30 17:44:10 +03:00
int end_stage1 = 0 ;
2004-11-22 16:47:29 +03:00
2005-01-21 16:52:04 +03:00
if ( mount ( " /proc " , " /proc " , " proc " , 0 , NULL ) )
fatal_error ( " Unable to mount proc filesystem " ) ;
if ( mount ( " /sysfs " , " /sys " , " sysfs " , 0 , NULL ) )
fatal_error ( " Unable to mount sysfs filesystem " ) ;
2004-01-20 21:32:43 +03:00
/* ignore Control-C and keyboard stop signals */
signal ( SIGINT , SIG_IGN ) ;
signal ( SIGTSTP , SIG_IGN ) ;
2005-01-21 16:52:04 +03:00
fd = open ( " /dev/console " , O_RDWR , 0 ) ;
if ( fd < 0 )
fatal_error ( " failed to open /dev/console " ) ;
2004-01-20 21:32:43 +03:00
2005-01-21 16:52:04 +03:00
dup2 ( fd , 0 ) ;
dup2 ( fd , 1 ) ;
dup2 ( fd , 2 ) ;
close ( fd ) ;
2004-01-20 21:32:43 +03:00
/* I set me up as session leader (probably not necessary?) */
setsid ( ) ;
2004-03-19 20:02:37 +03:00
if ( ioctl ( 0 , TIOCSCTTY , NULL ) ) {
perror ( " TIOCSCTTY " ) ;
2004-01-20 21:32:43 +03:00
print_error ( " could not set new controlling tty " ) ;
2004-03-19 20:02:37 +03:00
}
2004-01-20 21:32:43 +03:00
2005-01-21 16:52:04 +03:00
char my_hostname [ ] = " localhost.localdomain " ;
2007-03-03 00:48:18 +03:00
if ( sethostname ( my_hostname , sizeof ( my_hostname ) ) < 0 )
print_error ( " could not set hostname " ) ;
2005-01-21 16:52:04 +03:00
/* the default domainname (as of 2.0.35) is "(none)", which confuses glibc */
2007-03-03 00:48:18 +03:00
if ( setdomainname ( " " , 0 ) < 0 )
print_error ( " could not set domainname " ) ;
2004-01-20 21:32:43 +03:00
2005-01-21 16:52:04 +03:00
doklog ( ) ;
2004-01-20 21:32:43 +03:00
/* Go into normal init mode - keep going, and then do a orderly shutdown
when :
1 ) install exits
2 ) we receive a SIGHUP
*/
2004-04-14 22:23:20 +04:00
printf ( " Running stage1... \n " ) ;
2005-01-21 16:52:04 +03:00
/* create a pipe for env passing */
2007-03-03 00:48:18 +03:00
if ( pipe ( fds ) < 0 )
fatal_error ( " failed to create env pipe " ) ;
2005-01-21 16:52:04 +03:00
fcntl ( fds [ 0 ] , F_SETFD , 1 ) ;
fcntl ( fds [ 1 ] , F_SETFD , 0 ) ;
2004-01-20 21:32:43 +03:00
if ( ! ( installpid = fork ( ) ) ) {
/* child */
char * child_argv [ 2 ] ;
child_argv [ 0 ] = " /sbin/stage1 " ;
child_argv [ 1 ] = NULL ;
execve ( child_argv [ 0 ] , child_argv , env ) ;
printf ( " error in exec of stage1 :-( \n " ) ;
return 0 ;
}
2005-01-21 16:52:04 +03:00
close ( fds [ 1 ] ) ;
2004-11-30 17:44:10 +03:00
while ( ! end_stage1 ) {
2004-01-20 21:32:43 +03:00
childpid = wait4 ( - 1 , & wait_status , 0 , NULL ) ;
if ( childpid = = installpid )
2004-11-30 17:44:10 +03:00
end_stage1 = 1 ;
2004-01-20 21:32:43 +03:00
}
2004-11-30 17:44:10 +03:00
if ( ! ( WIFEXITED ( wait_status ) ) ) {
/* something went wrong */
2004-01-20 21:32:43 +03:00
printf ( " wait_status: %i, install exited abnormally :-( " , wait_status ) ;
if ( WIFSIGNALED ( wait_status ) )
printf ( " -- received signal %d " , WTERMSIG ( wait_status ) ) ;
printf ( " \n " ) ;
2004-11-30 17:44:10 +03:00
sync ( ) ; sync ( ) ;
printf ( " sending termination signals... " ) ;
kill ( - 1 , 15 ) ;
2004-01-20 21:32:43 +03:00
sleep ( 2 ) ;
2004-11-30 17:44:10 +03:00
printf ( " done \n " ) ;
printf ( " sending kill signals... " ) ;
kill ( - 1 , 9 ) ;
sleep ( 2 ) ;
printf ( " done \n " ) ;
unmount_filesystems ( ) ;
2004-01-20 21:32:43 +03:00
printf ( " you may safely reboot your system \n " ) ;
while ( 1 ) ;
}
2005-01-21 16:52:04 +03:00
grab_env ( fds [ 0 ] ) ;
2004-11-30 17:44:10 +03:00
kill ( klog_pid , 9 ) ;
2005-01-21 16:52:04 +03:00
waitpid ( klog_pid , & wait_status , 0 ) ;
2004-12-01 18:10:51 +03:00
printf ( " Spawning init ... " ) ;
2005-01-21 16:52:04 +03:00
2004-11-30 17:44:10 +03:00
/* rest was seamlessy stolen from klibc */
/* First, change to the new root directory */
if ( chdir ( STAGE2_LOCATION ) )
fatal_error ( " chdir to new root " ) ;
/* This is a potentially highly destructive program. Take some
extra precautions . */
/* Make sure the current directory is not on the same filesystem
as the root directory */
if ( stat ( " / " , & rst ) | | stat ( " . " , & cst ) )
fatal_error ( " stat " ) ;
2005-01-24 21:12:06 +03:00
2004-11-30 17:44:10 +03:00
if ( rst . st_dev = = cst . st_dev )
fatal_error ( " current directory on the same filesystem as the root " ) ;
2005-01-24 21:12:06 +03:00
2004-11-30 17:44:10 +03:00
/* The initramfs should have /init */
if ( stat ( " /init " , & ist ) | | ! S_ISREG ( ist . st_mode ) )
fatal_error ( " can't find /init on initramfs " ) ;
2005-01-24 21:12:06 +03:00
2004-11-30 17:44:10 +03:00
/* Make sure we're on a ramfs */
if ( statfs ( " / " , & sfs ) )
fatal_error ( " statfs / " ) ;
if ( sfs . f_type ! = RAMFS_MAGIC & & sfs . f_type ! = TMPFS_MAGIC )
fatal_error ( " rootfs not a ramfs or tmpfs " ) ;
2005-01-24 21:12:06 +03:00
2004-11-30 17:44:10 +03:00
/* Okay, I think we should be safe... */
2005-01-24 21:12:06 +03:00
/* overmount image under new root if needed */
if ( statfs ( IMAGE_LOCATION , & sfs ) )
fatal_error ( " statfs " IMAGE_LOCATION ) ;
/* if something is mounted under IMAGE_LOCATION ? */
2005-01-26 16:43:53 +03:00
if ( sfs . f_type ! = RAMFS_MAGIC & & sfs . f_type ! = TMPFS_MAGIC ) {
2005-01-24 21:12:06 +03:00
if ( mount ( IMAGE_LOCATION , " . " IMAGE_LOCATION , NULL , MS_MOVE , NULL ) )
fatal_error ( " overmounting image location " ) ;
2005-01-26 16:43:53 +03:00
/* test for nested mount: disk or nfs with iso image */
if ( statfs ( IMAGE_LOCATION , & sfs ) )
fatal_error ( " nested statfs " IMAGE_LOCATION ) ;
if ( sfs . f_type ! = RAMFS_MAGIC & & sfs . f_type ! = TMPFS_MAGIC )
if ( mount ( IMAGE_LOCATION , " . " IMAGE_LOCATION " /isolinux " , NULL , MS_MOVE , NULL ) )
fatal_error ( " overmounting nested image location " ) ;
}
2004-11-30 19:11:41 +03:00
2004-11-30 17:44:10 +03:00
umount ( " /sys " ) ;
2004-12-01 18:10:51 +03:00
umount ( " /proc/bus/usb " ) ;
2004-11-30 17:44:10 +03:00
umount ( " /proc " ) ;
2005-01-24 21:12:06 +03:00
2004-11-30 17:44:10 +03:00
/* Delete rootfs contents */
if ( nuke_dir ( " / " ) )
fatal_error ( " nuking initramfs contents " ) ;
2005-01-24 21:12:06 +03:00
2004-11-30 17:44:10 +03:00
/* Overmount the root */
if ( mount ( " . " , " / " , NULL , MS_MOVE , NULL ) )
fatal_error ( " overmounting root " ) ;
2005-01-24 21:12:06 +03:00
2004-11-30 17:44:10 +03:00
/* chroot, chdir */
2005-02-03 18:38:15 +03:00
if ( chroot ( " . " ) | | chdir ( " / " ) )
2004-11-30 17:44:10 +03:00
fatal_error ( " chroot " ) ;
2005-01-24 21:12:06 +03:00
2004-11-30 17:44:10 +03:00
/* Open /dev/console */
2005-02-03 18:38:15 +03:00
if ( ( fd = open ( " /dev/console " , O_RDWR ) ) ! = - 1 ) {
dup2 ( fd , 0 ) ;
dup2 ( fd , 1 ) ;
dup2 ( fd , 2 ) ;
close ( fd ) ;
}
2005-01-24 21:12:06 +03:00
2005-01-19 16:31:08 +03:00
/* Check for given init */
2005-10-28 19:05:40 +04:00
if ( stat ( STAGE2_BINNAME , & ist ) | | ! S_ISREG ( ist . st_mode ) )
2005-01-19 16:31:08 +03:00
fatal_error ( " can't find init on root fs " ) ;
2005-01-24 21:12:06 +03:00
2005-01-26 16:43:53 +03:00
/* Spawn init */
printf ( " done. \n " ) ;
2005-10-28 19:05:40 +04:00
argv [ 0 ] = STAGE2_BINNAME ;
execve ( argv [ 0 ] , argv , myenv ) ;
2004-11-30 17:44:10 +03:00
fatal_error ( " stage2 " ) ; /* Failed to spawn init */
2005-01-21 16:52:04 +03:00
return 0 ;
2004-01-20 21:32:43 +03:00
}