sos-code-article10/userland/libc.c

809 lines
14 KiB
C

/* Copyright (C) 2005 David Decotigny
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.
*/
#include "crt.h"
#include "string.h"
#include "stdarg.h"
#include "libc.h"
void exit (int status)
{
_sos_exit(status);
}
pid_t fork(void)
{
return _sos_fork();
}
pid_t getpid()
{
return _sos_getpid();
}
/**
* Helper to transform the array of argv and envp into an area
* suitable for the crt.c:unmarshall_argv_envp() function.
* @see crt.c for the format of this array.
*/
static void const* marshall_execve_parameters(char* const argv[],
char* const envp[],
/*out*/size_t * sz)
{
size_t i, i_offset, count_argv, count_envp, size;
char * str;
void * result;
addr_t * offset;
*sz = 0;
/* Count the number of argv parameters and the size to allocate */
for (count_argv = 0, size = 0 ; argv && argv[count_argv] != NULL ; count_argv ++)
size += strlen(argv[count_argv]) + 1 /* trailing \0 */;
/* Count the number of envp parameters and the size to allocate */
for (count_envp = 0 ; envp && envp[count_envp] != NULL ; count_envp ++)
size += strlen(envp[count_envp]) + 1 /* trailing \0 */;
size += (count_argv + count_envp + 3) * sizeof(addr_t);
result = malloc(size);
if (NULL == result)
return NULL;
/* Build the array of offsets and the string contents */
offset = (addr_t*)result;
str = (char*) & offset[count_argv + count_envp + 3];
i_offset = 0;
/* First element of the array is the number of argv entries */
offset [i_offset++] = count_argv;
/* Marshall the argv array */
for (i = 0 ; i < count_argv ; i ++, i_offset ++)
{
char const* c;
offset[i_offset] = (void*)str - result;
for (c = argv[i] ; *c ; c ++, str ++)
*str = *c;
*str = '\0'; str ++;
}
/* The NULL between argv and envp */
offset [i_offset++] = 0;
for (i = 0 ; i < count_envp ; i ++, i_offset ++)
{
char const* c;
offset[i_offset] = (void*)str - result;
for (c = envp[i] ; *c ; c ++, str ++)
*str = *c;
*str = '\0'; str ++;
}
/* Final NULL */
offset[count_argv + count_envp + 2] = 0;
*sz = size;
return result;
}
int execve(const char *filename,
char *const argv [],
char *const envp[])
{
void const* args_area;
size_t args_sz;
int retval;
args_area = marshall_execve_parameters(argv, envp, & args_sz);
retval = _sos_exec(filename,
args_area, args_sz);
if (NULL != args_area)
free((void*)args_area);
return retval;
}
int execv(const char * filename,
char * const argv [])
{
return execve(filename, argv, environ);
}
static int compute_nargs(va_list ap)
{
int nargs = 0;
while (va_arg(ap, char*))
nargs ++;
return nargs;
}
static int vexec(const char *path, va_list ap, int envp_in_list)
{
int retval;
char ** vector, **entry;
char *str;
char ** envp;
size_t nargs = compute_nargs(ap);
/* Allocate the char* vector */
vector = malloc((nargs + 1)*sizeof(char*));
if (! vector)
return -1;
/* Fill it */
entry = vector;
while ((str = va_arg(ap, char*)) != NULL)
{
*entry = str;
entry ++;
}
*entry = NULL;
if (envp_in_list)
envp = (char**)va_arg(ap, char*);
else
envp = environ;
retval = execve(path, vector, envp);
free(vector);
return retval;
}
int execl(const char *path, ...)
{
int retval;
va_list ap;
va_start(ap, path);
retval = vexec(path, ap, FALSE);
va_end(ap);
return retval;
}
int execle(const char *path, ...)
{
int retval;
va_list ap;
va_start(ap, path);
retval = vexec(path, ap, TRUE);
va_end(ap);
return retval;
}
int exec(char * const filename)
{
return execl(filename, filename, NULL);
}
int sleep(unsigned int seconds)
{
return nanosleep(seconds, 0);
}
int nanosleep(unsigned long int sec,
unsigned long int nanosec)
{
return _sos_nanosleep(sec, nanosec);
}
/**
* Max number of environment variables
*/
#define SOS_MAX_ENVVARS 1024
char **environ = NULL;
/**
* Compare the keys of two strings of the form "key=val"
*/
static int equal_key(const char *e1,
const char *e2)
{
for ( ; e1 && (*e1) && e2 && (*e2) && (*e1 == *e2) ; e1++, e2++)
if (*e1 == '=')
return TRUE;
return FALSE;
}
/**
* Helper function to register an environment variable
*/
static int registerenv(char * string, int can_overwrite)
{
int i;
char ** overwritten;
/* The first time: allocate the environ table */
if (! environ)
{
environ = malloc(SOS_MAX_ENVVARS * sizeof(char*));
if (! environ)
return 0;
memset(environ, 0x0, SOS_MAX_ENVVARS * sizeof(char*));
}
/* Walk the environment variables */
overwritten = NULL;
for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++)
{
/* Reached end of list ? */
if (! environ[i])
break;
/* Variable already present ? */
if (equal_key(environ[i], string))
overwritten = & environ[i];
}
if (overwritten)
{
/* Not allowed to overwrite it ! */
if (! can_overwrite)
return -1;
/* Overwrite allowed: do it now */
free(*overwritten);
*overwritten = string;
return 0;
}
/* Must add the variable */
/* No place left ? */
if (i >= SOS_MAX_ENVVARS)
return -1;
/* Ok, add it at the end */
environ[i] = string;
return 0;
}
/**
* @return TRUE when the key of the "keyvalpair" pair is "key"
*/
static int key_is(char * keyvalpair, const char * key,
char ** /*out*/value)
{
for ( ; keyvalpair && *keyvalpair && key && *key ; keyvalpair ++, key ++)
if (*key != *keyvalpair)
break;
if (value)
*value = keyvalpair + 1;
return (keyvalpair && (*keyvalpair == '=') && key && (*key == '\0'));
}
int putenv(char * string)
{
char * str;
if (! string)
return -1;
str = strdup(string);
if (! str)
return -1;
return registerenv(string, TRUE);
}
int setenv(const char *name, const char *value, int overwrite)
{
char * str, * c;
size_t sz_name, sz_value;
if (!name || !value)
return -1;
/* Allocate space for the "name=value" string */
sz_name = strlen(name);
sz_value = strlen(value);
str = malloc(sz_name + 1 + sz_value + 1);
if (! str)
return -1;
/* sprintf(str, "%s=%s", name, value); */
c = str;
*c = '\0';
strzcpy(c, name, sz_name+1); c += sz_name;
strzcpy(c, "=", 2); c += 1;
strzcpy(c, value, sz_value+1);
return registerenv(str, overwrite);
}
char *getenv(const char *name)
{
int i;
if (! environ)
return NULL;
for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++)
{
char *value;
/* Reached end of list ? */
if (! environ[i])
return NULL;
/* Variable already present ? */
if (key_is(environ[i], name, & value))
return value;
}
return NULL;
}
void unsetenv(const char *name)
{
int i;
char ** entry, ** last_entry;
if (! environ)
return;
/* Walk the environment variables */
entry = last_entry = NULL;
for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++)
{
/* Reached end of list ? */
if (! environ[i])
break;
/* Variable already present ? */
if (key_is(environ[i], name, NULL))
entry = & environ[i];
else if (entry)
last_entry = & environ[i];
}
/* Found nothing ! */
if (! entry)
return;
/* Found it: erase it... */
free(*entry);
*entry = NULL;
/* ... and replace it with the last entry */
if (last_entry)
{
*entry = *last_entry;
*last_entry = NULL;
}
}
int clearenv(void)
{
int i;
if (! environ)
return 0;
for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++)
{
if (! environ[i])
break;
free(environ[i]);
environ[i] = NULL;
}
return 0;
}
int munmap(void * start, size_t length)
{
return _sos_munmap(start, length);
}
void * mremap(void * old_addr, size_t old_len,
size_t new_len,
unsigned long flags)
{
void * new_uaddr = old_addr;
if (0 != _sos_mresize(old_addr, old_len, & new_uaddr, new_len, flags))
return NULL;
return new_uaddr;
}
int mprotect(const void *addr, size_t len, int prot)
{
return _sos_mprotect(addr, len, prot);
}
int msync(void *start, size_t length, int flags)
{
return _sos_msync(start, length, flags);
}
/**
* The presence of this global variable without any protected access
* to it explains why the "brk/sbrk" functions below are MT-unsafe !
*/
static void * kernel_heap_top = NULL;
int brk(void *end_data_seg)
{
if (! end_data_seg)
return -1;
kernel_heap_top = _sos_brk(end_data_seg);
return 0;
}
void *sbrk(ptrdiff_t increment)
{
if (! kernel_heap_top)
kernel_heap_top = _sos_brk(0);
kernel_heap_top = _sos_brk(kernel_heap_top + increment);
return kernel_heap_top;
}
void * calloc (size_t nmemb, size_t size)
{
return malloc(nmemb * size);
}
/**
* The presence of this global variable without any protected access
* to it explains why the "malloc/calloc" functions below are
* MT-unsafe !
*/
static void * malloc_heap_top = NULL;
void * malloc (size_t size)
{
void * retval;
if (size <= 0)
return NULL;
/* Align on a 4B boundary */
size = ((size-1) & ~3) + 4;
if (! kernel_heap_top)
kernel_heap_top = _sos_brk(0);
if (! malloc_heap_top)
malloc_heap_top = kernel_heap_top;
retval = malloc_heap_top;
malloc_heap_top += size;
_sos_brk(malloc_heap_top);
return retval;
}
void free(void *ptr)
{
// bochs_printf("Free ignored (not implemented yet)\n");
}
int mount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const char *data)
{
return _sos_mount(source, target, filesystemtype, mountflags, data);
}
int umount(const char *target)
{
return _sos_umount(target);
}
void sync(void)
{
return _sos_sync();
}
int statvfs(const char *path, struct statvfs *buf)
{
return _sos_statvfs(path, buf);
}
int open(const char *pathname, int flags, /* mode_t mode */...)
{
va_list ap;
unsigned int mode = 0;
va_start(ap, flags);
if (flags & O_CREAT)
mode = va_arg(ap, unsigned int);
va_end(ap);
return _sos_open(pathname, flags, mode);
}
int close(int fd)
{
return _sos_close(fd);
}
int read(int fd, char * buf, size_t len)
{
int retval = _sos_read(fd, buf, & len);
if (retval < 0)
return retval;
return len;
}
int write(int fd, const char * buf, size_t len)
{
int retval = _sos_write(fd, buf, & len);
if (retval < 0)
return retval;
return len;
}
off_t lseek(int fd, off_t offset, int whence)
{
loff_t result = offset;
int retval = _sos_seek64(fd, & result, whence);
if (retval < 0)
return retval;
return result;
}
loff_t lseek64(int fd, loff_t offset, int whence)
{
loff_t result = offset;
int retval = _sos_seek64(fd, & result, whence);
if (retval < 0)
return retval;
return result;
}
void * mmap(void *start, size_t length, int prot , int flags,
int fd, loff_t offset)
{
/* At kernel side, offset is considered positive */
if (offset < 0)
return NULL;
if (0 != _sos_fmmap(& start, length, prot, flags, fd, offset))
return NULL;
return start;
}
int ftruncate(int fd, off_t length)
{
return _sos_ftruncate64(fd, length);
}
int ftruncate64(int fd, loff_t length)
{
return _sos_ftruncate64(fd, length);
}
int fcntl(int fd, int cmd, int arg)
{
return _sos_fcntl(fd, cmd, arg);
}
int ioctl(int fd, int cmd, int arg)
{
return _sos_ioctl(fd, cmd, arg);
}
int creat(const char *pathname, mode_t mode)
{
return _sos_creat(pathname, mode);
}
int link (const char *oldpath, const char *newpath)
{
return _sos_link(oldpath, newpath);
}
int unlink(const char *pathname)
{
return _sos_unlink(pathname);
}
int rename(const char *oldpath, const char *newpath)
{
return _sos_rename(oldpath, newpath);
}
int symlink(const char *target, const char *path)
{
return _sos_symlink(target, path);
}
int mknod(const char *pathname, mode_t mode,
int type,
unsigned int major, unsigned minor)
{
if (type == S_IFREG)
return creat(pathname, mode);
return _sos_mknod(pathname, mode, type,
major, minor);
}
int mkdir(const char *pathname, mode_t mode)
{
return _sos_mkdir(pathname, mode);
}
int rmdir(const char *pathname)
{
return _sos_rmdir(pathname);
}
int chmod(const char *path, mode_t mode)
{
return _sos_chmod(path, mode);
}
struct sos_DIR_struct
{
int fd;
struct dirent dirent;
};
DIR *opendir(const char *name)
{
DIR * result = malloc(sizeof(DIR));
if (! result)
return NULL;
result->fd = _sos_open(name, O_DIRECTORY | O_RDONLY, 0);
return result;
}
int dirfd(const DIR * dir)
{
if (dir)
return dir->fd;
return -1;
}
struct dirent *readdir(DIR *dir)
{
int retval = _sos_readdir(dir->fd, & dir->dirent);
if (retval < 0)
return NULL;
return & dir->dirent;
}
int closedir(DIR *dir)
{
close(dir->fd);
free(dir);
return 0;
}
int stat(const char *file_name, struct stat *buf)
{
return _sos_stat(file_name, TRUE, buf);
}
int lstat(const char *file_name, struct stat *buf)
{
return _sos_stat(file_name, FALSE, buf);
}
int chroot(const char *path)
{
return _sos_chroot(path);
}
int chdir(const char *path)
{
return _sos_chdir(path);
}
int fchdir(int fd)
{
return _sos_fchdir(fd);
}
int printf (const char *format, ...)
{
char buff[4096];
va_list ap;
va_start(ap, format);
vsnprintf(buff, sizeof(buff), format, ap);
va_end(ap);
return write (1, buff, strlen(buff));
}