HelenOS sources

root/kernel/generic/src/sysinfo/sysinfo.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sysinfo_item_constructor
  2. sysinfo_item_destructor
  3. sysinfo_init
  4. sysinfo_find_item
  5. sysinfo_create_path
  6. sysinfo_set_item_val
  7. sysinfo_set_item_data
  8. sysinfo_set_item_gen_val
  9. sysinfo_set_item_gen_data
  10. sysinfo_set_item_undefined
  11. sysinfo_set_subtree_fn
  12. sysinfo_indent
  13. sysinfo_dump_internal
  14. sysinfo_dump
  15. sysinfo_get_item
  16. sysinfo_get_item_uspace
  17. sysinfo_get_keys
  18. sysinfo_get_keys_uspace
  19. sys_sysinfo_get_keys_size
  20. sys_sysinfo_get_keys
  21. sys_sysinfo_get_val_type
  22. sys_sysinfo_get_value
  23. sys_sysinfo_get_data_size
  24. sys_sysinfo_get_data

/*
 * Copyright (c) 2006 Jakub Vana
 * Copyright (c) 2012 Martin Decky
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - 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.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 */

/** @addtogroup kernel_generic
 * @{
 */
/** @file
 */

#include <assert.h>
#include <sysinfo/sysinfo.h>
#include <mm/slab.h>
#include <stdio.h>
#include <syscall/copy.h>
#include <synch/mutex.h>
#include <arch/asm.h>
#include <errno.h>
#include <macros.h>
#include <stdlib.h>

/** Maximal sysinfo path length */
#define SYSINFO_MAX_PATH  2048

bool fb_exported = false;

/** Global sysinfo tree root item */
static sysinfo_item_t *global_root = NULL;

/** Sysinfo SLAB cache */
static slab_cache_t *sysinfo_item_cache;

/** Sysinfo lock */
static mutex_t sysinfo_lock;

/** Sysinfo item constructor
 *
 */
_NO_TRACE static errno_t sysinfo_item_constructor(void *obj, unsigned int kmflag)
{
        sysinfo_item_t *item = (sysinfo_item_t *) obj;

        item->name = NULL;
        item->val_type = SYSINFO_VAL_UNDEFINED;
        item->subtree_type = SYSINFO_SUBTREE_NONE;
        item->subtree.table = NULL;
        item->next = NULL;

        return EOK;
}

/** Sysinfo item destructor
 *
 * Note that the return value is not perfectly correct
 * since more space might get actually freed thanks
 * to the disposal of item->name
 *
 */
_NO_TRACE static size_t sysinfo_item_destructor(void *obj)
{
        sysinfo_item_t *item = (sysinfo_item_t *) obj;

        if (item->name != NULL)
                free(item->name);

        return 0;
}

/** Initialize sysinfo subsystem
 *
 * Create SLAB cache for sysinfo items.
 *
 */
void sysinfo_init(void)
{
        sysinfo_item_cache = slab_cache_create("sysinfo_item_t",
            sizeof(sysinfo_item_t), 0, sysinfo_item_constructor,
            sysinfo_item_destructor, SLAB_CACHE_MAGDEFERRED);

        mutex_initialize(&sysinfo_lock, MUTEX_ACTIVE);
}

/** Recursively find an item in sysinfo tree
 *
 * Should be called with sysinfo_lock held.
 *
 * @param name    Current sysinfo path suffix.
 * @param subtree Current sysinfo (sub)tree root item.
 * @param ret     If the return value is NULL, this argument
 *                can be set either to NULL (i.e. no item was
 *                found and no data was generated) or the
 *                original pointer is used to store the value
 *                generated by a generated subtree function.
 * @param dry_run Do not actually get any generated
 *                binary data, just calculate the size.
 *
 * @return Found item or NULL if no item in the fixed tree
 *         was found (N.B. ret).
 *
 */
