//----------------------------------------------------------------------------- // Copyright © 2001 - Philip Howard - All rights reserved, released under GPL // // 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, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //----------------------------------------------------------------------------- // package cdinit // program cdinit2 (cd initializer stage 2) // // description // This program is the stage 2 initializer for the cdinit package. The // stage 1 program will have invoked this program in PID 1. // // action // 1. Symlink /lib -> /tmpfs/cdrom/lib (for ld-linux.so, libc.so) // 2. Run tar to extract /tmpfs/cdrom/${ARCH}/boot/root.tar into /tmpfs // 3. Pivot root: /tmpfs -> / -> /mnt // 4. Unmount /mnt to release the initrd // 5. Free ramdisk buffers (does not currently work due to kernel bug) // 6. Execute cdinit3 to release mappings on the CDROM this program has // //----------------------------------------------------------------------------- // author Philip Howard // email phil@ipal.org //----------------------------------------------------------------------------- #include "cdinit.h" //----------------------------------------------------------------------------- // DISPLAY_PER_SEC is the number of times per second to display progress. //----------------------------------------------------------------------------- #define DISPLAY_PER_SEC 16 #define ROTOR_CHARS "\\|/-" //----------------------------------------------------------------------------- // Define timespec struct to make parent sleep briefly while tar starts. //----------------------------------------------------------------------------- struct timespec sleep_time = { 10, 0 }; //----------------------------------------------------------------------------- // Define the file to untar to populate the tmpfs. //----------------------------------------------------------------------------- static const char tar_file [] = "/tmpfs/cdrom/" CDINIT_ARCH_NAME "/boot/root.tar"; //----------------------------------------------------------------------------- // Define argument lists for execve(). //----------------------------------------------------------------------------- static char * const tar_command [] = { "/tmpfs/cdrom/" CDINIT_ARCH_NAME "/bin/tar", "xpf", "-", NULL }; static char * cdinit3_command [] = { "/sbin/cdinit3", NULL }; //----------------------------------------------------------------------------- // Define an environment for execve(). //----------------------------------------------------------------------------- static char * environment [] = { "PATH=/sbin:/bin:/usr/sbin:/usr/bin", "HOME=/", "TERM=linux", NULL }; //----------------------------------------------------------------------------- // Set up display string. //----------------------------------------------------------------------------- // 1111111111222222222233333333334444444444555555555566666666667777777777 // 1234567890123456789012345678901234567890123456789012345678901234567890123456789 static char display [] = "\rloading tmpfs: - - [ -.-%] of - |.........................|"; //----------------------------------------------------------------------------- // Where to store trace info. //----------------------------------------------------------------------------- #ifdef CDINIT_TRACE static const char * trace [4]; #endif //----------------------------------------------------------------------------- // Include functions. //----------------------------------------------------------------------------- #include "write_string.c" #ifdef CDINIT_TRACE #include "write_dec_slong.c" #endif //----------------------------------------------------------------------------- // Function setup does setup to be done only once. //----------------------------------------------------------------------------- static int main1 () { struct stat statbuf ; unsigned long file_size ; ssize_t len ; int fd ; int pid ; int status ; int pipe_fds [2] ; char cmdline [1024] ; //--------------------------------- // Show the kernel boot parameters. //--------------------------------- trace2( "mkdir", "/proc" ); if ( mkdir( "/proc", 0755 ) != 0 ) return 2; trace4( "umount"+1, "/proc"+1, "/proc", "/proc"+1 ); if ( mount( "/proc"+1, "/proc", "/proc"+1, MS_MGC_VAL, 0 ) != 0 ) return 4; trace2( "open", "/proc/cmdline" ); if ( ( fd = open( "/proc/cmdline", O_RDONLY, 0 ) ) < 0 ) return 2; trace2( "read", "/proc/cmdline" ); len = read( fd, cmdline, sizeof (cmdline) ); if ( len > 2 ) { -- len; if ( cmdline[len] == '\n' ) cmdline[len] = 0; write_string( 1, "boot parameters: " ); write( 1, cmdline, len ); write( 1, "\n", 1 ); } close( fd ); umount( "/proc" ); rmdir( "/proc" ); //---------------------------------------------------------- // Set up to run tar to load tar file from CDROM into tmpfs. //---------------------------------------------------------- trace3( "symlink", "/tmpfs/cdrom/" CDINIT_ARCH_NAME "/lib", "/lib" ); if ( symlink( "/tmpfs/cdrom/" CDINIT_ARCH_NAME "/lib", "/lib" ) != 0 ) return 3; trace3( "open", tar_file, "O_RDONLY" ); if ( ( fd = open( tar_file, O_RDONLY, 0 ) ) < 0 ) return 3; trace1( "fstat" ); if ( fstat( fd, & statbuf ) != 0 ) return 2; file_size = (unsigned long) statbuf.st_size; signal( SIGPIPE, SIG_IGN ); trace1( "pipe" ); if ( pipe( pipe_fds ) < 0 ) return 1; trace4( "fork", tar_command[0], tar_command[1], tar_command[2] ); pid = fork(); if ( pid < 0 ) return 4; if ( pid == 0 ) { close( pipe_fds[1] ); close( 0 ); dup2( pipe_fds[0], 0 ); close( pipe_fds[0] ); fcntl( 0, F_SETFD, 0 ); trace2( "chdir", "/tmpfs" ); if ( chdir( "/tmpfs" ) < 0 ) return 2; trace1( "execve" ); execve( tar_command[0], tar_command, environment ); return 4; } close( pipe_fds[0] ); //----------------------------------------------------------- // Copy the tar file to the pipe with rotor progress display. //----------------------------------------------------------- { char * data_ptr ; char * rotor_ptr ; char * ptr ; size_t total_len ; ssize_t read_len ; ssize_t write_len ; unsigned long num ; unsigned long new_time ; unsigned long old_time ; struct timeval full_time ; char data_buf [2048] ; //-- Fill in file size. num = file_size; ptr = display+50; do { * ptr = '0' + ( num % 10 ); } while ( -- ptr > display+41 && ( num /= 10 ) ); do { * ptr = ' '; } while ( -- ptr > display+41 ); write( 1, display, sizeof (display) - 1 ); data_ptr = data_buf; // not needed except to avoid a warning at -O2. rotor_ptr = ROTOR_CHARS; read_len = 0; write_len = 0; total_len = 0; old_time = 0; for (;;) { trace1( "gettimeofday" ); if ( gettimeofday( & full_time, NULL ) < 0 ) return 1; new_time = full_time.tv_usec; new_time /= (unsigned long) ( 1000000U / DISPLAY_PER_SEC ); new_time += full_time.tv_sec * DISPLAY_PER_SEC; //-- Check for time to display. if ( read_len < 0 || write_len < 0 || new_time != old_time ) { old_time = new_time; //-- Pick rotor. if ( ! * rotor_ptr ) rotor_ptr = ROTOR_CHARS; display[16] = * rotor_ptr ++; //-- Fill in current size. num = total_len; ptr = display+27; do { * ptr = '0' + ( num % 10U ); } while ( -- ptr > display+18 && ( num /= 10U ) ); do { * ptr = ' '; } while ( -- ptr > display+18 ); //-- Fill in percentage. num = (( total_len * 5U )) / (( file_size / 200U )); display[34] = '0' + num % 10U; num /= 10U; display[32] = '0' + num % 10U; num /= 10U; if ( num ) { display[31] = '0' + num % 10U; num /= 10U; if ( num ) display[30] = '0' + num % 10U; } //-- Fill in progress bar. num = (( total_len * 5U )) / (( file_size / 5U )); ptr = display+54; while ( num -- ) * ptr ++ = '*'; //-- Display as much of the progress string as has changed write( 1, display, ptr - display ); //-- Check for time to leave. if ( read_len < 0 || write_len < 0 ) break; } //-- If buffer is empty, read in more. if ( read_len == 0 ) { read_len = read( fd, data_buf, sizeof (data_buf) ); if ( read_len == 0 ) read_len = -2; if ( read_len < 0 ) continue; data_ptr = data_buf; } //-- If buffer has data, write it. if ( read_len > 0 ) { write_len = write( pipe_fds[1], data_ptr, read_len ); if ( write_len < 0 ) continue; data_ptr += write_len; read_len -= write_len; total_len += write_len; } } if ( read_len == -1 ) write_string( 1, "READ ERROR\n" ); if ( write_len == -1 ) write_string( 1, "WRITE ERROR\n" ); } close( fd ); close( pipe_fds[1] ); trace1( "waitpid" ); if ( waitpid( pid, & status, 0 ) < 0 ) return 4; write( 1, "\n", 1 ); //--------------------------------------------- // Switch over to the tmpfs filesystem as root. //--------------------------------------------- errno = 0; trace3( "pivot_root", "/tmpfs", "/tmpfs/mnt" ); if ( pivot_root( "/tmpfs", "/tmpfs/mnt" ) != 0 ) return 3; trace2( "chdir", "/" ); if ( chdir( "/" ) != 0 ) return 2; //----------------------------------------------------- // Close and reopen the console via the inode in tmpfs. // This will not be needed after migrating to devfs. //----------------------------------------------------- close( 0 ); close( 1 ); close( 2 ); open( "/dev/console", O_RDWR, 0 ); dup( 0 ); dup( 0 ); //----------------------------------- // Unmount the original root ramdisk. //----------------------------------- trace2( "umount", "/tmpfs/mnt"+6 ); if ( umount( "/tmpfs/mnt"+6 ) != 0 ) return 2; //-------------------------------------------------------- // Since stage 2 is running mapped from the mounted CDROM, // run stage 3 from tmpfs to allow unmounting the CDROM. //-------------------------------------------------------- execve( cdinit3_command[0], cdinit3_command, environment ); trace1( "executing cdinit3" ); return 1; } //----------------------------------------------------------------------------- // Function _start is really the main function. //----------------------------------------------------------------------------- void _start () { int rc ; errno = 0; #if 0 write_string( 1, "\r" #ifdef CDINIT_COLOR "\033[33;1m" #endif "cdinit v" CDINIT_VERSION ", " "Copyright (C) 2001, Philip Howard, All rights reserved, See GPL" #ifdef CDINIT_COLOR "\033[0m" #endif "\n" ); #endif rc = main1(); #ifdef CDINIT_TRACE if ( rc != 0 ) { int i; write_string( 1, "Error " ); write_dec_slong( 1, (signed long) ( errno ) ); write_string( 1, " doing:" ); for ( i = 0; i < rc; ++ i ) { write_string( 1, " " ); write_string( 1, trace[i] ); } write_string( 1, "\n" ); } #endif write_string( 1, "halting ... \n\n\n\n\n\n" ); reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_HALT, NULL ); for (;;); }