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 [ ] = {
" PATH=/usr/bin:/bin:/sbin:/usr/sbin:/mnt/sbin:/mnt/usr/sbin:/mnt/bin:/mnt/usr/bin " ,
" LD_LIBRARY_PATH=/lib:/usr/lib:/mnt/lib:/mnt/usr/lib:/usr/X11R6/lib:/mnt/usr/X11R6/lib " ,
" HOME=/ " ,
" TERM=linux " ,
" TERMINFO=/etc/terminfo " ,
NULL
} ;
/*
* this needs to handle the following cases :
*
* 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-05-25 16:53:56 +04:00
int testing = 0 ;
2004-01-20 21:32:43 +03:00
int klog_pid ;
void fatal_error ( char * msg )
{
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 ) ;
}
void print_int_init ( int fd , int i )
{
char buf [ 10 ] ;
char * chptr = buf + 9 ;
int j = 0 ;
if ( i < 0 )
{
write ( 1 , " - " , 1 ) ;
i = - 1 * i ;
}
while ( i )
{
* chptr - - = ' 0 ' + ( i % 10 ) ;
j + + ;
i = i / 10 ;
}
write ( fd , chptr + 1 , j ) ;
}
void print_str_init ( int fd , char * string )
{
write ( fd , string , strlen ( string ) ) ;
}
/* 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 ( )
{
int in , out , i ;
int log ;
int s ;
int sock = - 1 ;
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 )
write ( out , buf , i ) ;
write ( log , buf , i ) ;
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 ) ;
} else {
return 0 ;
}
}
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
# define MS_MOVE 8192
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 ;
2005-01-19 16:31:08 +03:00
char * stage2_argv [ ] = { STAGE2_BINNAME , NULL } ;
2004-01-20 21:32:43 +03:00
int wait_status ;
int fd ;
2004-11-30 17:44:10 +03:00
int end_stage1 = 0 ;
2004-11-22 16:47:29 +03:00
2004-01-20 21:32:43 +03:00
/* getpid() != 1 should work, by linuxrc tends to get a larger pid */
2004-05-25 16:53:56 +04:00
/*testing = (getpid() > 50); */
2004-01-20 21:32:43 +03:00
if ( ! testing ) {
/* turn off screen blanking */
printf ( " \033 [9;0] " ) ;
printf ( " \033 [8] " ) ;
}
else
printf ( " *** TESTING MODE *** \n " ) ;
2004-04-14 22:23:20 +04:00
// printf("\n\t\t\t\033[1;40mWelcome to \033[1;36mALT Linux\033[0;39m\n\n");
2004-01-20 21:32:43 +03:00
if ( ! testing ) {
if ( mount ( " /proc " , " /proc " , " proc " , 0 , NULL ) )
fatal_error ( " Unable to mount proc filesystem " ) ;
2004-08-18 17:33:07 +04:00
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 ) ;
if ( ! testing ) {
fd = open ( " /dev/tty1 " , O_RDWR , 0 ) ;
2004-03-19 20:02:37 +03:00
if ( fd < 0 ) {
perror ( " can't open /dev/tty1 " ) ;
2004-01-20 21:32:43 +03:00
/* try with devfs */
fd = open ( " /dev/vc/1 " , O_RDWR , 0 ) ;
2004-03-19 20:02:37 +03:00
}
2004-01-20 21:32:43 +03:00
if ( fd < 0 )
fatal_error ( " failed to open /dev/tty1 and /dev/vc/1 " ) ;
dup2 ( fd , 0 ) ;
dup2 ( fd , 1 ) ;
dup2 ( fd , 2 ) ;
close ( fd ) ;
}
/* 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
if ( ! testing ) {
char my_hostname [ ] = " localhost.localdomain " ;
sethostname ( my_hostname , sizeof ( my_hostname ) ) ;
/* the default domainname (as of 2.0.35) is "(none)", which confuses
glibc */
setdomainname ( " " , 0 ) ;
}
if ( ! testing )
doklog ( ) ;
/* 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 " ) ;
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 ;
}
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
if ( testing )
return 0 ;
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 ) ;
}
2004-11-30 17:44:10 +03:00
kill ( klog_pid , 9 ) ;
2004-12-01 18:10:51 +03:00
printf ( " Spawning init ... " ) ;
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 " ) ;
if ( rst . st_dev = = cst . st_dev )
fatal_error ( " current directory on the same filesystem as the root " ) ;
/* The initramfs should have /init */
if ( stat ( " /init " , & ist ) | | ! S_ISREG ( ist . st_mode ) )
fatal_error ( " can't find /init on initramfs " ) ;
/* 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 " ) ;
/* Okay, I think we should be safe... */
2004-12-07 17:37:32 +03:00
/* overmount image under pwd */
2004-11-30 19:11:41 +03:00
if ( mount ( IMAGE_LOCATION , " . " IMAGE_LOCATION , NULL , MS_MOVE , NULL ) )
fatal_error ( " overmounting image location " ) ;
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 " ) ;
/* Delete rootfs contents */
if ( nuke_dir ( " / " ) )
fatal_error ( " nuking initramfs contents " ) ;
/* Overmount the root */
if ( mount ( " . " , " / " , NULL , MS_MOVE , NULL ) )
fatal_error ( " overmounting root " ) ;
/* chroot, chdir */
if ( chroot ( " . " ) | | chdir ( " / " ) )
fatal_error ( " chroot " ) ;
/* Open /dev/console */
if ( ( fd = open ( " /dev/console " , O_RDWR ) ) < 0 )
fatal_error ( " opening console " ) ;
dup2 ( fd , 0 ) ;
dup2 ( fd , 1 ) ;
dup2 ( fd , 2 ) ;
close ( fd ) ;
/* Spawn init */
2004-12-01 18:10:51 +03:00
printf ( " done. \n " ) ;
2005-01-19 16:31:08 +03:00
/* Check for given init */
if ( stat ( stage2_argv [ 0 ] , & ist ) | | ! S_ISREG ( ist . st_mode ) )
fatal_error ( " can't find init on root fs " ) ;
2004-11-30 17:44:10 +03:00
execve ( stage2_argv [ 0 ] , stage2_argv , env ) ;
fatal_error ( " stage2 " ) ; /* Failed to spawn init */
2004-01-20 21:32:43 +03:00
}