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
*
*/
2008-02-29 17:37:40 +03:00
# include <errno.h>
# include <string.h>
# include <stdlib.h>
# include <stdio.h>
# include <unistd.h>
# include <dirent.h>
# include <fcntl.h>
# include <sys/klog.h>
# include <sys/mount.h>
# include <sys/stat.h>
# include <sys/vfs.h>
# include <sys/wait.h>
2004-01-20 21:32:43 +03:00
# include "config-stage1.h"
# if defined(__powerpc__)
# define TIOCSCTTY 0x540
# endif
2008-02-29 17:37:40 +03:00
# define MKDEV(ma,mi) ((ma)<<8 | (mi))
# define RAMFS_MAGIC 0x858458f6
# define TMPFS_MAGIC 0x01021994
2004-01-20 21:32:43 +03:00
char * env [ ] = {
2008-02-29 17:37:40 +03:00
" PATH=/usr/bin:/bin:/sbin:/usr/sbin " ,
" LD_LIBRARY_PATH=/lib:/usr/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
2008-02-29 17:37:40 +03:00
char * stage [ ] = { " /sbin/stage1 " , NULL } ;
char * udevd [ ] = { " /sbin/udevd " , NULL } ;
char * udevtrigger [ ] = { " /sbin/udevtrigger " , NULL } ;
char * udevsettle [ ] = { " /sbin/udevsettle " , 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
*/
2008-02-29 17:37:40 +03:00
void fatal ( const char * msg )
2004-01-20 21:32:43 +03:00
{
2008-02-29 17:37:40 +03:00
printf ( " FATAL ERROR IN INIT: %s \n I can't recover from this, "
" please reboot manually and send bugreport. \n " , msg ) ;
2004-01-20 21:32:43 +03:00
while ( 1 ) ;
}
2008-02-29 17:37:40 +03:00
void warn ( char * msg )
2004-01-20 21:32:43 +03:00
{
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 )
*/
2008-02-29 17:37:40 +03:00
pid_t doklog ( )
2004-01-20 21:32:43 +03:00
{
2007-03-03 00:48:18 +03:00
int in , out , i , ii ;
2008-02-29 17:37:40 +03:00
pid_t pid ;
2004-01-20 21:32:43 +03:00
char buf [ 1024 ] ;
/* open kernel message logger */
2008-02-29 17:37:40 +03:00
if ( ( in = open ( " /proc/kmsg " , O_RDONLY , 0 ) ) < 0 )
fatal ( " failed to open /proc/kmsg " ) ;
2004-01-20 21:32:43 +03:00
2008-02-29 17:37:40 +03:00
if ( mknod ( " /dev/tty4 " , S_IFCHR , MKDEV ( 4 , 4 ) ) < 0 | |
( out = open ( " /dev/tty4 " , O_WRONLY , 0 ) ) < 0 )
fatal ( " failed to open /dev/tty4 " ) ;
2004-01-20 21:32:43 +03:00
2008-02-29 17:37:40 +03:00
if ( ( pid = fork ( ) ) ) {
if ( pid < 0 ) fatal ( " doklog " ) ;
2004-01-20 21:32:43 +03:00
close ( in ) ;
2008-02-29 17:37:40 +03:00
close ( out ) ;
return pid ;
2004-03-19 20:02:37 +03:00
}
2004-01-20 21:32:43 +03:00
2008-02-29 17:37:40 +03:00
/* child */
close ( 0 ) ;
close ( 1 ) ;
close ( 2 ) ;
2004-01-20 21:32:43 +03:00
/* disable on-console syslog output */
2008-02-29 17:37:40 +03:00
klogctl ( 8 , NULL , 1 ) ;
while ( 1 )
if ( ( i = read ( in , buf , sizeof ( buf ) ) ) > 0 )
ii = write ( out , buf , i ) ;
}
pid_t spawn ( char * av [ ] )
{
pid_t pid ;
if ( ( pid = fork ( ) ) ) {
if ( pid < 0 ) fatal ( av [ 0 ] ) ;
return pid ;
} else {
execve ( av [ 0 ] , av , env ) ;
perror ( av [ 0 ] ) ;
exit ( 1 ) ;
2004-01-20 21:32:43 +03:00
}
}
2005-01-21 16:52:04 +03:00
void grab_env ( int fd )
{
2007-04-12 20:38:53 +04:00
static char buf [ PIPE_BUF ] ;
2005-01-21 16:52:04 +03:00
char * p = buf ;
char * * ep ;
int i ;
if ( ( i = read ( fd , buf , sizeof ( buf ) ) ) < 0 ) {
2008-02-29 17:37:40 +03:00
warn ( " failed to read env from pipe " ) ;
2005-01-21 16:52:04 +03:00
myenv = env ;
return ;
}
close ( fd ) ;
buf [ i ] = ' \0 ' ;
if ( ( ep = myenv = malloc ( sizeof ( char * ) * 32 ) ) = = NULL ) {
2008-02-29 17:37:40 +03:00
warn ( " can't malloc env " ) ;
2005-01-21 16:52:04 +03:00
myenv = env ;
return ;
}
2008-02-29 17:37:40 +03:00
2005-01-21 16:52:04 +03:00
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 ;
2008-02-29 17:37:40 +03:00
fatal ( what ) ;
2004-11-30 17:44:10 +03:00
}
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 ) {
2008-02-29 17:37:40 +03:00
warn ( " failed to open /proc/mounts " ) ;
2004-01-20 21:32:43 +03:00
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 ) {
2008-02-29 17:37:40 +03:00
fatal ( " failed to umount some filesystems \n " ) ;
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 ;
2008-02-29 17:37:40 +03:00
pid_t pid , klogpid , udevpid ;
2004-01-20 21:32:43 +03:00
int wait_status ;
2008-02-29 17:37:40 +03:00
int fd = - 1 ;
2005-01-21 16:52:04 +03:00
int fds [ 2 ] ;
2008-02-29 17:37:40 +03:00
2005-01-21 16:52:04 +03:00
if ( mount ( " /proc " , " /proc " , " proc " , 0 , NULL ) )
2008-02-29 17:37:40 +03:00
fatal ( " failed to mount proc filesystem " ) ;
if ( mount ( " sysfs " , " /sys " , " sysfs " , 0 , NULL ) )
fatal ( " failed to mount sysfs filesystem " ) ;
if ( mount ( " udev " , " /dev " , " tmpfs " , 0 , " size=10M,mode=0755 " ) )
fatal ( " failed to mount tmpfs filesystem " ) ;
2004-01-20 21:32:43 +03:00
/* ignore Control-C and keyboard stop signals */
signal ( SIGINT , SIG_IGN ) ;
signal ( SIGTSTP , SIG_IGN ) ;
2008-02-29 17:37:40 +03:00
if ( mknod ( " /dev/console " , S_IFCHR , MKDEV ( 5 , 1 ) ) < 0 | |
( fd = open ( " /dev/console " , O_RDWR , 0 ) ) < 0 ) {
fatal ( " failed to open /dev/console " ) ;
}
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
2008-02-29 17:37:40 +03:00
if ( mknod ( " /dev/null " , S_IFCHR , MKDEV ( 1 , 3 ) ) < 0 )
fatal ( " failed to create /dev/null " ) ;
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 " ) ;
2008-02-29 17:37:40 +03:00
warn ( " could not set new controlling tty " ) ;
2004-03-19 20:02:37 +03:00
}
2004-01-20 21:32:43 +03:00
2008-02-29 17:37:40 +03:00
if ( sethostname ( " localhost.localdomain " , sizeof ( " localhost.localdomain " ) ) < 0 )
warn ( " could not set hostname " ) ;
2007-03-03 00:48:18 +03:00
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 )
2008-02-29 17:37:40 +03:00
warn ( " could not set domainname " ) ;
if ( mkdir ( " /dev/.initramfs " , 0755 ) < 0 | |
mkdir ( " /dev/pts " , 0755 ) < 0 | |
mkdir ( " /dev/shm " , 0755 ) < 0 )
fatal ( " mkdir \n " ) ;
if ( ( fd = open ( " /proc/sys/kernel/hotplug " , O_WRONLY , 0 ) ) < 0 | |
write ( fd , " \n " , sizeof ( " \n " ) ) < sizeof ( " \n " ) )
fatal ( " /proc/sys/kernel/hotplug \n " ) ;
close ( fd ) ;
2004-01-20 21:32:43 +03:00
2008-02-29 17:37:40 +03:00
klogpid = doklog ( ) ;
if ( mkdir ( " /dev/.udev " , 0755 ) < 0 | |
mkdir ( " /dev/.udev/db " , 0755 ) < 0 )
fatal ( " /dev/.udev/db " ) ;
printf ( " Spawning udevd... " ) ;
udevpid = spawn ( udevd ) ;
if ( mkdir ( " /dev/.udev/queue " , 0755 ) < 0 & & errno ! = EEXIST )
fatal ( " cannot create /dev/.udev/queue " ) ;
if ( waitpid ( spawn ( udevtrigger ) , & wait_status , 0 ) < 0 | |
! ( WIFEXITED ( wait_status ) ) )
warn ( " udevtrigger " ) ;
if ( waitpid ( spawn ( udevsettle ) , & wait_status , 0 ) < 0 | |
! ( WIFEXITED ( wait_status ) ) )
warn ( " udevsettle " ) ;
printf ( " done \n " ) ;
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 )
2008-02-29 17:37:40 +03:00
fatal ( " failed to create env pipe " ) ;
2007-03-03 00:48:18 +03:00
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
2008-02-29 17:37:40 +03:00
pid = spawn ( stage ) ;
2005-01-21 16:52:04 +03:00
close ( fds [ 1 ] ) ;
2008-02-29 17:37:40 +03:00
while ( pid ! = wait ( & wait_status ) ) ;
2004-01-20 21:32:43 +03:00
2004-11-30 17:44:10 +03:00
if ( ! ( WIFEXITED ( wait_status ) ) ) {
/* something went wrong */
2008-02-29 17:37:40 +03:00
printf ( " wait_status: %i, install exited abnormally " , wait_status ) ;
2004-01-20 21:32:43 +03:00
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 ] ) ;
2008-02-29 17:37:40 +03:00
if ( waitpid ( spawn ( udevsettle ) , & wait_status , 0 ) < 0 | |
! ( WIFEXITED ( wait_status ) ) )
warn ( " udevsettle " ) ;
kill ( udevpid , 9 ) ;
waitpid ( udevpid , & wait_status , 0 ) ;
nuke_dir ( " /dev/.udev/queue " ) ;
kill ( klogpid , 9 ) ;
waitpid ( klogpid , & wait_status , 0 ) ;
2005-01-21 16:52:04 +03:00
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 ) )
2008-02-29 17:37:40 +03:00
fatal ( " chdir to new root " ) ;
2004-11-30 17:44:10 +03:00
/* 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 ) )
2008-02-29 17:37:40 +03:00
fatal ( " stat " ) ;
2005-01-24 21:12:06 +03:00
2004-11-30 17:44:10 +03:00
if ( rst . st_dev = = cst . st_dev )
2008-02-29 17:37:40 +03:00
fatal ( " 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 ) )
2008-02-29 17:37:40 +03:00
fatal ( " 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 ) )
2008-02-29 17:37:40 +03:00
fatal ( " statfs / " ) ;
2004-11-30 17:44:10 +03:00
if ( sfs . f_type ! = RAMFS_MAGIC & & sfs . f_type ! = TMPFS_MAGIC )
2008-02-29 17:37:40 +03:00
fatal ( " 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 ) )
2008-02-29 17:37:40 +03:00
fatal ( " statfs " IMAGE_LOCATION ) ;
2005-01-24 21:12:06 +03:00
/* 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 ) )
2008-02-29 17:37:40 +03:00
fatal ( " 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 ) )
2008-02-29 17:37:40 +03:00
fatal ( " nested statfs " IMAGE_LOCATION ) ;
2005-01-26 16:43:53 +03:00
if ( sfs . f_type ! = RAMFS_MAGIC & & sfs . f_type ! = TMPFS_MAGIC )
if ( mount ( IMAGE_LOCATION , " . " IMAGE_LOCATION " /isolinux " , NULL , MS_MOVE , NULL ) )
2008-02-29 17:37:40 +03:00
fatal ( " overmounting nested image location " ) ;
2005-01-26 16:43:53 +03:00
}
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
2008-02-29 17:37:40 +03:00
if ( mount ( " /dev " , " ./dev " , NULL , MS_MOVE , NULL ) )
fatal ( " overmounting /dev " ) ;
2004-11-30 17:44:10 +03:00
/* Delete rootfs contents */
2008-02-29 17:37:40 +03:00
if ( nuke_dir ( " / " ) )
fatal ( " nuking initramfs contents " ) ;
2005-01-24 21:12:06 +03:00
2004-11-30 17:44:10 +03:00
/* Overmount the root */
2008-02-29 17:37:40 +03:00
if ( mount ( " . " , " / " , NULL , MS_MOVE , NULL ) )
fatal ( " 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 ( " / " ) )
2008-02-29 17:37:40 +03:00
fatal ( " chroot " ) ;
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 ) )
2008-02-29 17:37:40 +03:00
fatal ( " 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 ) ;
2008-02-29 17:37:40 +03:00
fatal ( " stage2 " ) ; /* Failed to spawn init */
2005-01-21 16:52:04 +03:00
return 0 ;
2004-01-20 21:32:43 +03:00
}