_NO_TRACE static sysinfo_item_t *sysinfo_find_item(const char *name,
    sysinfo_item_t *subtree, sysinfo_return_t **ret, bool dry_run)
{
        assert(subtree != NULL);

        sysinfo_item_t *cur = subtree;

        /* Walk all siblings */
        while (cur != NULL) {
                size_t i = 0;

                /* Compare name with path */
                while ((cur->name[i] != 0) && (name[i] == cur->name[i]))
                        i++;

                /* Check for perfect name and path match */
                if ((name[i] == 0) && (cur->name[i] == 0))
                        return cur;

                /* Partial match up to the delimiter */
                if ((name[i] == '.') && (cur->name[i] == 0)) {
                        /* Look into the subtree */
                        switch (cur->subtree_type) {
                        case SYSINFO_SUBTREE_TABLE:
                                /* Recursively find in subtree */
                                return sysinfo_find_item(name + i + 1,
                                    cur->subtree.table, ret, dry_run);
                        case SYSINFO_SUBTREE_FUNCTION:
                                /* Get generated data */
                                if (ret != NULL)
                                        **ret = cur->subtree.generator.fn(name + i + 1,
                                            dry_run, cur->subtree.generator.data);

                                return NULL;
                        default:
                                /* Not found, no data generated */
                                if (ret != NULL)
                                        *ret = NULL;

                                return NULL;
                        }
                }

                cur = cur->next;
        }

        /* Not found, no data generated */
        if (ret != NULL)
                *ret = NULL;

        return NULL;
}

/** Recursively create items in sysinfo tree
 *
 * Should be called with sysinfo_lock held.
 *
 * @param name     Current sysinfo path suffix.
 * @param psubtree Pointer to an already existing (sub)tree root
 *                 item or where to store a new tree root item.
 *
 * @return Existing or newly allocated sysinfo item or NULL
 *         if the current tree configuration does not allow to
 *         create a new item.
 *
 */
_NO_TRACE static sysinfo_item_t *sysinfo_create_path(const char *name,
    sysinfo_item_t **psubtree)
{
        assert(psubtree != NULL);

        if (*psubtree == NULL) {
                /* No parent */

                size_t i = 0;

                /* Find the first delimiter in name */
                while ((name[i] != 0) && (name[i] != '.'))
                        i++;

                *psubtree =
                    (sysinfo_item_t *) slab_alloc(sysinfo_item_cache, FRAME_ATOMIC);
                if (!*psubtree)
                        return NULL;

                /* Fill in item name up to the delimiter */
                (*psubtree)->name = str_ndup(name, i);
                if (!(*psubtree)->name) {
                        slab_free(sysinfo_item_cache, *psubtree);
                        return NULL;
                }

                /* Create subtree items */
                if (name[i] == '.') {
                        (*psubtree)->subtree_type = SYSINFO_SUBTREE_TABLE;
                        sysinfo_item_t *item = sysinfo_create_path(name + i + 1,
                            &((*psubtree)->subtree.table));
                        if (!item) {
                                free((*psubtree)->name);
                                slab_free(sysinfo_item_cache, *psubtree);
                        }
                        return item;
                }

                /* No subtree needs to be created */
                return *psubtree;
        }

        sysinfo_item_t *cur = *psubtree;

        /* Walk all siblings */
        while (cur != NULL) {
                size_t i = 0;

                /* Compare name with path */
                while ((cur->name[i] != 0) && (name[i] == cur->name[i]))
                        i++;

                /*
                 * Check for perfect name and path match
                 * -> item is already present.
                 */
                if ((name[i] == 0) && (cur->name[i] == 0))
                        return cur;

                /* Partial match up to the delimiter */
                if ((name[i] == '.') && (cur->name[i] == 0)) {
                        switch (cur->subtree_type) {
                        case SYSINFO_SUBTREE_NONE:
                                /* No subtree yet, create one */
                                cur->subtree_type = SYSINFO_SUBTREE_TABLE;
                                return sysinfo_create_path(name + i + 1,
                                    &(cur->subtree.table));
                        case SYSINFO_SUBTREE_TABLE:
                                /* Subtree already created, add new sibling */
                                return sysinfo_create_path(name + i + 1,
                                    &(cur->subtree.table));
                        default:
                                /*
                                 * Subtree items handled by a function, this
                                 * cannot be overriden by a constant item.
                                 */
                                return NULL;
                        }
                }

                /*
                 * No match and no more siblings to check
                 * -> create a new sibling item.
                 */
                if (cur->next == NULL) {
                        /* Find the first delimiter in name */
                        i = 0;
                        while ((name[i] != 0) && (name[i] != '.'))
                                i++;

                        sysinfo_item_t *item =
                            (sysinfo_item_t *) slab_alloc(sysinfo_item_cache, FRAME_ATOMIC);
                        if (!item)
                                return NULL;

                        cur->next = item;

                        /* Fill in item name up to the delimiter */
                        item->name = str_ndup(name, i);
                        if (!item->name) {
                                slab_free(sysinfo_item_cache, item);
                                return NULL;
                        }

                        /* Create subtree items */
                        if (name[i] == '.') {
                                item->subtree_type = SYSINFO_SUBTREE_TABLE;
                                sysinfo_item_t *sub = sysinfo_create_path(
                                    name + i + 1, &(item->subtree.table));
                                if (!sub) {
                                        free(item->name);
                                        slab_free(sysinfo_item_cache, item);
                                        return NULL;
                                }
                                return sub;
                        }

                        /* No subtree needs to be created */
                        return item;
                }

                cur = cur->next;
        }

        unreachable();
}

