Goto sanos source index

//
// os.c
//
// Operating system API
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os.h>
#include <string.h>
#include <inifile.h>
#include <moddb.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <verinfo.h>

#include <os/seg.h>
#include <os/tss.h>
#include <os/syspage.h>
#include <os/pe.h>
#include <os/syscall.h>

#include "heap.h"
#include "resolv.h"

//
// Globals
//

struct critsect heap_lock;
struct critsect mod_lock;
struct critsect env_lock;

struct section *config;
struct peb *global;
struct moddb usermods;

struct term console = {TERM_CONSOLE, 80, 25, 0, 1};

//
// Forward declarations
//

int sprintf(char *buf, const char *fmt, ...);

void init_syscall();

void init_sntpd();
void init_threads(hmodule_t hmod, struct term *initterm);
void init_userdb();

void start_syslog();
void stop_syslog();

void globalhandler(struct siginfo *info);

//
// Error handling
//

void panic(const char *msg) {
  syslog(LOG_CRIT, "panic: %s", msg);
  exit(3);
}

int *_errno() {
  return &(gettib()->errnum);
}

void dbgbreak() {
  __asm { int 3 };
}

//
// File routines
//

int *_fmode() {
  return &getpeb()->fmodeval;
}

int __getstdhndl(int n) {
  return gettib()->proc->iob[n];
}

static int check_access(struct stat64 *st, int mode) {
  int uid = getuid();
  
  if (uid == 0) return 0;

  if (uid == st->st_uid) {
    mode <<= 6;
  } else if (getgid() == st->st_gid) {
    mode <<= 3;
  }

  if ((mode && st->st_mode) == 0) {
    errno = EACCES;
    return -1;
  }

  return 0;
}

handle_t creat(const char *name, int mode) {
  return open(name, O_CREAT | O_TRUNC | O_WRONLY, mode);
}

handle_t sopen(const char *name, int flags, int shflags, ...) {
  va_list args;
  int mode = 0;

  if (flags & O_CREAT) {
    va_start(args, shflags);
    mode = va_arg(args, int);
    va_end(args);
  }

  return open(name, FILE_FLAGS(flags, shflags), mode);
}

int eof(handle_t f) {
  return tell64(f) == fstat64(f, NULL);
}

int umask(int mask) {
  int oldmask;

  mask &= S_IRWXUGO;
  oldmask = getpeb()->umaskval;
  getpeb()->umaskval = mask;
  return oldmask;
}

loff_t filelength(handle_t f) {
  return fstat(f, NULL);
}

off64_t filelength64(handle_t f) {
  return fstat64(f, NULL);
}

int isatty(handle_t f)  {
  int rc, tty;
  
  rc = ioctl(f, IOCTL_GET_TTY, &tty, sizeof(tty));
  if (rc < 0) return rc;
  if (tty) {
    return 1;
  } else {
    errno = ENOTTY;
    return 0;
  }
}

int canonicalize(const char *filename, char *buffer, int size) {
  char *p;
  char *end;
  int len;

  // Check for maximum filename length
  if (!filename) {
    errno = EINVAL;
    return -1;
  }

  // Remove drive letter from filename (e.g. c:)
  if (filename[0] != 0 && filename[1] == ':') filename += 2;

  // Initialize buffer
  p = buffer;
  end = buffer + size;

  // Add current directory to filename if relative path
  if (*filename != PS1 && *filename != PS2) {
    char curdir[MAXPATH];

    // Do not add current directory if it is root directory
    if (!getcwd(curdir, MAXPATH)) return -1;
    len = strlen(curdir);
    if (len > 1) {
      memcpy(p, curdir, len);
      p += len;
    }
  }

  while (*filename) {
    // Parse path separator
    if (*filename == PS1 || *filename == PS2) filename++;
    if (p == end) {
      errno = ENAMETOOLONG;
      return -1;
    }
    *p++ = getpeb()->pathsep;

    // Parse next name part in path
    len = 0;
    while (*filename && *filename != PS1 && *filename != PS2) {
      // We do not allow control characters in filenames
      if (*filename > 0 && *filename < ' ') {
        errno = EINVAL;
        return -1;
      }
      if (p == end) {
        errno = ENAMETOOLONG;
        return -1;
      }
      *p++ = *filename++;
      len++;
    }

    // Handle empty name parts and '.' and '..'
    if (len == 0) {
      p--;
    } else if (len == 1 && filename[-1] == '.') {
      p -= 2;
    } else if (len == 2 && filename[-1] == '.' && filename[-2] == '.') {
      p -= 4;
      if (p < buffer) {
        errno = EINVAL;
        return -1;
      }
      while (*p != PS1) p--;
    }
  }

  // Convert empty filename to /
  if (p == buffer) *p++ = getpeb()->pathsep;

  // Terminate string
  if (p == end) {
    errno = ENAMETOOLONG;
    return -1;
  }

  *p = 0;

  return p - buffer;
}

