1
0
Fork 0
mirror of https://gitlab.com/sortix/sortix.git synced 2023-02-13 20:55:38 -05:00
sortix--sortix/init/init.c++

549 lines
14 KiB
C++
Raw Normal View History

/*******************************************************************************
2014-10-04 11:34:51 -04:00
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
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 3 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/>.
2014-10-04 11:34:51 -04:00
init.c++
2014-09-30 16:59:34 -04:00
Start the operating system.
*******************************************************************************/
2013-06-01 07:52:36 -04:00
#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
2014-09-30 16:59:34 -04:00
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
2013-06-01 07:52:36 -04:00
#include <assert.h>
2013-07-10 15:37:07 -04:00
#include <brand.h>
2013-06-01 07:52:36 -04:00
#include <dirent.h>
#include <errno.h>
#include <error.h>
2011-11-24 04:26:36 -05:00
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
2013-06-01 07:52:36 -04:00
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <timespec.h>
#include <unistd.h>
Implemented the fork() system call and what it needed to work properly. This commit got completely out of control. Added the fork(), getpid(), getppid(), sleep(), usleep() system calls, and aliases in the Maxsi:: namespace. Fixed a bug where zero-byte allocation would fail. Worked on the DescriptorTable class which now works and can fork. Got rid of some massive print-registers statements and replaced them with the portable InterruptRegisters::LogRegisters() function. Removed the SysExecuteOld function and replaced it with Process::Execute(). Rewrote the boot sequence in kernel.cpp such that it now loads the system idle process 'idle' as PID 0, and the initization process 'init' as PID 1. Rewrote the SIGINT hack. Processes now maintain a family-tree structure and keep track of their threads. PIDs are now allocated using a simple hack. Virtual memory per-process can now be allocated using a simple hack. Processes can now be forked. Fixed the Process::Execute function such that it now resets the stack pointer to where the stack actually is - not just a magic value. Removed the old and ugly Process::_endcodesection hack. Rewrote the scheduler into a much cleaner and faster version. Debug code is now moved to designated functions. The noop kernel-thread has been replaced by a simple user-space infinite-loop program 'idle'. The Thread class has been seperated from the Scheduler except in Scheduler- related code. Thread::{Save,Load}Registers has been improved and has been moved to $(CPU)/thread.cpp. Threads can now be forked. A new CreateThread function creates threads properly and portably. Added a MicrosecondsSinceBoot() function. Fixed a crucial bug in MemoryManagement::Fork(). Added an 'idle' user-space program that is a noop infinite loop, which is used by the scheduler when there is nothing to do. Rewrote the 'init' program such that it now forks off a shell, instead of becoming the shell. Added the $$ (current PID) and $PPID (parent PPID) variables to the shell.
2011-09-21 14:52:29 -04:00
2013-06-01 07:52:36 -04:00
#include <fsmarshall.h>
char* read_single_line(FILE* fp)
{
char* ret = NULL;
size_t ret_size = 0;
ssize_t ret_length = getline(&ret, &ret_size, fp);
if ( ret_length < 0 )
return NULL;
if ( ret_length && ret[ret_length-1] == '\n' )
ret[--ret_length] = '\0';
return ret;
}
char* print_string(const char* format, ...)
{
char* ret = NULL;
va_list ap;
va_start(ap, format);
2014-09-30 16:59:34 -04:00
int status = vasprintf(&ret, format, ap);
2013-06-01 07:52:36 -04:00
va_end(ap);
2014-09-30 16:59:34 -04:00
assert(0 <= status);
2013-06-01 07:52:36 -04:00
return ret;
}
char* join_paths(const char* a, const char* b)
{
size_t a_len = strlen(a);
bool has_slash = (a_len && a[a_len-1] == '/') || b[0] == '/';
2014-09-30 16:59:34 -04:00
return has_slash ? print_string("%s%s", a, b)
: print_string("%s/%s", a, b);
2013-06-01 07:52:36 -04:00
}
typedef struct
{
char** strings;
size_t length;
size_t capacity;
} string_array_t;
string_array_t string_array_make()
{
string_array_t sa;
sa.strings = NULL;
sa.length = sa.capacity = 0;
return sa;
}
void string_array_reset(string_array_t* sa)
{
for ( size_t i = 0; i < sa->length; i++ )
free(sa->strings[i]);
free(sa->strings);
*sa = string_array_make();
}
size_t string_array_find(string_array_t* sa, const char* str)
{
for ( size_t i = 0; i < sa->length; i++ )
if ( !strcmp(sa->strings[i], str) )
return i;
return SIZE_MAX;
}
bool string_array_contains(string_array_t* sa, const char* str)
{
return string_array_find(sa, str) != SIZE_MAX;
}
bool string_array_append(string_array_t* sa, const char* str)
{
if ( sa->length == sa->capacity )
{
size_t new_capacity = sa->capacity ? sa->capacity * 2 : 8;
size_t new_size = sizeof(char*) * new_capacity;
char** new_strings = (char**) realloc(sa->strings, new_size);
if ( !new_strings )
return false;
sa->strings = new_strings;
sa->capacity = new_capacity;
}
2014-09-30 16:59:34 -04:00
char* copy = str ? strdup(str) : NULL;
2013-06-01 07:52:36 -04:00
if ( str && !copy )
return false;
sa->strings[sa->length++] = copy;
return true;
}
Implemented the fork() system call and what it needed to work properly. This commit got completely out of control. Added the fork(), getpid(), getppid(), sleep(), usleep() system calls, and aliases in the Maxsi:: namespace. Fixed a bug where zero-byte allocation would fail. Worked on the DescriptorTable class which now works and can fork. Got rid of some massive print-registers statements and replaced them with the portable InterruptRegisters::LogRegisters() function. Removed the SysExecuteOld function and replaced it with Process::Execute(). Rewrote the boot sequence in kernel.cpp such that it now loads the system idle process 'idle' as PID 0, and the initization process 'init' as PID 1. Rewrote the SIGINT hack. Processes now maintain a family-tree structure and keep track of their threads. PIDs are now allocated using a simple hack. Virtual memory per-process can now be allocated using a simple hack. Processes can now be forked. Fixed the Process::Execute function such that it now resets the stack pointer to where the stack actually is - not just a magic value. Removed the old and ugly Process::_endcodesection hack. Rewrote the scheduler into a much cleaner and faster version. Debug code is now moved to designated functions. The noop kernel-thread has been replaced by a simple user-space infinite-loop program 'idle'. The Thread class has been seperated from the Scheduler except in Scheduler- related code. Thread::{Save,Load}Registers has been improved and has been moved to $(CPU)/thread.cpp. Threads can now be forked. A new CreateThread function creates threads properly and portably. Added a MicrosecondsSinceBoot() function. Fixed a crucial bug in MemoryManagement::Fork(). Added an 'idle' user-space program that is a noop infinite loop, which is used by the scheduler when there is nothing to do. Rewrote the 'init' program such that it now forks off a shell, instead of becoming the shell. Added the $$ (current PID) and $PPID (parent PPID) variables to the shell.
2011-09-21 14:52:29 -04:00
int child()
{
pid_t init_pid = getppid();
char init_pid_str[sizeof(pid_t)*3];
snprintf(init_pid_str, sizeof(pid_t)*3, "%ju", (uintmax_t) init_pid);
setenv("INIT_PID", init_pid_str, 1);
setpgid(0, 0);
tcsetpgrp(0, getpid());
const char* default_shell = "sh";
const char* default_home = "/root";
const char* shell;
const char* home;
if ( struct passwd* passwd = getpwuid(getuid()) )
{
setenv("USERNAME", passwd->pw_name, 1);
home = passwd->pw_dir[0] ? passwd->pw_dir : default_home;
setenv("HOME", home, 1);
shell = passwd->pw_shell[0] ? passwd->pw_shell : default_shell;
setenv("SHELL", shell, 1);
}
else
{
setenv("USERNAME", "root", 1);
setenv("HOME", home = default_home, 1);
setenv("SHELL", shell = default_shell, 1);
}
chdir(home);
const char* newargv[] = { shell, NULL };
execvp(shell, (char* const*) newargv);
error(0, errno, "%s", shell);
return 2;
}
Implemented the fork() system call and what it needed to work properly. This commit got completely out of control. Added the fork(), getpid(), getppid(), sleep(), usleep() system calls, and aliases in the Maxsi:: namespace. Fixed a bug where zero-byte allocation would fail. Worked on the DescriptorTable class which now works and can fork. Got rid of some massive print-registers statements and replaced them with the portable InterruptRegisters::LogRegisters() function. Removed the SysExecuteOld function and replaced it with Process::Execute(). Rewrote the boot sequence in kernel.cpp such that it now loads the system idle process 'idle' as PID 0, and the initization process 'init' as PID 1. Rewrote the SIGINT hack. Processes now maintain a family-tree structure and keep track of their threads. PIDs are now allocated using a simple hack. Virtual memory per-process can now be allocated using a simple hack. Processes can now be forked. Fixed the Process::Execute function such that it now resets the stack pointer to where the stack actually is - not just a magic value. Removed the old and ugly Process::_endcodesection hack. Rewrote the scheduler into a much cleaner and faster version. Debug code is now moved to designated functions. The noop kernel-thread has been replaced by a simple user-space infinite-loop program 'idle'. The Thread class has been seperated from the Scheduler except in Scheduler- related code. Thread::{Save,Load}Registers has been improved and has been moved to $(CPU)/thread.cpp. Threads can now be forked. A new CreateThread function creates threads properly and portably. Added a MicrosecondsSinceBoot() function. Fixed a crucial bug in MemoryManagement::Fork(). Added an 'idle' user-space program that is a noop infinite loop, which is used by the scheduler when there is nothing to do. Rewrote the 'init' program such that it now forks off a shell, instead of becoming the shell. Added the $$ (current PID) and $PPID (parent PPID) variables to the shell.
2011-09-21 14:52:29 -04:00
int runsystem()
{
pid_t childpid = fork();
2014-09-30 16:59:34 -04:00
if ( childpid < 0 )
error(2, errno, "fork");
if ( childpid )
{
int status;
waitpid(childpid, &status, 0);
// TODO: Use the proper macro!
if ( 128 <= WEXITSTATUS(status) || WIFSIGNALED(status) )
{
printf("Looks like the system crashed, trying to bring it back up.\n");
return runsystem();
}
return WEXITSTATUS(status);
}
2013-03-10 07:08:20 -04:00
exit(child());
}
2014-09-30 16:59:34 -04:00
int chain_boot_path(const char* path)
2013-06-01 07:52:36 -04:00
{
// Run the next init program and restart it in case of a crash.
try_reboot_system:
if ( pid_t child_pid = fork() )
{
int status;
waitpid(child_pid, &status, 0);
// TODO: Use the proper macro!
if ( 128 <= WEXITSTATUS(status) || WIFSIGNALED(status) )
{
printf("Looks like the system crashed, trying to bring it back up.\n");
goto try_reboot_system;
}
return WEXITSTATUS(status);
}
// Switch to the new root directory,
chroot(path);
chdir("/");
assert(getenv("cputype"));
char* init_path = print_string("/%s/bin/init", getenv("cputype"));
execl(init_path, init_path, NULL);
exit(127);
}
int init_emergency(int errnum, const char* format, ...)
{
fprintf(stderr, "init: emergency: ");
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
if ( errnum )
fprintf(stderr, ": %s", strerror(errnum));
fprintf(stderr, "\n");
fprintf(stderr, "init: Dropping you to an emergency shell.\n");
fprintf(stderr, "init: Run `init' again when you have resolved the "
"situation to continue.\n");
return runsystem();
}
void add_block_devices_to_string_array(const char* path, string_array_t* sa)
{
DIR* dir = opendir(path);
if ( !dir )
return;
while ( struct dirent* entry = readdir(dir) )
{
if ( entry->d_name[0] == '.' )
continue;
char* dev_path = join_paths(path, entry->d_name);
struct stat st;
if ( !(stat(dev_path, &st) == 0 &&
S_ISBLK(st.st_mode) &&
string_array_append(sa, dev_path)) )
free(dev_path);
}
closedir(dir);
}
bool is_ext2_filesystem(const char* path, const char* uuid = NULL)
{
if ( pid_t child_pid = fork() )
{
int exit_status;
waitpid(child_pid, &exit_status, 0);
return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 0;
}
if ( uuid )
execlp("extfs", "extfs", "--probe", "--test-uuid", uuid, path, NULL);
else
execlp("extfs", "extfs", "--probe", path, NULL);
exit(127);
}
bool is_master_boot_record(const char* path)
{
if ( pid_t child_pid = fork() )
{
int exit_status;
waitpid(child_pid, &exit_status, 0);
return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 0;
}
execlp("mbrfs", "mbrfs", "--probe", path, NULL);
exit(127);
}
bool create_master_boot_record_partitions(const char* path, string_array_t* sa)
{
int pipe_fds[2];
pipe(pipe_fds);
if ( pid_t child_pid = fork() )
{
close(pipe_fds[1]);
FILE* mbrfp = fdopen(pipe_fds[0], "r");
while ( char* partition = read_single_line(mbrfp) )
{
if ( string_array_contains(sa, partition) ||
!string_array_append(sa, partition) )
free(partition);
}
fclose(mbrfp);
int exit_status;
waitpid(child_pid, &exit_status, 0);
return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 0;
}
dup2(pipe_fds[1], 1);
close(pipe_fds[0]);
close(pipe_fds[1]);
execlp("mbrfs", "mbrfs", path, NULL);
exit(127);
}
int chain_boot_device(const char* dev_path)
{
// Create a directory where we will mount the root filesystem.
const char* mount_point = "/fs";
const char* mount_point_dev = "/fs/dev";
mkdir(mount_point, 0666);
// Get information about the mount point before mounting.
struct stat orig_st, new_st;
stat(mount_point, &orig_st);
// Spawn the filesystem server for the root filesystem.
pid_t fs_pid = fork();
if ( !fs_pid )
{
execlp("extfs", "extfs", "--foreground", dev_path, mount_point, NULL);
exit(127);
}
// Wait for the filesystem server to come online.
struct timespec mount_wait_ts = timespec_make(0, 50L * 1000L * 1000L);
do nanosleep(&mount_wait_ts, NULL), stat(mount_point, &new_st);
2013-06-01 07:52:36 -04:00
while ( new_st.st_ino == orig_st.st_ino && new_st.st_dev == orig_st.st_dev );
// Create a device directory in the root filesystem.
mkdir(mount_point_dev, 0666);
// Mount the current device directory inside the new root filesystem.
int old_dev_fd = open("/dev", O_DIRECTORY | O_RDONLY);
int new_dev_fd = open(mount_point_dev, O_DIRECTORY | O_RDONLY);
fsm_fsbind(old_dev_fd, new_dev_fd, 0);
close(new_dev_fd);
close(old_dev_fd);
2014-09-30 16:59:34 -04:00
int ret = chain_boot_path(mount_point);
int root_fd = open(mount_point, O_RDONLY);
if ( 0 <= root_fd )
{
fsync(root_fd);
close(root_fd);
}
unmount(mount_point, 0);
int fs_exitstatus;
waitpid(fs_pid, &fs_exitstatus, 0);
2013-06-01 07:52:36 -04:00
if ( ret == 127 )
return init_emergency(errno, "Unable to locate the next init program");
return ret;
}
int chain_boot_uuid(const char* root_uuid)
{
string_array_t block_devices = string_array_make();
add_block_devices_to_string_array("/dev", &block_devices);
string_array_t root_block_devices = string_array_make();
// Scan through all the block devices and check for a filesystem with the
// desired uuid while creating partitions if encountering partition tables.
for ( size_t i = 0; i < block_devices.length; i++ )
{
const char* device_path = block_devices.strings[i];
assert(device_path);
if ( is_ext2_filesystem(device_path) )
{
if ( is_ext2_filesystem(device_path, root_uuid) )
string_array_append(&root_block_devices, device_path);
}
else if ( is_master_boot_record(device_path) )
{
create_master_boot_record_partitions(device_path, &block_devices);
}
}
string_array_reset(&block_devices);
// Panic if we are unable to locate the desired root filesystem.
if ( !root_block_devices.length )
return init_emergency(0, "Unable to locate root filesystem with uuid="
"`%s'", root_uuid);
// If we only found a single matching filesystem, we can just boot it.
if ( root_block_devices.length == 1 )
return chain_boot_device(root_block_devices.strings[0]);
// Handle the case where multiple root filesystems with the correct uuid is
// found - we have to ask the user for help in this case.
fprintf(stderr, "init: Found multiple devices with uuid=`%s'.\n", root_uuid);
fprintf(stderr, "init: Select the correct boot device or nothing to get an emergency shell.\n");
retry_ask_root_block_device:
for ( size_t i = 0; i < root_block_devices.length; i++ )
fprintf(stderr, "%zu.\t%s\n", i, root_block_devices.strings[i]);
printf("Enter index or name of boot device [root shell]: ");
fflush(stdout);
char* input = read_single_line(stdin);
if ( !input )
return init_emergency(errno, "Unable read line from standard input");
if ( !input[0] )
return init_emergency(0, "ambigious root filesystem - shell selected");
char* input_end;
unsigned long index = strtoul(input, &input_end, 0);
if ( *input_end )
{
if ( string_array_contains(&root_block_devices, input) )
return chain_boot_device(input);
fprintf(stderr, "init: error: `%s' is not an allowed choice\n", input);
goto retry_ask_root_block_device;
}
if ( root_block_devices.length <= index )
{
fprintf(stderr, "init: error: `%lu' is not an allowed choice\n", index);
goto retry_ask_root_block_device;
}
return chain_boot_device(root_block_devices.strings[index]);
}
2014-09-30 16:59:34 -04:00
void set_hostname()
Implemented the fork() system call and what it needed to work properly. This commit got completely out of control. Added the fork(), getpid(), getppid(), sleep(), usleep() system calls, and aliases in the Maxsi:: namespace. Fixed a bug where zero-byte allocation would fail. Worked on the DescriptorTable class which now works and can fork. Got rid of some massive print-registers statements and replaced them with the portable InterruptRegisters::LogRegisters() function. Removed the SysExecuteOld function and replaced it with Process::Execute(). Rewrote the boot sequence in kernel.cpp such that it now loads the system idle process 'idle' as PID 0, and the initization process 'init' as PID 1. Rewrote the SIGINT hack. Processes now maintain a family-tree structure and keep track of their threads. PIDs are now allocated using a simple hack. Virtual memory per-process can now be allocated using a simple hack. Processes can now be forked. Fixed the Process::Execute function such that it now resets the stack pointer to where the stack actually is - not just a magic value. Removed the old and ugly Process::_endcodesection hack. Rewrote the scheduler into a much cleaner and faster version. Debug code is now moved to designated functions. The noop kernel-thread has been replaced by a simple user-space infinite-loop program 'idle'. The Thread class has been seperated from the Scheduler except in Scheduler- related code. Thread::{Save,Load}Registers has been improved and has been moved to $(CPU)/thread.cpp. Threads can now be forked. A new CreateThread function creates threads properly and portably. Added a MicrosecondsSinceBoot() function. Fixed a crucial bug in MemoryManagement::Fork(). Added an 'idle' user-space program that is a noop infinite loop, which is used by the scheduler when there is nothing to do. Rewrote the 'init' program such that it now forks off a shell, instead of becoming the shell. Added the $$ (current PID) and $PPID (parent PPID) variables to the shell.
2011-09-21 14:52:29 -04:00
{
2014-09-30 16:59:34 -04:00
FILE* hostname_fp = fopen("/etc/hostname", "r");
if ( !hostname_fp )
{
if ( errno == ENOENT )
return;
error(0, errno, "unable to open /etc/hostname, hostname is not set");
return;
}
char* hostname = read_single_line(hostname_fp);
if ( !hostname )
{
error(0, errno, "unable to read /etc/hostname, hostname is not set");
fclose(hostname_fp);
return;
}
fclose(hostname_fp);
if ( sethostname(hostname, strlen(hostname) + 1) < 0 )
{
error(0, errno, "unable to set hostname to `%s'", hostname);
free(hostname);
return;
}
free(hostname);
}
int init_main(int argc, char* argv[])
{
if ( 3 <= argc && !strcmp(argv[1], "--chain") )
return chain_boot_device(argv[2]);
Implemented the fork() system call and what it needed to work properly. This commit got completely out of control. Added the fork(), getpid(), getppid(), sleep(), usleep() system calls, and aliases in the Maxsi:: namespace. Fixed a bug where zero-byte allocation would fail. Worked on the DescriptorTable class which now works and can fork. Got rid of some massive print-registers statements and replaced them with the portable InterruptRegisters::LogRegisters() function. Removed the SysExecuteOld function and replaced it with Process::Execute(). Rewrote the boot sequence in kernel.cpp such that it now loads the system idle process 'idle' as PID 0, and the initization process 'init' as PID 1. Rewrote the SIGINT hack. Processes now maintain a family-tree structure and keep track of their threads. PIDs are now allocated using a simple hack. Virtual memory per-process can now be allocated using a simple hack. Processes can now be forked. Fixed the Process::Execute function such that it now resets the stack pointer to where the stack actually is - not just a magic value. Removed the old and ugly Process::_endcodesection hack. Rewrote the scheduler into a much cleaner and faster version. Debug code is now moved to designated functions. The noop kernel-thread has been replaced by a simple user-space infinite-loop program 'idle'. The Thread class has been seperated from the Scheduler except in Scheduler- related code. Thread::{Save,Load}Registers has been improved and has been moved to $(CPU)/thread.cpp. Threads can now be forked. A new CreateThread function creates threads properly and portably. Added a MicrosecondsSinceBoot() function. Fixed a crucial bug in MemoryManagement::Fork(). Added an 'idle' user-space program that is a noop infinite loop, which is used by the scheduler when there is nothing to do. Rewrote the 'init' program such that it now forks off a shell, instead of becoming the shell. Added the $$ (current PID) and $PPID (parent PPID) variables to the shell.
2011-09-21 14:52:29 -04:00
// Reset the terminal's color and the rest of it.
2013-07-10 15:37:07 -04:00
printf(BRAND_INIT_BOOT_MESSAGE);
fflush(stdout);
Implemented the fork() system call and what it needed to work properly. This commit got completely out of control. Added the fork(), getpid(), getppid(), sleep(), usleep() system calls, and aliases in the Maxsi:: namespace. Fixed a bug where zero-byte allocation would fail. Worked on the DescriptorTable class which now works and can fork. Got rid of some massive print-registers statements and replaced them with the portable InterruptRegisters::LogRegisters() function. Removed the SysExecuteOld function and replaced it with Process::Execute(). Rewrote the boot sequence in kernel.cpp such that it now loads the system idle process 'idle' as PID 0, and the initization process 'init' as PID 1. Rewrote the SIGINT hack. Processes now maintain a family-tree structure and keep track of their threads. PIDs are now allocated using a simple hack. Virtual memory per-process can now be allocated using a simple hack. Processes can now be forked. Fixed the Process::Execute function such that it now resets the stack pointer to where the stack actually is - not just a magic value. Removed the old and ugly Process::_endcodesection hack. Rewrote the scheduler into a much cleaner and faster version. Debug code is now moved to designated functions. The noop kernel-thread has been replaced by a simple user-space infinite-loop program 'idle'. The Thread class has been seperated from the Scheduler except in Scheduler- related code. Thread::{Save,Load}Registers has been improved and has been moved to $(CPU)/thread.cpp. Threads can now be forked. A new CreateThread function creates threads properly and portably. Added a MicrosecondsSinceBoot() function. Fixed a crucial bug in MemoryManagement::Fork(). Added an 'idle' user-space program that is a noop infinite loop, which is used by the scheduler when there is nothing to do. Rewrote the 'init' program such that it now forks off a shell, instead of becoming the shell. Added the $$ (current PID) and $PPID (parent PPID) variables to the shell.
2011-09-21 14:52:29 -04:00
2013-05-16 16:03:15 -04:00
// Set the default file creation mask.
umask(022);
2013-01-05 09:08:44 -05:00
// Set up the PATH variable.
const char* prefix = "/";
const char* cputype = getenv("cputype");
const char* suffix = "/bin";
char* path = new char[strlen(prefix) + strlen(cputype) + strlen(suffix) + 1];
stpcpy(stpcpy(stpcpy(path, prefix), cputype), suffix);
2013-06-01 07:52:36 -04:00
setenv("PATH", path, 1);
2013-01-05 09:08:44 -05:00
delete[] path;
2014-06-19 18:08:17 -04:00
// Set the terminal type.
setenv("TERM", "sortix", 1);
// Make sure that we have a /tmp directory.
mkdir("/tmp", 01777);
2014-09-30 16:59:34 -04:00
// Set the hostname as found in /etc/hostname.
set_hostname();
2013-06-01 07:52:36 -04:00
// Find the uuid of the root filesystem.
const char* root_uuid_file = "/etc/init/rootfs.uuid";
FILE* root_uuid_fp = fopen(root_uuid_file, "r");
// If there is no uuid of the root filesystem, the current root filesystem
// is the real and final root filesystem and we boot it.
if ( !root_uuid_fp && errno == ENOENT )
return runsystem();
if ( !root_uuid_fp )
init_emergency(errno, "unable to open: `%s'", root_uuid_file);
char* root_uuid = read_single_line(root_uuid_fp);
if ( !root_uuid )
init_emergency(errno, "unable to read: `%s'", root_uuid_file);
fclose(root_uuid_fp);
return chain_boot_uuid(root_uuid);
Implemented the fork() system call and what it needed to work properly. This commit got completely out of control. Added the fork(), getpid(), getppid(), sleep(), usleep() system calls, and aliases in the Maxsi:: namespace. Fixed a bug where zero-byte allocation would fail. Worked on the DescriptorTable class which now works and can fork. Got rid of some massive print-registers statements and replaced them with the portable InterruptRegisters::LogRegisters() function. Removed the SysExecuteOld function and replaced it with Process::Execute(). Rewrote the boot sequence in kernel.cpp such that it now loads the system idle process 'idle' as PID 0, and the initization process 'init' as PID 1. Rewrote the SIGINT hack. Processes now maintain a family-tree structure and keep track of their threads. PIDs are now allocated using a simple hack. Virtual memory per-process can now be allocated using a simple hack. Processes can now be forked. Fixed the Process::Execute function such that it now resets the stack pointer to where the stack actually is - not just a magic value. Removed the old and ugly Process::_endcodesection hack. Rewrote the scheduler into a much cleaner and faster version. Debug code is now moved to designated functions. The noop kernel-thread has been replaced by a simple user-space infinite-loop program 'idle'. The Thread class has been seperated from the Scheduler except in Scheduler- related code. Thread::{Save,Load}Registers has been improved and has been moved to $(CPU)/thread.cpp. Threads can now be forked. A new CreateThread function creates threads properly and portably. Added a MicrosecondsSinceBoot() function. Fixed a crucial bug in MemoryManagement::Fork(). Added an 'idle' user-space program that is a noop infinite loop, which is used by the scheduler when there is nothing to do. Rewrote the 'init' program such that it now forks off a shell, instead of becoming the shell. Added the $$ (current PID) and $PPID (parent PPID) variables to the shell.
2011-09-21 14:52:29 -04:00
}
2014-09-30 16:59:34 -04:00
int main(int argc, char* argv[])
{
if ( getpid() == 1 )
{
pid_t direct_child_pid = fork();
if ( direct_child_pid < 0 )
error(2, errno, "fork");
if ( direct_child_pid )
{
int status;
while ( true )
{
pid_t child_pid = waitpid(-1, &status, 0);
if ( child_pid < 0 )
error(2, errno, "waitpid");
if ( child_pid == direct_child_pid )
break;
}
int exit_value = WEXITSTATUS(status);
// TODO: Broadcast SIGKILL and wait for all processes to finish.
while ( 0 < waitpid(-1, &status, WNOHANG) )
{
}
return exit_value;
}
}
return init_main(argc, argv);
}