/** Set sysinfo item with a constant numeric value
 *
 * @param name Sysinfo path.
 * @param root Pointer to the root item or where to store
 *             a new root item (NULL for global sysinfo root).
 * @param val  Value to store in the item.
 *
 */
void sysinfo_set_item_val(const char *name, sysinfo_item_t **root,
    sysarg_t val)
{
        /* Protect sysinfo tree consistency */
        mutex_lock(&sysinfo_lock);

        if (root == NULL)
                root = &global_root;

        sysinfo_item_t *item = sysinfo_create_path(name, root);
        if (item != NULL) {
                item->val_type = SYSINFO_VAL_VAL;
                item->val.val = val;
        } else {
                printf("Could not set sysinfo item %s.\n", name);
        }

        mutex_unlock(&sysinfo_lock);
}

/** Set sysinfo item with a constant binary data
 *
 * Note that sysinfo only stores the pointer to the
 * binary data and does not touch it in any way. The
 * data should be static and immortal.
 *
 * @param name Sysinfo path.
 * @param root Pointer to the root item or where to store
 *             a new root item (NULL for global sysinfo root).
 * @param data Binary data.
 * @param size Size of the binary data.
 *
 */
void sysinfo_set_item_data(const char *name, sysinfo_item_t **root,
    void *data, size_t size)
{
        /* Protect sysinfo tree consistency */
        mutex_lock(&sysinfo_lock);

        if (root == NULL)
                root = &global_root;

        sysinfo_item_t *item = sysinfo_create_path(name, root);
        if (item != NULL) {
                item->val_type = SYSINFO_VAL_DATA;
                item->val.data.data = data;
                item->val.data.size = size;
        } else {
                printf("Could not set sysinfo item %s.\n", name);
        }

        mutex_unlock(&sysinfo_lock);
}

/** Set sysinfo item with a generated numeric value
 *
 * @param name Sysinfo path.
 * @param root Pointer to the root item or where to store
 *             a new root item (NULL for global sysinfo root).
 * @param fn   Numeric value generator function.
 * @param data Private data.
 *
 */
void sysinfo_set_item_gen_val(const char *name, sysinfo_item_t **root,
    sysinfo_fn_val_t fn, void *data)
{
        /* Protect sysinfo tree consistency */
        mutex_lock(&sysinfo_lock);

        if (root == NULL)
                root = &global_root;

        sysinfo_item_t *item = sysinfo_create_path(name, root);
        if (item != NULL) {
                item->val_type = SYSINFO_VAL_FUNCTION_VAL;
                item->val.gen_val.fn = fn;
                item->val.gen_val.data = data;
        } else {
                printf("Could not set sysinfo item %s.\n", name);
        }

        mutex_unlock(&sysinfo_lock);
}