//
// Heap routines
//

struct heap *create_module_heap(hmodule_t hmod) {
  size_t region_size;
  size_t group_size;

  region_size = get_image_header(hmod)->optional.size_of_heap_reserve & ~(PAGESIZE - 1);
  group_size = get_image_header(hmod)->optional.size_of_heap_commit & ~(PAGESIZE - 1);
  if (region_size == 0) region_size = 64 * 1024 * 1024;
  if (group_size < 128 * 1024) group_size = 128 * 1024;

  return heap_create(region_size, group_size);
}

void *malloc(size_t size) {
  void *p;

  //syslog(LOG_MODULE | LOG_DEBUG, "malloc %d bytes", size);

  enter(&heap_lock);
  p = heap_alloc(getpeb()->heap, size);
  leave(&heap_lock);

  if (!p && size) panic("malloc: out of memory");
  //if (!p && size) errno = ENOMEM;
  //syslog(LOG_MODULE | LOG_DEBUG, "malloced %d bytes at %p", size, p);

  return p;
}

void *realloc(void *mem, size_t size) {
  void *p;

  enter(&heap_lock);
  p = heap_realloc(getpeb()->heap, mem, size);
  leave(&heap_lock);

  if (!p && size) panic("realloc: out of memory");
  //if (!p && size) errno = ENOMEM;

  return p;
}

void *calloc(size_t num, size_t size) {
  void *p;

  enter(&heap_lock);
  p = heap_calloc(getpeb()->heap, num, size);
  leave(&heap_lock);

  if (!p && size * num != 0) panic("calloc: out of memory");
  //if (!p && size * num != 0) errno = ENOMEM;

  return p;
}

void free(void *p) {
  enter(&heap_lock);
  heap_free(getpeb()->heap, p);
  leave(&heap_lock);
}

struct mallinfo mallinfo() {
  struct mallinfo m;

  enter(&heap_lock);
  m = heap_mallinfo(getpeb()->heap);
  leave(&heap_lock);

  return m;
}

int malloc_usable_size(void *p) {
  return heap_malloc_usable_size(p);
}

struct heap *get_local_heap() {
  struct process *proc = gettib()->proc;
  if (!proc->heap) proc->heap = create_module_heap(proc->hmod);
  return proc->heap;
}

void *_lmalloc(size_t size) {
  return heap_alloc(get_local_heap(), size);
}

void *_lrealloc(void *mem, size_t size) {
  return heap_realloc(get_local_heap(), mem, size);
}

void *_lcalloc(size_t num, size_t size) {
  return heap_calloc(get_local_heap(), num, size);
}

void _lfree(void *p) {
  heap_free(get_local_heap(), p);
}

//
// Module routines
//

static int read_magic(char *filename, char *buffer, int size) {
  handle_t f;
  unsigned int bytes;

  // Open file
  f = open(filename, O_RDONLY | O_BINARY);
  if (f < 0) return -1;

  // Read headers
  if ((bytes = read(f, buffer, size)) < 0) {
    close(f);
    return -1;
  }

  // Close file
  close(f);

  return bytes;
}