/** Set sysinfo item with a generated binary data
 *
 * Note that each time the generator function is called
 * it is supposed to return a new dynamically allocated
 * data. This data is then freed by sysinfo in the context
 * of the current sysinfo request.
 *
 * @param name Sysinfo path.
 * @param root Pointer to the root item or where to store
 *             a new root item (NULL for global sysinfo root).
 * @param fn   Binary data generator function.
 * @param data Private data.
 *
 */
void sysinfo_set_item_gen_data(const char *name, sysinfo_item_t **root,
    sysinfo_fn_data_t fn, void *data)
{
        /* Protect sysinfo tree consistency */
        mutex_lock(&sysinfo_lock);

        if (root == NULL)
                root = &global_root;

        sysinfo_item_t *item = sysinfo_create_path(name, root);
        if (item != NULL) {
                item->val_type = SYSINFO_VAL_FUNCTION_DATA;
                item->val.gen_data.fn = fn;
                item->val.gen_data.data = data;
        } else {
                printf("Could not set sysinfo item %s.\n", name);
        }

        mutex_unlock(&sysinfo_lock);
}

/** Set sysinfo item with an undefined value
 *
 * @param name Sysinfo path.
 * @param root Pointer to the root item or where to store
 *             a new root item (NULL for global sysinfo root).
 *
 */
void sysinfo_set_item_undefined(const char *name, sysinfo_item_t **root)
{
        /* Protect sysinfo tree consistency */
        mutex_lock(&sysinfo_lock);

        if (root == NULL)
                root = &global_root;

        sysinfo_item_t *item = sysinfo_create_path(name, root);
        if (item != NULL)
                item->val_type = SYSINFO_VAL_UNDEFINED;
        else
                printf("Could not set sysinfo item %s.\n", name);

        mutex_unlock(&sysinfo_lock);
}

/** Set sysinfo item with a generated subtree
 *
 * @param name Sysinfo path.
 * @param root Pointer to the root item or where to store
 *             a new root item (NULL for global sysinfo root).
 * @param fn   Subtree generator function.
 * @param data Private data to be passed to the generator.
 *
 */
void sysinfo_set_subtree_fn(const char *name, sysinfo_item_t **root,
    sysinfo_fn_subtree_t fn, void *data)
{
        /* Protect sysinfo tree consistency */
        mutex_lock(&sysinfo_lock);

        if (root == NULL)
                root = &global_root;

        sysinfo_item_t *item = sysinfo_create_path(name, root);

        /*
         * Change the type of the subtree only if it is not already
         * a fixed subtree
         */
        if ((item != NULL) && (item->subtree_type != SYSINFO_SUBTREE_TABLE)) {
                item->subtree_type = SYSINFO_SUBTREE_FUNCTION;
                item->subtree.generator.fn = fn;
                item->subtree.generator.data = data;
        } else {
                printf("Could not set sysinfo item %s.\n", name);
        }

        mutex_unlock(&sysinfo_lock);
}

/** Sysinfo dump indentation helper routine
 *
 * @param depth Number of spaces to print.
 *
 */
_NO_TRACE static void sysinfo_indent(size_t spaces)
{
        for (size_t i = 0; i < spaces; i++)
                printf(" ");
}

/** Dump the structure of sysinfo tree
 *
 * Should be called with sysinfo_lock held.
 *
 * @param root   Root item of the current (sub)tree.
 * @param spaces Current indentation level.
 *
 */
_NO_TRACE static void sysinfo_dump_internal(sysinfo_item_t *root, size_t spaces)
{
        /* Walk all siblings */
        for (sysinfo_item_t *cur = root; cur; cur = cur->next) {
                size_t length;

                if (spaces == 0) {
                        printf("%s", cur->name);
                        length = str_length(cur->name);
                } else {
                        sysinfo_indent(spaces);
                        printf(".%s", cur->name);
                        length = str_length(cur->name) + 1;
                }

                sysarg_t val;
                size_t size;

                /* Display node value and type */
                switch (cur->val_type) {
                case SYSINFO_VAL_UNDEFINED:
                        printf(" [undefined]\n");
                        break;
                case SYSINFO_VAL_VAL:
                        printf(" -> %" PRIun " (%#" PRIxn ")\n", cur->val.val,
                            cur->val.val);
                        break;
                case SYSINFO_VAL_DATA:
                        printf(" (%zu bytes)\n", cur->val.data.size);
                        break;
                case SYSINFO_VAL_FUNCTION_VAL:
                        val = cur->val.gen_val.fn(cur, cur->val.gen_val.data);
                        printf(" -> %" PRIun " (%#" PRIxn ") [generated]\n", val,
                            val);
                        break;
                case SYSINFO_VAL_FUNCTION_DATA:
                        /* N.B.: No data was actually returned (only a dry run) */
                        (void) cur->val.gen_data.fn(cur, &size, true,
                            cur->val.gen_data.data);
                        printf(" (%zu bytes) [generated]\n", size);
                        break;
                default:
                        printf("+ %s [unknown]\n", cur->name);
                }

                /* Recursivelly nest into the subtree */
                switch (cur->subtree_type) {
                case SYSINFO_SUBTREE_NONE:
                        break;
                case SYSINFO_SUBTREE_TABLE:
                        sysinfo_dump_internal(cur->subtree.table, spaces + length);
                        break;
                case SYSINFO_SUBTREE_FUNCTION:
                        sysinfo_indent(spaces + length);
                        printf("<generated subtree>\n");
                        break;
                default:
                        sysinfo_indent(spaces + length);
                        printf("<unknown subtree>\n");
                }
        }
}

/** Dump the structure of sysinfo tree
 *
 * @param root  Root item of the sysinfo (sub)tree.
 *              If it is NULL then consider the global
 *              sysinfo tree.
 *
 */
void sysinfo_dump(sysinfo_item_t *root)
{
        /*
         * Avoid other functions to mess with sysinfo
         * while we are dumping it
         */
        mutex_lock(&sysinfo_lock);

        if (root == NULL)
                sysinfo_dump_internal(global_root, 0);
        else
                sysinfo_dump_internal(root, 0);

        mutex_unlock(&sysinfo_lock);
}

/** Return sysinfo item value determined by name
 *
 * Should be called with sysinfo_lock held.
 *
 * @param name    Sysinfo path.
 * @param root    Root item of the sysinfo (sub)tree.
 *                If it is NULL then consider the global
 *                sysinfo tree.
 * @param dry_run Do not actually get any generated
 *                binary data, just calculate the size.
 *
 * @return Item value (constant or generated).
 *
 */
_NO_TRACE static sysinfo_return_t sysinfo_get_item(const char *name,
    sysinfo_item_t **root, bool dry_run)
{
        if (root == NULL)
                root = &global_root;

        /* Try to find the item or generate data */
        sysinfo_return_t ret;
        sysinfo_return_t *ret_ptr = &ret;
        sysinfo_item_t *item = sysinfo_find_item(name, *root, &ret_ptr,
            dry_run);

        if (item != NULL) {
                /* Item found in the fixed sysinfo tree */

                ret.tag = item->val_type;
                switch (item->val_type) {
                case SYSINFO_VAL_UNDEFINED:
                        break;
                case SYSINFO_VAL_VAL:
                        ret.val = item->val.val;
                        break;
                case SYSINFO_VAL_DATA:
                        ret.data = item->val.data;
                        break;
                case SYSINFO_VAL_FUNCTION_VAL:
                        ret.val = item->val.gen_val.fn(item, item->val.gen_val.data);
                        break;
                case SYSINFO_VAL_FUNCTION_DATA:
                        ret.data.data = item->val.gen_data.fn(item, &ret.data.size,
                            dry_run, item->val.gen_data.data);
                        break;
                }
        } else {
                /* No item in the fixed sysinfo tree */
                if (ret_ptr == NULL) {
                        /* Even no data was generated */
                        ret.tag = SYSINFO_VAL_UNDEFINED;
                }
        }

        return ret;
}

/** Return sysinfo item determined by name from user space
 *
 * The path string passed from the user space has to be properly null-terminated
 * (the last passed character must be null).
 *
 * @param ptr     Sysinfo path in the user address space.
 * @param size    Size of the path string.
 * @param dry_run Do not actually get any generated
 *                binary data, just calculate the size.
 *
 */