static void *load_image(char *filename) {
  handle_t f;
  char *buffer;
  char *imgbase;
  struct dos_header *doshdr;
  struct image_header *imghdr;
  int i;
  unsigned int bytes;

  // Allocate header buffer
  buffer = malloc(PAGESIZE);
  if (!buffer) return NULL;
  memset(buffer, 0, PAGESIZE);

  // Open file
  f = open(filename, O_RDONLY | O_BINARY);
  if (f < 0) {
    free(buffer);
    return NULL;
  }

  // Read headers
  if ((bytes = read(f, buffer, PAGESIZE)) < 0) {
    close(f);
    free(buffer);
    return NULL;
  }
  
  doshdr = (struct dos_header *) buffer;
  imghdr = (struct image_header *) (buffer + doshdr->e_lfanew);

  // Check PE file signature
  if (doshdr->e_lfanew > bytes || imghdr->signature != IMAGE_PE_SIGNATURE) {
    close(f);
    free(buffer);
    return NULL;
  }

  // Check alignment
  //if (imghdr->optional.file_alignment != PAGESIZE || imghdr->optional.section_alignment != PAGESIZE) panic("image not page aligned");

  // Allocate memory for module
  imgbase = (char *) vmalloc(NULL, imghdr->optional.size_of_image, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE, 'UMOD');
  if (imgbase == NULL) {
    close(f);
    free(buffer);
    return NULL;
  }

  // copy header to image
  memcpy(imgbase, buffer, PAGESIZE);

  // Read sections
  for (i = 0; i < imghdr->header.number_of_sections; i++) {
    if (imghdr->sections[i].pointer_to_raw_data != 0) {
      if (lseek(f, imghdr->sections[i].pointer_to_raw_data, SEEK_SET) != imghdr->sections[i].pointer_to_raw_data) {
        vmfree(imgbase, imghdr->optional.size_of_image, MEM_RELEASE);
        close(f);
        free(buffer);
        return NULL;
      }

      if (read(f, RVA(imgbase, imghdr->sections[i].virtual_address), imghdr->sections[i].size_of_raw_data) != (int) imghdr->sections[i].size_of_raw_data) {
        vmfree(imgbase, imghdr->optional.size_of_image, MEM_RELEASE);
        close(f);
        free(buffer);
        return NULL;
      }
    }
  }

  //syslog(LOG_MODULE | LOG_DEBUG, "image %s loaded at %p (%d KB)", filename, imgbase, imghdr->optional.size_of_image / 1024);

  // Close file
  close(f);
  free(buffer);

  return imgbase;
}

static int unload_image(hmodule_t hmod, size_t size) {
  return vmfree(hmod, size, MEM_RELEASE);
}

static int protect_region(void *mem, size_t size, int protect) {
  return vmprotect(mem, size, protect);
}

static void logldr(char *msg) {
  syslog(LOG_MODULE | LOG_DEBUG, "mod: %s", msg);
}

void *dlsym(hmodule_t hmod, const char *procname) {
  void *addr;

  enter(&mod_lock);
  addr = get_proc_address(hmod, (char *) procname);
  leave(&mod_lock);
  return addr;
}

hmodule_t getmodule(const char *name) {
  hmodule_t hmod;

  if (name == NULL) return gettib()->proc->hmod;

  enter(&mod_lock);
  hmod = get_module_handle(&usermods, (char *) name);
  leave(&mod_lock);
  return hmod;
}

int getmodpath(hmodule_t hmod, char *buffer, int size) {
  int rc;

  if (hmod == NULL) hmod = gettib()->proc->hmod;

  enter(&mod_lock);
  rc = get_module_filename(&usermods, hmod, buffer, size);
  leave(&mod_lock);
  
  if (rc < 0) {
    errno = -rc;
    return -1;
  }

  return 0;
}

hmodule_t dlopen(const char *name, int mode) {
  hmodule_t hmod;
  int flags = 0;

  if (mode & RTLD_NOSHARE) flags |= MODLOAD_NOSHARE;
  if (mode & RTLD_EXE) flags |= MODLOAD_EXE;
  if (mode & RTLD_SCRIPT) flags |= MODLOAD_SCRIPT;

  enter(&mod_lock);
  errno = 0;
  hmod = load_module(&usermods, (char *) name, flags);
  if (hmod == NULL && errno == 0) errno = ENOEXEC;
  leave(&mod_lock);

  return hmod;
}

int dlclose(hmodule_t hmod) {
  int rc;

  enter(&mod_lock);
  rc = unload_module(&usermods, hmod);
  leave(&mod_lock);
  return rc;
}

char *dlerror() {
  return strerror(errno);
}