_NO_TRACE static sysinfo_return_t sysinfo_get_item_uspace(uspace_addr_t ptr, size_t size,
    bool dry_run)
{
        sysinfo_return_t ret;
        ret.tag = SYSINFO_VAL_UNDEFINED;

        if (size > SYSINFO_MAX_PATH)
                return ret;

        // TODO: Change this so that allocation is not needed.
        char *path = malloc(size + 1);
        if (!path)
                return ret;

        if ((copy_from_uspace(path, ptr, size + 1) == 0) &&
            (path[size] == 0)) {
                /*
                 * Prevent other functions from messing with sysinfo while we
                 * are reading it.
                 */
                mutex_lock(&sysinfo_lock);
                ret = sysinfo_get_item(path, NULL, dry_run);
                mutex_unlock(&sysinfo_lock);
        }

        free(path);
        return ret;
}

/** Return sysinfo keys determined by name
 *
 * Should be called with sysinfo_lock held.
 *
 * @param name    Sysinfo path.
 * @param root    Root item of the sysinfo (sub)tree.
 *                If it is NULL then consider the global
 *                sysinfo tree.
 * @param dry_run Do not actually get any generated
 *                binary data, just calculate the size.
 *
 * @return Item value (constant or generated).
 *
 */
_NO_TRACE static sysinfo_return_t sysinfo_get_keys(const char *name,
    sysinfo_item_t **root, bool dry_run)
{
        if (root == NULL)
                root = &global_root;

        sysinfo_item_t *subtree = NULL;

        if (name[0] != 0) {
                /* Try to find the item */
                sysinfo_item_t *item =
                    sysinfo_find_item(name, *root, NULL, dry_run);
                if ((item != NULL) &&
                    (item->subtree_type == SYSINFO_SUBTREE_TABLE))
                        subtree = item->subtree.table;
        } else
                subtree = *root;

        sysinfo_return_t ret;
        ret.tag = SYSINFO_VAL_UNDEFINED;

        if (subtree != NULL) {
                /*
                 * Calculate the size of subkeys.
                 */
                size_t size = 0;
                for (sysinfo_item_t *cur = subtree; cur; cur = cur->next)
                        size += str_size(cur->name) + 1;

                if (dry_run) {
                        ret.tag = SYSINFO_VAL_DATA;
                        ret.data.data = NULL;
                        ret.data.size = size;
                } else {
                        /* Allocate buffer for subkeys */
                        char *names = (char *) malloc(size);
                        if (names == NULL)
                                return ret;

                        size_t pos = 0;
                        for (sysinfo_item_t *cur = subtree; cur; cur = cur->next) {
                                str_cpy(names + pos, size - pos, cur->name);
                                pos += str_size(cur->name) + 1;
                        }

                        /* Correct return value */
                        ret.tag = SYSINFO_VAL_DATA;
                        ret.data.data = (void *) names;
                        ret.data.size = size;
                }
        }

        return ret;
}

/** Return sysinfo keys determined by name from user space
 *
 * The path string passed from the user space has to be properly
 * null-terminated (the last passed character must be null).
 *
 * @param ptr     Sysinfo path in the user address space.
 * @param size    Size of the path string.
 * @param dry_run Do not actually get any generated
 *                binary data, just calculate the size.
 *
 */
_NO_TRACE static sysinfo_return_t sysinfo_get_keys_uspace(uspace_addr_t ptr, size_t size,
    bool dry_run)
{
        sysinfo_return_t ret;
        ret.tag = SYSINFO_VAL_UNDEFINED;
        ret.data.data = NULL;
        ret.data.size = 0;

        if (size > SYSINFO_MAX_PATH)
                return ret;

        // TODO: Change this so that allocation is not needed.
        char *path = malloc(size + 1);
        if (!path)
                return ret;

        if ((copy_from_uspace(path, ptr, size + 1) == 0) &&
            (path[size] == 0)) {
                /*
                 * Prevent other functions from messing with sysinfo while we
                 * are reading it.
                 */
                mutex_lock(&sysinfo_lock);
                ret = sysinfo_get_keys(path, NULL, dry_run);
                mutex_unlock(&sysinfo_lock);
        }

        free(path);
        return ret;
}

/** Get the sysinfo keys size (syscall)
 *
 * The path string passed from the user space has
 * to be properly null-terminated (the last passed
 * character must be null).
 *
 * @param path_ptr  Sysinfo path in the user address space.
 * @param path_size Size of the path string.
 * @param size_ptr  User space pointer where to store the
 *                  keys size.
 *
 * @return Error code (EOK in case of no error).
 *
 */
sys_errno_t sys_sysinfo_get_keys_size(uspace_addr_t path_ptr, size_t path_size,
    uspace_addr_t size_ptr)
{
        errno_t rc;

        /*
         * Get the keys.
         *
         * N.B.: There is no need to free any potential keys
         * since we request a dry run.
         */
        sysinfo_return_t ret =
            sysinfo_get_keys_uspace(path_ptr, path_size, true);

        /* Check return data tag */
        if (ret.tag == SYSINFO_VAL_DATA)
                rc = copy_to_uspace(size_ptr, &ret.data.size,
                    sizeof(ret.data.size));
        else
                rc = EINVAL;

        return (sys_errno_t) rc;
}

/** Get the sysinfo keys (syscall)
 *
 * The path string passed from the user space has
 * to be properly null-terminated (the last passed
 * character must be null).
 *
 * If the user space buffer size does not equal
 * the actual size of the returned data, the data
 * is truncated.
 *
 * The actual size of data returned is stored to
 * size_ptr.
 *
 * @param path_ptr    Sysinfo path in the user address space.
 * @param path_size   Size of the path string.
 * @param buffer_ptr  User space pointer to the buffer where
 *                    to store the binary data.
 * @param buffer_size User space buffer size.
 * @param size_ptr    User space pointer where to store the
 *                    binary data size.
 *
 * @return Error code (EOK in case of no error).
 *
 */
sys_errno_t sys_sysinfo_get_keys(uspace_addr_t path_ptr, size_t path_size,
    uspace_addr_t buffer_ptr, size_t buffer_size, uspace_ptr_size_t size_ptr)
{
        errno_t rc;

        /* Get the keys */
        sysinfo_return_t ret = sysinfo_get_keys_uspace(path_ptr, path_size,
            false);

        /* Check return data tag */
        if (ret.tag == SYSINFO_VAL_DATA) {
                size_t size = min(ret.data.size, buffer_size);
                rc = copy_to_uspace(buffer_ptr, ret.data.data, size);
                if (rc == EOK)
                        rc = copy_to_uspace(size_ptr, &size, sizeof(size));

                free(ret.data.data);
        } else
                rc = EINVAL;

        return (sys_errno_t) rc;
}

/** Get the sysinfo value type (syscall)
 *
 * The path string passed from the user space has
 * to be properly null-terminated (the last passed
 * character must be null).
 *
 * @param path_ptr  Sysinfo path in the user address space.
 * @param path_size Size of the path string.
 *
 * @return Item value type.
 *
 */
sysarg_t sys_sysinfo_get_val_type(uspace_addr_t path_ptr, size_t path_size)
{
        /*
         * Get the item.
         *
         * N.B.: There is no need to free any potential generated
         * binary data since we request a dry run.
         */
        sysinfo_return_t ret = sysinfo_get_item_uspace(path_ptr, path_size, true);

        /*
         * Map generated value types to constant types (user space does
         * not care whether the value is constant or generated).
         */
        if (ret.tag == SYSINFO_VAL_FUNCTION_VAL)
                ret.tag = SYSINFO_VAL_VAL;
        else if (ret.tag == SYSINFO_VAL_FUNCTION_DATA)
                ret.tag = SYSINFO_VAL_DATA;

        return (sysarg_t) ret.tag;
}

/** Get the sysinfo numerical value (syscall)
 *
 * The path string passed from the user space has
 * to be properly null-terminated (the last passed
 * character must be null).
 *
 * @param path_ptr  Sysinfo path in the user address space.
 * @param path_size Size of the path string.
 * @param value_ptr User space pointer where to store the
 *                  numberical value.
 *
 * @return Error code (EOK in case of no error).
 *
 */