int exec(hmodule_t hmod, const char *args, char *env[]) {
  int rc;

  if (get_image_header(hmod)->header.characteristics & IMAGE_FILE_DLL) return -ENOEXEC;

  rc = ((int (*)(hmodule_t, char *, char **)) get_entrypoint(hmod))(hmod, (char *) args, env);

  return rc;
}

void *getresdata(hmodule_t hmod, int type, char *name, int lang, int *len) {
  void *data;
  int rc;

  if (hmod == NULL) hmod = gettib()->proc->hmod;

  rc = get_resource_data(hmod, INTRES(type), name, INTRES(lang), &data);
  if (rc < 0) {
    errno = -rc;
    return NULL;
  }

  if (len) *len = rc;
  return data;
}

int getreslen(hmodule_t hmod, int type, char *name, int lang) {
  void *data;
  int rc;

  if (hmod == NULL) hmod = gettib()->proc->hmod;

  rc = get_resource_data(hmod, INTRES(type), name, INTRES(lang), &data);
  if (rc < 0) {
    errno = -rc;
    return rc;
  }

  return rc;
}

struct verinfo *getverinfo(hmodule_t hmod) {
  struct verinfo *ver;

  if (hmod == NULL) hmod = gettib()->proc->hmod;
  ver = get_version_info(hmod);
  if (ver == NULL) errno = ENOENT;
  return ver;
}

int getvervalue(hmodule_t hmod, char *name, char *buf, int size) {
  int rc;

  if (hmod == NULL) hmod = gettib()->proc->hmod;
  rc = get_version_value(hmod, name, buf, size);
  if (rc < 0) {
    errno = -rc;
    return -1;
  }

  return rc;
}

#define MAX_FRAMES 10

static void error_output(const char *s) {
  write(fderr, s, strlen(s));
}

void dump_stack(struct context *ctxt) {
  struct tib *tib = gettib();
  struct stackframe frames[MAX_FRAMES];
  struct stackframe *f;
  int n, i;
  char buf[128];

  n = get_stack_trace(&usermods, ctxt, tib->stacktop, tib->stacklimit, frames, MAX_FRAMES);
  if (n > 0) error_output("Stack trace:\r\n");
  for (i = 0; i < n; ++i) {
    f = &frames[i];
    sprintf(buf, "%p", f->eip);
    error_output(buf);

    if (f->func) {
      char *func = f->func;
      char *fend = func;
      while (*fend && *fend != ':') fend++;
      error_output(" ");
      write(fderr, func, fend - func);
    }
    
    if (f->file) {
      error_output(" (");
      error_output(f->file);

      if (f->line != -1) {
        sprintf(buf, " line %d", f->line);
        error_output(buf);
      }
      error_output(")");
    }

    if (f->modname) {
      error_output(" [");
      error_output(f->modname);
      if (!f->func) {
        sprintf(buf, "+%x", (unsigned long) f->eip - (unsigned long) f->hmod);
        error_output(buf);
      }
      error_output("]");
    }

    error_output("\r\n");
  }
}

//
// Misc. routines
//

int uname(struct utsname *buf) {
  struct cpuinfo cpu;
  char machine[UTSNAMELEN];
  struct verinfo *ver;
  int osflags;
  char *build;
  struct tm tm;

  if (!buf) {
    errno = EINVAL;
    return -1;
  }

  memset(&cpu, 0, sizeof(struct cpuinfo));
  if (sysinfo(SYSINFO_CPU, &cpu, sizeof(struct cpuinfo)) < 0) return -1;
  if (*cpu.modelid) {
    strncpy(machine, cpu.modelid, UTSNAMELEN);
  } else {
    machine[0] = 'i';
    machine[1] = '0' + cpu.cpu_family;
    machine[2] = '8';
    machine[3] = '6';
    machine[4] = 0;
  }

  osflags = getpeb()->osversion.file_flags;
  if (osflags & VER_FLAG_PRERELEASE) {
    build = "prerelease ";
  } else if (osflags & VER_FLAG_PATCHED) {
    build = "patch ";
  } else if (osflags & VER_FLAG_PRIVATEBUILD) {
    build = "private ";
  } else if (osflags & VER_FLAG_DEBUG) {
    build = "debug ";
  } else {
    build = "";
  }

  gmtime_r(&getpeb()->ostimestamp, &tm);
  ver = &getpeb()->osversion;

  memset(buf, 0, sizeof(struct utsname));
  strncpy(buf->sysname, getpeb()->osname, UTSNAMELEN);
  gethostname(buf->nodename, UTSNAMELEN);
  sprintf(buf->release, "%d.%d.%d.%d", ver->file_major_version, ver->file_minor_version, ver->file_release_number, ver->file_build_number);
  sprintf(buf->version, "%s%04d-%02d-%02d %02d:%02d:%02d", build, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
  strncpy(buf->machine, machine, UTSNAMELEN);

  return 0;
}

unsigned sleep(unsigned seconds) {
  int rc;
  
  rc = msleep(seconds * 1000);
  if (rc > 0) return rc / 1000;
  return 0;
}

char *crypt(const char *key, const char *salt) {
  return crypt_r(key, salt, gettib()->cryptbuf);
}

struct section *osconfig() {
  return config;
}

struct peb *getpeb() {
  return (struct peb *) PEB_ADDRESS;
}

//
// Initialization
//

void init_net() {
  struct section *sect;
  struct property *prop;
  struct ifcfg ifcfg;
  struct sockaddr_in *sin;
  char str[256];
  int first;
  int rc;
  int sock;

  sock = socket(AF_INET, SOCK_DGRAM, 0);
  if (sock < 0) return;

  sect = find_section(config, "netif");
  if (!sect) return;

  first = 1;
  prop = sect->properties;
  while (prop) {
    memset(&ifcfg, 0, sizeof(ifcfg));

    strcpy(ifcfg.name, prop->name);

    if (get_option(prop->value, "ip", str, sizeof str, NULL)) {
      sin = (struct sockaddr_in *) &ifcfg.addr;
      sin->sin_family = AF_INET;
      sin->sin_addr.s_addr = inet_addr(str);
    } else {
      ifcfg.flags |= IFCFG_DHCP;
    }

    if (get_option(prop->value, "gw", str, sizeof str, NULL)) {
      sin = (struct sockaddr_in *) &ifcfg.gw;
      sin->sin_family = AF_INET;
      sin->sin_addr.s_addr = inet_addr(str);
    }

    if (get_option(prop->value, "mask", str, sizeof str, NULL)) {
      sin = (struct sockaddr_in *) &ifcfg.netmask;
      sin->sin_family = AF_INET;
      sin->sin_addr.s_addr = inet_addr(str);
    }

    if (get_option(prop->value, "broadcast", str, sizeof str, NULL)) {
      sin = (struct sockaddr_in *) &ifcfg.broadcast;
      sin->sin_family = AF_INET;
      sin->sin_addr.s_addr = inet_addr(str);
    }

    ifcfg.flags |= IFCFG_UP;
    if (first) ifcfg.flags |= IFCFG_DEFAULT;

    rc = ioctl(sock, SIOIFCFG, &ifcfg, sizeof(struct ifcfg));
    if (rc < 0) {
      syslog(LOG_ERR, "%s: unable to configure net interface, %s", ifcfg.name, strerror(errno));
    } else {
      unsigned long addr = ((struct sockaddr_in *) &ifcfg.addr)->sin_addr.s_addr;
      unsigned long gw = ((struct sockaddr_in *) &ifcfg.gw)->sin_addr.s_addr;
      unsigned long mask = ((struct sockaddr_in *) &ifcfg.netmask)->sin_addr.s_addr;
      unsigned long bcast = ((struct sockaddr_in *) &ifcfg.broadcast)->sin_addr.s_addr;

      //syslog(LOG_INFO, "%s: addr %a mask %a gw %a bcast %a", ifcfg.name, &addr, &mask, &gw, &bcast);

      if (first) getpeb()->ipaddr.s_addr = addr;
    }

    prop = prop->next;
    first = 0;
  }

  close(sock);
}

void init_hostname() {
  struct hostent *hp;
  char *dot;
  int len;

  // Get hostname and domain from os config
  if (!*getpeb()->hostname) {
    char *host = get_property(config, "os", "hostname", NULL);
    if (host) strcpy(getpeb()->hostname, host);
  }

  if (!*getpeb()->default_domain) {
    char *domain = get_property(config, "dns", "domain", NULL);
    if (domain) strcpy(getpeb()->default_domain, domain);
  }

  // Check for hostname already set by configuration or DHCP
  if (*getpeb()->hostname) return;

  // Check for any IP address configured
  if (getpeb()->ipaddr.s_addr == INADDR_ANY) return;

  // Try to lookup hostname from the ip address using DNS
  hp = gethostbyaddr((char *) &getpeb()->ipaddr, sizeof(struct in_addr), AF_INET);
  if (!hp || !hp->h_name || !*hp->h_name) return;

  // Check that domain name matches
  dot = strchr(hp->h_name, '.');
  if (!dot) return;
  if (strcmp(dot + 1, getpeb()->default_domain) != 0) return;

  // Copy hostname from DNS to PEB
  len = dot - hp->h_name;
  if (len >= sizeof(getpeb()->hostname)) return;
  memcpy(getpeb()->hostname, hp->h_name, len);
  getpeb()->hostname[len] = 0;
}

void init_mount() {
  struct section *sect;
  struct property *prop;
  char devname[256];
  char *type;
  char *opts;
  int rc;

  sect = find_section(config, "mount");
  if (!sect) return;

  prop = sect->properties;
  while (prop) {
    strcpy(devname, prop->value ? prop->value : "");
    type = strchr(devname, ',');
    if (type) {
      *type++ = 0;
      while (*type == ' ') type++;
      opts = strchr(type, ',');
      if (opts) {
        *opts++ = 0;
        while (*opts == ' ') opts++;
      } else {
        opts = NULL;
      }
    } else {
      type = "dfs";
      opts = NULL;
    }

    //syslog(LOG_DEBUG, "mount %s on %s type %s opts %s", devname, prop->name, type, opts);

    rc = mount(type, prop->name, devname, opts);
    if (rc < 0) syslog(LOG_ERR, "%s: error %d mounting %s %s", prop->name, errno, type, devname);

    prop = prop->next;
  }
}

//
// Main user mode entry point
//

int __stdcall start(hmodule_t hmod, void *reserved, void *reserved2) {
  int rc;
  char *init;

  // Initialize process environment block (PEB)
  getpeb()->globalhandler = globalhandler;
  getpeb()->fmodeval = O_BINARY; //O_TEXT;
  getpeb()->umaskval = S_IWGRP | S_IWOTH;
#if DEBUG
  getpeb()->debug = 1;
#endif

  // Initialize syscall handler
  init_syscall();

  // Initialize global heap
  getpeb()->heap = create_module_heap(hmod);
  if (!getpeb()->heap) panic("unable to create global heap");

  // Initialize locks
  mkcs(&heap_lock);
  mkcs(&mod_lock);
  mkcs(&env_lock);

  // Load configuration file
  config = read_properties("/etc/os.ini");
  getpeb()->debug = get_numeric_property(config, "os", "debug", getpeb()->debug);

  // Initialize initial process
  init_threads(hmod, &console);

  // Initialize network interfaces
  init_net();

  // Initialize resolver
  res_init();

  // Determine hostname
  init_hostname();

  // Initialize NTP daemon
  init_sntpd();

  // Initialize user module database
  getpeb()->usermods = &usermods;
  usermods.read_magic = read_magic;
  usermods.load_image = load_image;
  usermods.unload_image = unload_image;
  usermods.protect_region = protect_region;
  usermods.log = logldr;
  init_module_database(&usermods, "os.dll", hmod, get_property(config, "os", "libpath", "/bin"), find_section(config, "modaliases"), 0);

  // Load user database
  init_userdb();
  initgroups("root", 0);

  // Mount devices
  init_mount();

  // Initialize log
  start_syslog();

  // Run init commands
  if (!getpeb()->rcdone) {
    char *rcscript = get_property(config, "os", "rc", NULL);
    if (rcscript) {
      rc = spawn(P_WAIT, NULL, rcscript, NULL, NULL);
      if (rc != 0) {
        syslog(LOG_ERR, "%s: returned error code %d", rcscript, rc);
      }
    }
    getpeb()->rcdone = 1;
  }

  // Load and execute init program
  init = get_property(config, "os", "init", "/bin/sh");
  while (1) {
    rc = spawn(P_WAIT, NULL, init, NULL, NULL);
    if (rc != 0) {
      syslog(LOG_DEBUG, "Init returned exit code %d: %s", rc, init);
      sleep(1);
    }
  }
}