sys_errno_t sys_sysinfo_get_value(uspace_addr_t path_ptr, size_t path_size,
    uspace_addr_t value_ptr)
{
        errno_t rc;

        /*
         * Get the item.
         *
         * N.B.: There is no need to free any potential generated binary
         * data since we request a dry run.
         */
        sysinfo_return_t ret = sysinfo_get_item_uspace(path_ptr, path_size, true);

        /* Only constant or generated numerical value is returned */
        if ((ret.tag == SYSINFO_VAL_VAL) || (ret.tag == SYSINFO_VAL_FUNCTION_VAL))
                rc = copy_to_uspace(value_ptr, &ret.val, sizeof(ret.val));
        else
                rc = EINVAL;

        return (sys_errno_t) rc;
}

/** Get the sysinfo binary data size (syscall)
 *
 * The path string passed from the user space has
 * to be properly null-terminated (the last passed
 * character must be null).
 *
 * @param path_ptr  Sysinfo path in the user address space.
 * @param path_size Size of the path string.
 * @param size_ptr  User space pointer where to store the
 *                  binary data size.
 *
 * @return Error code (EOK in case of no error).
 *
 */
sys_errno_t sys_sysinfo_get_data_size(uspace_addr_t path_ptr, size_t path_size,
    uspace_addr_t size_ptr)
{
        errno_t rc;

        /*
         * Get the item.
         *
         * N.B.: There is no need to free any potential generated binary
         * data since we request a dry run.
         */
        sysinfo_return_t ret = sysinfo_get_item_uspace(path_ptr, path_size, true);

        /* Only the size of constant or generated binary data is considered */
        if ((ret.tag == SYSINFO_VAL_DATA) || (ret.tag == SYSINFO_VAL_FUNCTION_DATA))
                rc = copy_to_uspace(size_ptr, &ret.data.size,
                    sizeof(ret.data.size));
        else
                rc = EINVAL;

        return (sys_errno_t) rc;
}

/** Get the sysinfo binary data (syscall)
 *
 * The path string passed from the user space has
 * to be properly null-terminated (the last passed
 * character must be null).
 *
 * If the user space buffer size does not equal
 * the actual size of the returned data, the data
 * is truncated. Whether this is actually a fatal
 * error or the data can be still interpreted as valid
 * depends on the nature of the data and has to be
 * decided by the user space.
 *
 * The actual size of data returned is stored to
 * size_ptr.
 *
 * @param path_ptr    Sysinfo path in the user address space.
 * @param path_size   Size of the path string.
 * @param buffer_ptr  User space pointer to the buffer where
 *                    to store the binary data.
 * @param buffer_size User space buffer size.
 * @param size_ptr    User space pointer where to store the
 *                    binary data size.
 *
 * @return Error code (EOK in case of no error).
 *
 */
sys_errno_t sys_sysinfo_get_data(uspace_addr_t path_ptr, size_t path_size,
    uspace_addr_t buffer_ptr, size_t buffer_size, uspace_ptr_size_t size_ptr)
{
        errno_t rc;

        /* Get the item */
        sysinfo_return_t ret = sysinfo_get_item_uspace(path_ptr, path_size,
            false);

        /* Only constant or generated binary data is considered */
        if ((ret.tag == SYSINFO_VAL_DATA) ||
            (ret.tag == SYSINFO_VAL_FUNCTION_DATA)) {
                size_t size = min(ret.data.size, buffer_size);
                rc = copy_to_uspace(buffer_ptr, ret.data.data, size);
                if (rc == EOK)
                        rc = copy_to_uspace(size_ptr, &size, sizeof(size));
        } else
                rc = EINVAL;

        /* N.B.: The generated binary data should be freed */
        if ((ret.tag == SYSINFO_VAL_FUNCTION_DATA) && (ret.data.data != NULL))
                free(ret.data.data);

        return (sys_errno_t) rc;
}

/** @}
 */

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub