<commit_before>// Copyright 2010-2012 RethinkDB, all rights reserved.
#include "errors.hpp"

#ifndef _MSC_VER
#include <cxxabi.h>
#endif

#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <typeinfo>

#include "logger.hpp"
#include "utils.hpp"
#include "backtrace.hpp"
#include "thread_local.hpp"
#include "clustering/administration/logs/log_writer.hpp"
#include "arch/timing.hpp"

TLS_with_init(bool, crashed, false); // to prevent crashing within crashes

NOINLINE int get_errno() {
    return errno;
}
NOINLINE void set_errno(int new_errno) {
    errno = new_errno;
}

NORETURN void crash_oom() {
    const char *message = "rethinkdb: Memory allocation failed. This usually means "
                          "that we have run out of RAM. Aborting.\n";
    UNUSED size_t res = fwrite(message, 1, strlen(message), stderr);
    abort();
}

void report_user_error(const char *msg, ...) {
    fprintf(stderr, "Version: %s\n", RETHINKDB_VERSION_STR);
    if (TLS_get_crashed()) {
        va_list args;
        va_start(args, msg);
        fprintf(stderr, "Crashing while already crashed. Printing error message to stderr.\n");
        vfprintf(stderr, msg, args);
        va_end(args);
        return;
    }

    TLS_set_crashed(true);

    thread_log_writer_disabler_t disabler;

    va_list args;
    va_start(args, msg);
    vlogERR(msg, args);
    va_end(args);
}

void report_fatal_error(const char *file, int line, const char *msg, ...) {
    fprintf(stderr, "Version: %s\n", RETHINKDB_VERSION_STR);
    if (TLS_get_crashed()) {
        va_list args;
        va_start(args, msg);
        fprintf(stderr, "Crashing while already crashed. Printing error message to stderr.\n");
        vfprintf(stderr, msg, args);
        va_end(args);
        return;
    }

    TLS_set_crashed(true);

    thread_log_writer_disabler_t disabler;

    va_list args;
    va_start(args, msg);
    logERR("Error in thread %d in %s at line %d:", get_thread_id(), file, line);
    vlogERR(msg, args);
    va_end(args);

    /* Don't print backtraces in valgrind mode because valgrind issues lots of spurious
    warnings when print_backtrace() is run. */
#if !defined(VALGRIND)
    logERR("Backtrace:");
    logERR("%s", format_backtrace().c_str());
#endif

    logERR("Exiting.");
}

const char *errno_string_maybe_using_buffer(int errsv, char *buf, size_t buflen) {
#ifdef _GNU_SOURCE
    return strerror_r(errsv, buf, buflen);
#elif defined(_WIN32)
    UNUSED errno_t res = strerror_s(buf, buflen, errsv);
    return buf;
#else
    // The result is either 0 or ERANGE (if the buffer is too small) or EINVAL (if the error number
    // is invalid), but in either case a friendly nul-terminated buffer is written.
    UNUSED int res = strerror_r(errsv, buf, buflen);
    return buf;
#endif
}

#ifdef _WIN32
MUST_USE const std::string winerr_string(DWORD winerr) {
    char *errmsg;
    DWORD res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                   NULL, winerr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&errmsg, 0, NULL);
    if (res != 0) {
        size_t end = strlen(errmsg);
        while (end > 0 && isspace(errmsg[end-1])) {
            end--;
        }
        errmsg[end] = 0;
        std::string ret(errmsg);
        LocalFree(errmsg);
        return ret;
    } else {
        return strprintf("Unkown error 0x%x (error formatting the error: 0x%x)", winerr, GetLastError());
    }
}
#endif

/* Handlers for various signals and for unexpected exceptions or calls to std::terminate() */

#ifndef _WIN32 // ATN TODO
NORETURN void generic_crash_handler(int signum) {
    if (signum == SIGSEGV) {
        crash("Segmentation fault.");
    } else if (signum == SIGBUS) {
        crash("Bus error.");
    } else {
        crash("Unexpected signal: %d", signum);
    }
}
#endif

#ifdef _WIN32 // ATN TODO
#define terminate_handler terminate_handler_
#endif
NORETURN void terminate_handler() {
#ifndef _WIN32
    std::type_info *t = abi::__cxa_current_exception_type();
#else
    bool t = std::uncaught_exception();
#endif
    if (t) {
#ifndef _WIN32
        std::string name;
        try {
            name = demangle_cpp_name(t->name());
        } catch (const demangle_failed_exc_t &) {
            name = t->name();
        }
#else
        std::string name = "unknown";
#endif
        try {
            /* This will rethrow whatever unexpected exception was thrown. */
            throw;
        } catch (const std::exception& e) {
            crash("Uncaught exception of type \"%s\"\n  what(): %s", name.c_str(), e.what());
        } catch (...) {
            crash("Uncaught exception of type \"%s\"", name.c_str());
        }
        unreachable();
    } else {
        crash("std::terminate() called without any exception.");
    }
}

#ifdef _WIN32
LONG WINAPI windows_crash_handler(EXCEPTION_POINTERS *exception) {
    std::string message;
    switch (exception->ExceptionRecord->ExceptionCode) {
    case 0xe06d7363:
        if (std::uncaught_exception()) {
            try {
                throw;
            } catch (const std::exception& e) {
                message = strprintf("Uncaught C++ exception: %s", + e.what());
            } catch (...) {
                message = "Uncaught C++ exception of unknown type";
            }
        } else {
            message = "Uncaught C++ exception";
        }
        break;
    case EXCEPTION_BREAKPOINT:
        return EXCEPTION_EXECUTE_HANDLER;
    case EXCEPTION_ACCESS_VIOLATION:
        message = "ACCESS_VIOLATION: The thread tried to read from or write to a virtual address for which it does not have the appropriate access.";
        break;
    case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
        message = "ARRAY_BOUNDS_EXCEEDED: The thread tried to access an array element that is out of bounds and the underlying hardware supports bounds checking.";
        break;
    case EXCEPTION_DATATYPE_MISALIGNMENT:
        message = "DATATYPE_MISALIGNMENT: The thread tried to read or write data that is misaligned on hardware that does not provide alignment. For example, 16-bit values must be aligned on 2-byte boundaries; 32-bit values on 4-byte boundaries, and so on.";
        break;
    case EXCEPTION_FLT_DENORMAL_OPERAND:
        message = "FLT_DENORMAL_OPERAND: One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value.";
        break;
    case EXCEPTION_FLT_DIVIDE_BY_ZERO:
        message = "FLT_DIVIDE_BY_ZERO: The thread tried to divide a floating-point value by a floating-point divisor of zero.";
        break;
    case EXCEPTION_FLT_INEXACT_RESULT:
        message = "FLT_INEXACT_RESULT: The result of a floating-point operation cannot be represented exactly as a decimal fraction.";
        break;
    case EXCEPTION_FLT_INVALID_OPERATION:
        message = "FLT_INVALID_OPERATION: This exception represents any floating-point exception not included in this list.";
        break;
    case EXCEPTION_FLT_OVERFLOW:
        message = "FLT_OVERFLOW: The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.";
        break;
    case EXCEPTION_FLT_STACK_CHECK:
        message = "FLT_STACK_CHECK: The stack overflowed or underflowed as the result of a floating-point operation.";
        break;
    case EXCEPTION_FLT_UNDERFLOW:
        message = "FLT_UNDERFLOW: The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.";
        break;
    case EXCEPTION_ILLEGAL_INSTRUCTION:
        message = "ILLEGAL_INSTRUCTION: The thread tried to execute an invalid instruction.";
        break;
    case EXCEPTION_IN_PAGE_ERROR:
        message = "IN_PAGE_ERROR: The thread tried to access a page that was not present, and the system was unable to load the page. For example, this exception might occur if a network connection is lost while running a program over the network.";
        break;
    case EXCEPTION_INT_DIVIDE_BY_ZERO:
        message = "INT_DIVIDE_BY_ZERO: The thread tried to divide an integer value by an integer divisor of zero.";
        break;
    case EXCEPTION_INT_OVERFLOW:
        message = "INT_OVERFLOW: The result of an integer operation caused a carry out of the most significant bit of the result.";
        break;
    case EXCEPTION_INVALID_DISPOSITION:
        message = "INVALID_DISPOSITION: An exception handler returned an invalid disposition to the exception dispatcher. Programmers using a high-level language such as C should never encounter this exception.";
        break;
    case EXCEPTION_NONCONTINUABLE_EXCEPTION:
        message = "NONCONTINUABLE_EXCEPTION: The thread tried to continue execution after a noncontinuable exception occurred.";
        break;
    case EXCEPTION_PRIV_INSTRUCTION:
        message = "PRIV_INSTRUCTION: The thread tried to execute an instruction whose operation is not allowed in the current machine mode.";
        break;
    case EXCEPTION_SINGLE_STEP:
        message = "SINGLE_STEP: A trace trap or other single-instruction mechanism signaled that one instruction has been executed.";
        break;
    case EXCEPTION_STACK_OVERFLOW:
        message = "STACK_OVERFLOW: The thread used up its stack.";
        break;
    default:
        message = "Unkown exception.";
    }

    logERR("Windows exception 0x%x: %s", exception->ExceptionRecord->ExceptionCode, message.c_str());
    logERR("backtrace:\n%s", format_backtrace(exception->ContextRecord).c_str());

    // This usually results in process termination
    return EXCEPTION_EXECUTE_HANDLER;
}

int windows_runtime_debug_failure_handler(int type, char *message, int *retval) {
    logERR("run-time debug failure:\n%s", message);
    logERR("backtrace:\n%s", format_backtrace().c_str());

    return FALSE;
}

#endif

void install_generic_crash_handler() {
#ifdef _WIN32
    // TODO ATN: maybe call SetErrorMode
    SetUnhandledExceptionFilter(windows_crash_handler);
#else
#ifdef _MSC_VER
    _CrtSetReportHook(windows_runtime_debug_failure_handler);
#endif
#ifndef VALGRIND
    {
        struct sigaction sa = make_sa_handler(0, generic_crash_handler);

        int res = sigaction(SIGSEGV, &sa, NULL);
        guarantee_err(res == 0, "Could not install SEGV signal handler");
        res = sigaction(SIGBUS, &sa, NULL);
        guarantee_err(res == 0, "Could not install BUS signal handler");
    }
#endif

    struct sigaction sa = make_sa_handler(0, SIG_IGN);
    int res = sigaction(SIGPIPE, &sa, NULL);
    guarantee_err(res == 0, "Could not install PIPE handler");
#endif

    std::set_terminate(&terminate_handler);
}

/* If a call to `operator new()` or `operator new[]()` fails, we have to crash
immediately.
The default behavior of this handler is to throw an `std::bad_alloc` exception.
However that is dangerous because it unwinds the stack and can cause side-effects
including data corruption. */
NORETURN void new_oom_handler() {
    crash_oom();
}

void install_new_oom_handler() {
    std::set_new_handler(&new_oom_handler);
}

/* Boost will call this function when an assertion fails. */

namespace boost {

void assertion_failed(char const * expr, char const * function, char const * file, long line) { // NOLINT(runtime/int)
    report_fatal_error(file, line, "BOOST_ASSERT failure in '%s': %s", function, expr);
    BREAKPOINT;
}

void assertion_failed_msg(char const * expr, char const * msg, char const * function, char const * file, long line) { // NOLINT(runtime/int)
    report_fatal_error(file, line, "BOOST_ASSERT_MSG failure in '%s': %s (%s)", function, expr, msg);
    BREAKPOINT;
}

}  // namespace boost
<commit_msg>Improved uncaught exception handler<commit_after>// Copyright 2010-2012 RethinkDB, all rights reserved.
#include "errors.hpp"

#ifndef _MSC_VER
#include <cxxabi.h>
#endif

#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <typeinfo>

#include "logger.hpp"
#include "utils.hpp"
#include "backtrace.hpp"
#include "thread_local.hpp"
#include "clustering/administration/logs/log_writer.hpp"
#include "arch/timing.hpp"

TLS_with_init(bool, crashed, false); // to prevent crashing within crashes

NOINLINE int get_errno() {
    return errno;
}
NOINLINE void set_errno(int new_errno) {
    errno = new_errno;
}

NORETURN void crash_oom() {
    const char *message = "rethinkdb: Memory allocation failed. This usually means "
                          "that we have run out of RAM. Aborting.\n";
    UNUSED size_t res = fwrite(message, 1, strlen(message), stderr);
    abort();
}

void report_user_error(const char *msg, ...) {
    fprintf(stderr, "Version: %s\n", RETHINKDB_VERSION_STR);
    if (TLS_get_crashed()) {
        va_list args;
        va_start(args, msg);
        fprintf(stderr, "Crashing while already crashed. Printing error message to stderr.\n");
        vfprintf(stderr, msg, args);
        va_end(args);
        return;
    }

    TLS_set_crashed(true);

    thread_log_writer_disabler_t disabler;

    va_list args;
    va_start(args, msg);
    vlogERR(msg, args);
    va_end(args);
}

void report_fatal_error(const char *file, int line, const char *msg, ...) {
    fprintf(stderr, "Version: %s\n", RETHINKDB_VERSION_STR);
    if (TLS_get_crashed()) {
        va_list args;
        va_start(args, msg);
        fprintf(stderr, "Crashing while already crashed. Printing error message to stderr.\n");
        vfprintf(stderr, msg, args);
        va_end(args);
        return;
    }

    TLS_set_crashed(true);

    thread_log_writer_disabler_t disabler;

    va_list args;
    va_start(args, msg);
    logERR("Error in thread %d in %s at line %d:", get_thread_id(), file, line);
    vlogERR(msg, args);
    va_end(args);

    /* Don't print backtraces in valgrind mode because valgrind issues lots of spurious
    warnings when print_backtrace() is run. */
#if !defined(VALGRIND)
    logERR("Backtrace:");
    logERR("%s", format_backtrace().c_str());
#endif

    logERR("Exiting.");
}

const char *errno_string_maybe_using_buffer(int errsv, char *buf, size_t buflen) {
#ifdef _GNU_SOURCE
    return strerror_r(errsv, buf, buflen);
#elif defined(_WIN32)
    UNUSED errno_t res = strerror_s(buf, buflen, errsv);
    return buf;
#else
    // The result is either 0 or ERANGE (if the buffer is too small) or EINVAL (if the error number
    // is invalid), but in either case a friendly nul-terminated buffer is written.
    UNUSED int res = strerror_r(errsv, buf, buflen);
    return buf;
#endif
}

#ifdef _WIN32
MUST_USE const std::string winerr_string(DWORD winerr) {
    char *errmsg;
    DWORD res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                   NULL, winerr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&errmsg, 0, NULL);
    if (res != 0) {
        size_t end = strlen(errmsg);
        while (end > 0 && isspace(errmsg[end-1])) {
            end--;
        }
        errmsg[end] = 0;
        std::string ret(errmsg);
        LocalFree(errmsg);
        return ret;
    } else {
        return strprintf("Unkown error 0x%x (error formatting the error: 0x%x)", winerr, GetLastError());
    }
}
#endif

/* Handlers for various signals and for unexpected exceptions or calls to std::terminate() */

#ifndef _WIN32 // ATN TODO
NORETURN void generic_crash_handler(int signum) {
    if (signum == SIGSEGV) {
        crash("Segmentation fault.");
    } else if (signum == SIGBUS) {
        crash("Bus error.");
    } else {
        crash("Unexpected signal: %d", signum);
    }
}
#endif

#ifdef _WIN32 // ATN TODO
#define terminate_handler terminate_handler_
#endif
NORETURN void terminate_handler() {
#ifndef _WIN32
    std::type_info *t = abi::__cxa_current_exception_type();
#else
    bool t = std::uncaught_exception();
#endif
    if (t) {
#ifndef _WIN32
        std::string name;
        try {
            name = demangle_cpp_name(t->name());
        } catch (const demangle_failed_exc_t &) {
            name = t->name();
        }
#else
        std::string name = "unknown";
#endif
        try {
            /* This will rethrow whatever unexpected exception was thrown. */
            throw;
        } catch (const std::exception& e) {
            crash("Uncaught exception of type \"%s\"\n  what(): %s", name.c_str(), e.what());
        } catch (...) {
            crash("Uncaught exception of type \"%s\"", name.c_str());
        }
        unreachable();
    } else {
        crash("std::terminate() called without any exception.");
    }
}

#ifdef _WIN32
template <class T>
T *relative_to(ULONG_PTR hInstance, T *ptr) {
    return reinterpret_cast<T*>(hInstance + reinterpret_cast<ULONG_PTR>(ptr));
}

LONG WINAPI windows_crash_handler(EXCEPTION_POINTERS *exception) {
    std::string message;
    EXCEPTION_RECORD *record = exception->ExceptionRecord;
    switch (record->ExceptionCode) {
    case 0xe06d7363:
        try {
            _CxxThrowException(reinterpret_cast<void*>(record->ExceptionInformation[1]),
                               reinterpret_cast<_ThrowInfo*>(record->ExceptionInformation[2]));
        } catch (std::exception &e) {
            message = strprintf("Unhandled C++ exception: %s: %s", typeid(e).name(), e.what());
        } catch (...) {
            message = "Unhandled C++ exception";
        }
        break;
    case EXCEPTION_BREAKPOINT:
        return EXCEPTION_EXECUTE_HANDLER;
    case EXCEPTION_ACCESS_VIOLATION:
        message = "ACCESS_VIOLATION: The thread tried to read from or write to a virtual address for which it does not have the appropriate access.";
        break;
    case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
        message = "ARRAY_BOUNDS_EXCEEDED: The thread tried to access an array element that is out of bounds and the underlying hardware supports bounds checking.";
        break;
    case EXCEPTION_DATATYPE_MISALIGNMENT:
        message = "DATATYPE_MISALIGNMENT: The thread tried to read or write data that is misaligned on hardware that does not provide alignment. For example, 16-bit values must be aligned on 2-byte boundaries; 32-bit values on 4-byte boundaries, and so on.";
        break;
    case EXCEPTION_FLT_DENORMAL_OPERAND:
        message = "FLT_DENORMAL_OPERAND: One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value.";
        break;
    case EXCEPTION_FLT_DIVIDE_BY_ZERO:
        message = "FLT_DIVIDE_BY_ZERO: The thread tried to divide a floating-point value by a floating-point divisor of zero.";
        break;
    case EXCEPTION_FLT_INEXACT_RESULT:
        message = "FLT_INEXACT_RESULT: The result of a floating-point operation cannot be represented exactly as a decimal fraction.";
        break;
    case EXCEPTION_FLT_INVALID_OPERATION:
        message = "FLT_INVALID_OPERATION: This exception represents any floating-point exception not included in this list.";
        break;
    case EXCEPTION_FLT_OVERFLOW:
        message = "FLT_OVERFLOW: The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.";
        break;
    case EXCEPTION_FLT_STACK_CHECK:
        message = "FLT_STACK_CHECK: The stack overflowed or underflowed as the result of a floating-point operation.";
        break;
    case EXCEPTION_FLT_UNDERFLOW:
        message = "FLT_UNDERFLOW: The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.";
        break;
    case EXCEPTION_ILLEGAL_INSTRUCTION:
        message = "ILLEGAL_INSTRUCTION: The thread tried to execute an invalid instruction.";
        break;
    case EXCEPTION_IN_PAGE_ERROR:
        message = "IN_PAGE_ERROR: The thread tried to access a page that was not present, and the system was unable to load the page. For example, this exception might occur if a network connection is lost while running a program over the network.";
        break;
    case EXCEPTION_INT_DIVIDE_BY_ZERO:
        message = "INT_DIVIDE_BY_ZERO: The thread tried to divide an integer value by an integer divisor of zero.";
        break;
    case EXCEPTION_INT_OVERFLOW:
        message = "INT_OVERFLOW: The result of an integer operation caused a carry out of the most significant bit of the result.";
        break;
    case EXCEPTION_INVALID_DISPOSITION:
        message = "INVALID_DISPOSITION: An exception handler returned an invalid disposition to the exception dispatcher. Programmers using a high-level language such as C should never encounter this exception.";
        break;
    case EXCEPTION_NONCONTINUABLE_EXCEPTION:
        message = "NONCONTINUABLE_EXCEPTION: The thread tried to continue execution after a noncontinuable exception occurred.";
        break;
    case EXCEPTION_PRIV_INSTRUCTION:
        message = "PRIV_INSTRUCTION: The thread tried to execute an instruction whose operation is not allowed in the current machine mode.";
        break;
    case EXCEPTION_SINGLE_STEP:
        message = "SINGLE_STEP: A trace trap or other single-instruction mechanism signaled that one instruction has been executed.";
        break;
    case EXCEPTION_STACK_OVERFLOW:
        message = "STACK_OVERFLOW: The thread used up its stack.";
        break;
    default:
        message = "Unkown exception.";
    }

    logERR("Windows exception 0x%x: %s", exception->ExceptionRecord->ExceptionCode, message.c_str());
    logERR("backtrace:\n%s", format_backtrace(exception->ContextRecord).c_str());

    // This usually results in process termination
    return EXCEPTION_EXECUTE_HANDLER;
}

int windows_runtime_debug_failure_handler(int type, char *message, int *retval) {
    logERR("run-time debug failure:\n%s", message);
    logERR("backtrace:\n%s", format_backtrace().c_str());

    return FALSE;
}

#endif

void install_generic_crash_handler() {
#ifdef _WIN32
    // TODO ATN: maybe call SetErrorMode
    SetUnhandledExceptionFilter(windows_crash_handler);
#else
#ifdef _MSC_VER
    _CrtSetReportHook(windows_runtime_debug_failure_handler);
#endif
#ifndef VALGRIND
    {
        struct sigaction sa = make_sa_handler(0, generic_crash_handler);

        int res = sigaction(SIGSEGV, &sa, NULL);
        guarantee_err(res == 0, "Could not install SEGV signal handler");
        res = sigaction(SIGBUS, &sa, NULL);
        guarantee_err(res == 0, "Could not install BUS signal handler");
    }
#endif

    struct sigaction sa = make_sa_handler(0, SIG_IGN);
    int res = sigaction(SIGPIPE, &sa, NULL);
    guarantee_err(res == 0, "Could not install PIPE handler");
#endif

    std::set_terminate(&terminate_handler);
}

/* If a call to `operator new()` or `operator new[]()` fails, we have to crash
immediately.
The default behavior of this handler is to throw an `std::bad_alloc` exception.
However that is dangerous because it unwinds the stack and can cause side-effects
including data corruption. */
NORETURN void new_oom_handler() {
    crash_oom();
}

void install_new_oom_handler() {
    std::set_new_handler(&new_oom_handler);
}

/* Boost will call this function when an assertion fails. */

namespace boost {

void assertion_failed(char const * expr, char const * function, char const * file, long line) { // NOLINT(runtime/int)
    report_fatal_error(file, line, "BOOST_ASSERT failure in '%s': %s", function, expr);
    BREAKPOINT;
}

void assertion_failed_msg(char const * expr, char const * msg, char const * function, char const * file, long line) { // NOLINT(runtime/int)
    report_fatal_error(file, line, "BOOST_ASSERT_MSG failure in '%s': %s (%s)", function, expr, msg);
    BREAKPOINT;
}

}  // namespace boost
<|endoftext|>


<commit_before>/*
*
* Copyright 2013 Telefonica Investigacion y Desarrollo, S.A.U
*
* This file is part of Orion Context Broker.
*
* Orion Context Broker is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Orion Context Broker 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 Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Orion Context Broker. If not, see http://www.gnu.org/licenses/.
*
* For those usages not covered by this license please contact with
* iot_support at tid dot es
*
* Author: Ken Zangelin
*/
#include <time.h>
#include <stdint.h>

#include <string>

#include "logMsg/logMsg.h"
#include "logMsg/traceLevels.h"

#include "common/globals.h"
#include "common/sem.h"
#include "alarmMgr/alarmMgr.h"
#include "serviceRoutines/versionTreat.h"     // For orionInit()
#include "mongoBackend/MongoGlobal.h"         // For orionInit()
#include "ngsiNotify/onTimeIntervalThread.h"  // For orionInit()

/* ****************************************************************************
*
* Globals
*/
static Timer*          timer                = NULL;
int                    startTime            = -1;
int                    statisticsTime       = -1;
OrionExitFunction      orionExitFunction    = NULL;
static struct timeval  logStartTime;
bool                   countersStatistics   = false;
bool                   semWaitStatistics    = false;
bool                   timingStatistics     = false;
bool                   notifQueueStatistics = false;

/* ****************************************************************************
*
* transactionIdSet - set the transaction ID
*
* To ensure a unique identifier of the transaction, the startTime down to milliseconds
* of the broker is used as prefix (to almost guarantee its uniqueness among brokers)
* Furthermore, a running number is appended for the transaction.
* A 32 bit signed number is used, so its max value is 0x7FFFFFFF (2,147,483,647).
*
* If the running number overflows, a millisecond is added to the start time.
*
* The whole thing is stored in the thread variable 'transactionId', supported by the
* logging library 'liblm'.
*
*/
void transactionIdSet(void)
{
  static int transaction = 0;

  transSemTake("transactionIdSet", "changing the transaction id");
  ++transaction;

  if (transaction < 0)
  {
    logStartTime.tv_usec += 1;
    transaction = 1;
  }

  snprintf(transactionId, sizeof(transactionId), "%lu-%03d-%011d",
           logStartTime.tv_sec, (int) logStartTime.tv_usec / 1000, transaction);

  transSemGive("transactionIdSet", "changing the transaction id");
}

/* ****************************************************************************
*
* orionInit -
*/
void orionInit
(
  OrionExitFunction  exitFunction,
  const char*        version,
  SemOpType          reqPolicy,
  bool               _countersStatistics,
  bool               _semWaitStatistics,
  bool               _timingStatistics,
  bool               _notifQueueStatistics
)
{
  // Give the rest library the correct version string of this executable
  versionSet(version);

  // The function to call on fatal error
  orionExitFunction = exitFunction;

  // Initialize the semaphore used by mongoBackend
  semInit(reqPolicy, _semWaitStatistics);

  // Set timer object (singleton)
  setTimer(new Timer());

  // startTime for log library
  if (gettimeofday(&logStartTime, NULL) != 0)
  {
    fprintf(stderr, "gettimeofday: %s\n", strerror(errno));
    orionExitFunction(1, "gettimeofday error");
  }

  // Set start time and statisticsTime used by REST interface
  startTime      = logStartTime.tv_sec;
  statisticsTime = startTime;

  // Set other flags related with statistics
  countersStatistics   = _countersStatistics;
  timingStatistics     = _timingStatistics;
  notifQueueStatistics = _notifQueueStatistics;

  strncpy(transactionId, "N/A", sizeof(transactionId));
}

/* ****************************************************************************
*
* isTrue -
*/
bool isTrue(const std::string& s)
{
  if ((s == "true") || (s == "1"))
  {
    return true;
  }

  return false;
}

/* ****************************************************************************
*
* isFalse -
*/
bool isFalse(const std::string& s)
{
  if ((s == "false") || (s == "0"))
  {
    return true;
  }

  return false;
}

/*****************************************************************************
*
* getTimer -
*/
Timer* getTimer(void)
{
  return timer;
}

/*****************************************************************************
*
* setTimer -
*/
void setTimer(Timer* t)
{
  timer = t;
}

/* ****************************************************************************
*
* getCurrentTime -
*/
int getCurrentTime(void)
{
  if (getTimer() == NULL)
  {
    LM_T(LmtSoftError, ("getTimer() == NULL - calling exit function for library user"));
    orionExitFunction(1, "getTimer() == NULL");
    return -1;
  }

  return getTimer()->getCurrentTime();
}

/* ****************************************************************************
*
* toSeconds -
*/
int64_t toSeconds(int value, char what, bool dayPart)
{
  int64_t result = -1;

  if (dayPart == true)
  {
    if (what == 'Y')
    {
      result = 365L * 24 * 3600 * value;
    }
    else if (what == 'M')
    {
      result = 30L * 24 * 3600 * value;
    }
    else if (what == 'W')
    {
      result = 7L * 24 * 3600 * value;
    }
    else if (what == 'D')
    {
      result = 24L * 3600 * value;
    }
  }
  else
  {
    if (what == 'H')
    {
      result = 3600L * value;
    }
    else if (what == 'M')
    {
      result = 60L * value;
    }
    else if (what == 'S')
    {
      result = value;
    }
  }

  if (result == -1)
  {
    alarmMgr.badInput(clientIp, "ERROR in duration string");
  }

  return result;
}

/*****************************************************************************
*
* parse8601 -
*
* This is common code for Duration and Throttling (at least)
*
*/
int64_t parse8601(const std::string& s)
{
  if (s == "")
  {
    return -1;
  }

  char*      duration    = strdup(s.c_str());
  char*      toFree      = duration;
  bool       dayPart     = true;
  int64_t    accumulated = 0;
  char*      start;

  if (*duration != 'P')
  {
    free(toFree);
    return -1;
  }

  ++duration;
  start = duration;

  if (*duration == 0)
  {
    free(toFree);
    return -1;
  }

  bool digitsPending = false;

  while (*duration != 0)
  {
    if (isdigit(*duration) || (*duration == '.') || (*duration == ','))
    {
      ++duration;
      digitsPending = true;
    }
    else if ((dayPart == true) &&
             ((*duration == 'Y') || (*duration == 'M') || (*duration == 'D') || (*duration == 'W')))
    {
      char what = *duration;

      *duration = 0;
      int value = atoi(start);

      if ((value == 0) && (*start != '0'))
      {
        std::string details = std::string("parse error for duration '") + start + "'";
        alarmMgr.badInput(clientIp, details);

        free(toFree);
        return -1;
      }

      accumulated += toSeconds(value, what, dayPart);
      digitsPending = false;
      ++duration;
      start = duration;
    }
    else if ((dayPart == true) && (*duration == 'T'))
    {
      dayPart = false;
      ++duration;
      start = duration;
      digitsPending = false;
    }
    else if ((dayPart == false) &&
             ((*duration == 'H') || (*duration == 'M') || (*duration == 'S')))
    {
      char what = *duration;
      int  value;

      *duration = 0;

      if (what == 'S')  // We support floats for the seconds, but only to round to an integer
      {
        //
        // To round e.g. 4.51 to 5, we split 4.51 in its integer part and its decimal part:
        //   secs    = 4.51
        //   intPart = 4
        //   rest    = 0.51
        //
        // After this simple operation is done, we just need to add ONE to 'intPart' if
        // 'rest' >= 0.5 ('greater than' or just 'greater'?)
        // and if 'rest' < 0.5, it is just thrown away.
        //
        float secs    = atof(start);
        int   intPart = (int) secs;
        float rest    = secs - intPart;

        if (rest >= 0.5)
        {
          ++intPart;
        }

        value = intPart;
      }
      else
      {
        value = atoi(start);
      }

      accumulated += toSeconds(value, what, dayPart);
      digitsPending = false;
      ++duration;
      start = duration;
    }
    else
    {
      free(toFree);
      return -1;  // ParseError
    }
  }

  free(toFree);

  if (digitsPending == true)
  {
    return -1;
  }

  return accumulated;
}
<commit_msg>Using round function instead of 'homemade'<commit_after>/*
*
* Copyright 2013 Telefonica Investigacion y Desarrollo, S.A.U
*
* This file is part of Orion Context Broker.
*
* Orion Context Broker is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Orion Context Broker 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 Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Orion Context Broker. If not, see http://www.gnu.org/licenses/.
*
* For those usages not covered by this license please contact with
* iot_support at tid dot es
*
* Author: Ken Zangelin
*/
#include <time.h>
#include <stdint.h>
#include <math.h>

#include <string>

#include "logMsg/logMsg.h"
#include "logMsg/traceLevels.h"

#include "common/globals.h"
#include "common/sem.h"
#include "alarmMgr/alarmMgr.h"
#include "serviceRoutines/versionTreat.h"     // For orionInit()
#include "mongoBackend/MongoGlobal.h"         // For orionInit()
#include "ngsiNotify/onTimeIntervalThread.h"  // For orionInit()

/* ****************************************************************************
*
* Globals
*/
static Timer*          timer                = NULL;
int                    startTime            = -1;
int                    statisticsTime       = -1;
OrionExitFunction      orionExitFunction    = NULL;
static struct timeval  logStartTime;
bool                   countersStatistics   = false;
bool                   semWaitStatistics    = false;
bool                   timingStatistics     = false;
bool                   notifQueueStatistics = false;

/* ****************************************************************************
*
* transactionIdSet - set the transaction ID
*
* To ensure a unique identifier of the transaction, the startTime down to milliseconds
* of the broker is used as prefix (to almost guarantee its uniqueness among brokers)
* Furthermore, a running number is appended for the transaction.
* A 32 bit signed number is used, so its max value is 0x7FFFFFFF (2,147,483,647).
*
* If the running number overflows, a millisecond is added to the start time.
*
* The whole thing is stored in the thread variable 'transactionId', supported by the
* logging library 'liblm'.
*
*/
void transactionIdSet(void)
{
  static int transaction = 0;

  transSemTake("transactionIdSet", "changing the transaction id");
  ++transaction;

  if (transaction < 0)
  {
    logStartTime.tv_usec += 1;
    transaction = 1;
  }

  snprintf(transactionId, sizeof(transactionId), "%lu-%03d-%011d",
           logStartTime.tv_sec, (int) logStartTime.tv_usec / 1000, transaction);

  transSemGive("transactionIdSet", "changing the transaction id");
}

/* ****************************************************************************
*
* orionInit -
*/
void orionInit
(
  OrionExitFunction  exitFunction,
  const char*        version,
  SemOpType          reqPolicy,
  bool               _countersStatistics,
  bool               _semWaitStatistics,
  bool               _timingStatistics,
  bool               _notifQueueStatistics
)
{
  // Give the rest library the correct version string of this executable
  versionSet(version);

  // The function to call on fatal error
  orionExitFunction = exitFunction;

  // Initialize the semaphore used by mongoBackend
  semInit(reqPolicy, _semWaitStatistics);

  // Set timer object (singleton)
  setTimer(new Timer());

  // startTime for log library
  if (gettimeofday(&logStartTime, NULL) != 0)
  {
    fprintf(stderr, "gettimeofday: %s\n", strerror(errno));
    orionExitFunction(1, "gettimeofday error");
  }

  // Set start time and statisticsTime used by REST interface
  startTime      = logStartTime.tv_sec;
  statisticsTime = startTime;

  // Set other flags related with statistics
  countersStatistics   = _countersStatistics;
  timingStatistics     = _timingStatistics;
  notifQueueStatistics = _notifQueueStatistics;

  strncpy(transactionId, "N/A", sizeof(transactionId));
}

/* ****************************************************************************
*
* isTrue -
*/
bool isTrue(const std::string& s)
{
  if ((s == "true") || (s == "1"))
  {
    return true;
  }

  return false;
}

/* ****************************************************************************
*
* isFalse -
*/
bool isFalse(const std::string& s)
{
  if ((s == "false") || (s == "0"))
  {
    return true;
  }

  return false;
}

/*****************************************************************************
*
* getTimer -
*/
Timer* getTimer(void)
{
  return timer;
}

/*****************************************************************************
*
* setTimer -
*/
void setTimer(Timer* t)
{
  timer = t;
}

/* ****************************************************************************
*
* getCurrentTime -
*/
int getCurrentTime(void)
{
  if (getTimer() == NULL)
  {
    LM_T(LmtSoftError, ("getTimer() == NULL - calling exit function for library user"));
    orionExitFunction(1, "getTimer() == NULL");
    return -1;
  }

  return getTimer()->getCurrentTime();
}

/* ****************************************************************************
*
* toSeconds -
*/
int64_t toSeconds(int value, char what, bool dayPart)
{
  int64_t result = -1;

  if (dayPart == true)
  {
    if (what == 'Y')
    {
      result = 365L * 24 * 3600 * value;
    }
    else if (what == 'M')
    {
      result = 30L * 24 * 3600 * value;
    }
    else if (what == 'W')
    {
      result = 7L * 24 * 3600 * value;
    }
    else if (what == 'D')
    {
      result = 24L * 3600 * value;
    }
  }
  else
  {
    if (what == 'H')
    {
      result = 3600L * value;
    }
    else if (what == 'M')
    {
      result = 60L * value;
    }
    else if (what == 'S')
    {
      result = value;
    }
  }

  if (result == -1)
  {
    alarmMgr.badInput(clientIp, "ERROR in duration string");
  }

  return result;
}

/*****************************************************************************
*
* parse8601 -
*
* This is common code for Duration and Throttling (at least)
*
*/
int64_t parse8601(const std::string& s)
{
  if (s == "")
  {
    return -1;
  }

  char*      duration    = strdup(s.c_str());
  char*      toFree      = duration;
  bool       dayPart     = true;
  int64_t    accumulated = 0;
  char*      start;

  if (*duration != 'P')
  {
    free(toFree);
    return -1;
  }

  ++duration;
  start = duration;

  if (*duration == 0)
  {
    free(toFree);
    return -1;
  }

  bool digitsPending = false;

  while (*duration != 0)
  {
    if (isdigit(*duration) || (*duration == '.') || (*duration == ','))
    {
      ++duration;
      digitsPending = true;
    }
    else if ((dayPart == true) &&
             ((*duration == 'Y') || (*duration == 'M') || (*duration == 'D') || (*duration == 'W')))
    {
      char what = *duration;

      *duration = 0;
      int value = atoi(start);

      if ((value == 0) && (*start != '0'))
      {
        std::string details = std::string("parse error for duration '") + start + "'";
        alarmMgr.badInput(clientIp, details);

        free(toFree);
        return -1;
      }

      accumulated += toSeconds(value, what, dayPart);
      digitsPending = false;
      ++duration;
      start = duration;
    }
    else if ((dayPart == true) && (*duration == 'T'))
    {
      dayPart = false;
      ++duration;
      start = duration;
      digitsPending = false;
    }
    else if ((dayPart == false) &&
             ((*duration == 'H') || (*duration == 'M') || (*duration == 'S')))
    {
      char what = *duration;
      int  value;

      *duration = 0;

      if (what == 'S')  // We support floats for the seconds, but only to round to an integer
      {
        float secs  = atof(start);
        value       = (int) round(secs);
      }
      else
      {
        value = atoi(start);
      }

      accumulated += toSeconds(value, what, dayPart);
      digitsPending = false;
      ++duration;
      start = duration;
    }
    else
    {
      free(toFree);
      return -1;  // ParseError
    }
  }

  free(toFree);

  if (digitsPending == true)
  {
    return -1;
  }

  return accumulated;
}
<|endoftext|>


<commit_before>/* This file is part of Ingen.  Copyright (C) 2006 Dave Robillard.
 *
 * Ingen 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.
 *
 * Ingen 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 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.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <iostream>
#include <raptor.h>
#include "raul/RDFQuery.h"
#include "Loader.h"
#include "ModelEngineInterface.h"

using namespace Raul;

namespace Ingen {
namespace Client {

Loader::Loader(SharedPtr<ModelEngineInterface> engine, SharedPtr<Namespaces> namespaces)
	: _engine(engine)
	, _namespaces(namespaces)
{
	if (!_namespaces)
		_namespaces = SharedPtr<Namespaces>(new Namespaces());

	// FIXME: hack
	(*_namespaces)["ingen"] = "http://codeson.net/ns/ingen#";
	(*_namespaces)["ingenuity"] = "http://codeson.net/ns/ingenuity#";
	(*_namespaces)["lv2"] = "http://lv2plug.in/ontology#>";
}

/** Load (create) all objects from an RDF into the engine.
 *
 * @param filename Filename to load objects from.
 * @param parent Path of parent under which to load objects.
 * @return whether or not load was successful.
 */
bool
Loader::load(const Glib::ustring& filename,
             const Path&          parent,
			 string               patch_name,
			 Glib::ustring        patch_uri,
			 MetadataMap          data)
{
	// FIXME: this whole thing is a mess

	std::map<Path, bool> created;

	// FIXME: kluge
	unsigned char* document_uri_str = raptor_uri_filename_to_uri_string(filename.c_str());
	Glib::ustring document_uri = (const char*)document_uri_str;
	//Glib::ustring document_uri = "file:///home/dave/code/codesonnet/ingen/src/progs/ingenuity/test2.ingen.ttl";

	if (patch_uri == "")
		patch_uri = "<>"; // FIXME: Will load every patch in the file?

	cerr << "[Loader] Loading " << patch_uri << " from " << document_uri
		<< " under " << parent << endl;

	/* Get polyphony (mandatory) */

	// FIXME: polyphony datatype
	RDFQuery query(*_namespaces, Glib::ustring(
		"SELECT DISTINCT ?poly \nFROM <") + document_uri + ">\nWHERE {\n\t" +
		patch_uri + " ingen:polyphony ?poly .\n"
		"}");

	RDFQuery::Results results = query.run(document_uri);

	if (results.size() == 0) {
		cerr << "[Loader] ERROR: No polyphony found!" << endl;
		return false;
	}

	size_t patch_poly = atoi(((*results.begin())["poly"]).c_str());

	/* Get name (if available/necessary) */

	if (patch_name == "") {
		patch_name = string(filename.substr(filename.find_last_of("/")+1));

		query = RDFQuery(*_namespaces, Glib::ustring(
			"SELECT DISTINCT ?name FROM <") + document_uri + "> WHERE {\n" +
			patch_uri + " ingen:name ?name .\n"
			"}");

		results = query.run(document_uri);

		if (results.size() > 0)
			patch_name = string((*results.begin())["name"]);
	}

	Path patch_path = parent.base() + patch_name;
	cerr << "************ PATCH: " << patch_path << ", poly = " << patch_poly << endl;
	_engine->create_patch(patch_path, patch_poly);

	/* Load nodes */

	query = RDFQuery(*_namespaces, Glib::ustring(
		"SELECT DISTINCT ?name ?plugin ?floatkey ?floatval FROM <") + document_uri + "> WHERE {\n" +
		patch_uri + " ingen:node   ?node .\n"
		"?node        ingen:name   ?name ;\n"
		"             ingen:plugin ?plugin .\n"
		"OPTIONAL { ?node ?floatkey ?floatval . \n"
		"           FILTER ( datatype(?floatval) = xsd:decimal ) }\n"
		"}");

	results = query.run(document_uri);

	for (RDFQuery::Results::iterator i = results.begin(); i != results.end(); ++i) {

		const Glib::ustring& name   = (*i)["name"];
		const Glib::ustring& plugin = (*i)["plugin"];

		const Path node_path = patch_path.base() + (string)name;

		if (created.find(node_path) == created.end()) {
			_engine->create_node(node_path, plugin, false);
			created[node_path] = true;
		}

		Glib::ustring floatkey = _namespaces->qualify((*i)["floatkey"]);
		Glib::ustring floatval = (*i)["floatval"];

		if (floatkey != "" && floatval != "") {
			const float val = atof(floatval.c_str());
			_engine->set_metadata(patch_path.base() + name, floatkey, Atom(val));
		}
	}

	created.clear();

	/* Set node port control values */

	query = RDFQuery(*_namespaces, Glib::ustring(
		"SELECT DISTINCT ?nodename ?portname ?portval FROM <") + document_uri + "> WHERE {\n" +
		patch_uri + " ingen:node   ?node .\n"
		"?node        ingen:name   ?nodename ;\n"
		"             ingen:port   ?port .\n"
		"?port        ingen:name   ?portname ;\n"
		"             ingen:value  ?portval .\n"
		"}\n");

	results = query.run(document_uri);

	for (RDFQuery::Results::iterator i = results.begin(); i != results.end(); ++i) {

		const Glib::ustring& node_name = (*i)["nodename"];
		const Glib::ustring& port_name = (*i)["portname"];
		const Glib::ustring& portval   = (*i)["portval"];

		Path port_path = patch_path.base() + (const string&)node_name +"/"+ (const string&)port_name;

		if (portval != "") {
			const float val = atof(portval.c_str());
			cerr << port_path << " VALUE: " << val << endl;
			_engine->set_port_value(port_path, val);
		}
	}

	/* Load this patch's ports */

	query = RDFQuery(*_namespaces, Glib::ustring(
		"SELECT DISTINCT ?port ?type ?name ?datatype ?floatkey ?floatval ?portval FROM <") + document_uri + "> WHERE {\n" +
		patch_uri + " ingen:port     ?port .\n"
		"?port        a              ?type ;\n"
		"             ingen:name     ?name ;\n"
		"             ingen:dataType ?datatype .\n"
		"OPTIONAL { ?port ?floatkey ?floatval . \n"
		"           FILTER ( datatype(?floatval) = xsd:decimal ) }\n"
		"OPTIONAL { ?port ingen:value ?portval . \n"
		"           FILTER ( datatype(?portval) = xsd:decimal ) }\n"
		"}");

	results = query.run(document_uri);

	for (RDFQuery::Results::iterator i = results.begin(); i != results.end(); ++i) {
		const Glib::ustring& name     = (*i)["name"];
		const Glib::ustring& type     = _namespaces->qualify((*i)["type"]);
		const Glib::ustring& datatype = (*i)["datatype"];

		const Path port_path = patch_path.base() + (string)name;

		if (created.find(port_path) == created.end()) {
			//cerr << "TYPE: " << type << endl;
			bool is_output = (type == "ingen:OutputPort"); // FIXME: check validity
			_engine->create_port(port_path, datatype, is_output);
			created[port_path] = true;
		}

		const Glib::ustring& portval = (*i)["portval"];
		if (portval != "") {
			const float val = atof(portval.c_str());
			cerr << name << " VALUE: " << val << endl;
			_engine->set_port_value(patch_path.base() + name, val);
		}

		const Glib::ustring& floatkey = _namespaces->qualify((*i)["floatkey"]);
		const Glib::ustring& floatval = (*i)["floatval"];

		if (floatkey != "" && floatval != "") {
			const float val = atof(floatval.c_str());
			_engine->set_metadata(patch_path.base() + name, floatkey, Atom(val));
		}
	}

	created.clear();

	/* Load connections */

	// FIXME: path?
	query = RDFQuery(*_namespaces, Glib::ustring(
		"SELECT DISTINCT ?srcnode ?src ?dstnode ?dst FROM <") + document_uri + "> WHERE {\n" +
		"?srcnoderef ingen:port ?srcref .\n"
		"?dstnoderef ingen:port ?dstref .\n"
		"?dstref ingen:connectedTo ?srcref .\n"
		"?srcref ingen:name ?src .\n"
		"?dstref ingen:name ?dst .\n"
		"OPTIONAL { ?srcnoderef ingen:name ?srcnode } .\n"
		"OPTIONAL { ?dstnoderef ingen:name ?dstnode }\n"
		"}\n");

	results = query.run(document_uri);

	for (RDFQuery::Results::iterator i = results.begin(); i != results.end(); ++i) {
		Path src_node = patch_path;
		if ((*i).find("srcnode") != (*i).end())
			src_node = patch_path.base() + string((*i)["srcnode"]);
		Path src_port = src_node.base() + string((*i)["src"]);

		Path dst_node = patch_path;
		if ((*i).find("dstnode") != (*i).end())
			dst_node = patch_path.base() + string((*i)["dstnode"]);
		Path dst_port = dst_node.base() + string((*i)["dst"]);

		//cerr << "CONNECTION: " << src_port << " -> " << dst_port << endl;

		_engine->connect(src_port, dst_port);
	}

	// Set passed metadata last to override any loaded values
	for (MetadataMap::const_iterator i = data.begin(); i != data.end(); ++i)
		_engine->set_metadata(patch_path, i->first, i->second);

	return true;
}

} // namespace Client
} // namespace Ingen

<commit_msg>Bug fixes, LV2 spec updates.<commit_after>/* This file is part of Ingen.  Copyright (C) 2006 Dave Robillard.
 *
 * Ingen 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.
 *
 * Ingen 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 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.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <iostream>
#include <raptor.h>
#include "raul/RDFQuery.h"
#include "Loader.h"
#include "ModelEngineInterface.h"

using namespace Raul;

namespace Ingen {
namespace Client {

Loader::Loader(SharedPtr<ModelEngineInterface> engine, SharedPtr<Namespaces> namespaces)
	: _engine(engine)
	, _namespaces(namespaces)
{
	if (!_namespaces)
		_namespaces = SharedPtr<Namespaces>(new Namespaces());

	(*_namespaces)["xsd"] = "http://www.w3.org/2001/XMLSchema#";
	(*_namespaces)["ingen"] = "http://codeson.net/ns/ingen#";
	(*_namespaces)["ingenuity"] = "http://codeson.net/ns/ingenuity#";
	(*_namespaces)["lv2"] = "http://lv2plug.in/ontology#>";
}

/** Load (create) all objects from an RDF into the engine.
 *
 * @param filename Filename to load objects from.
 * @param parent Path of parent under which to load objects.
 * @return whether or not load was successful.
 */
bool
Loader::load(const Glib::ustring& filename,
             const Path&          parent,
			 string               patch_name,
			 Glib::ustring        patch_uri,
			 MetadataMap          data)
{
	// FIXME: this whole thing is a mess

	std::map<Path, bool> created;

	// FIXME: kluge
	unsigned char* document_uri_str = raptor_uri_filename_to_uri_string(filename.c_str());
	Glib::ustring document_uri = (const char*)document_uri_str;
	//Glib::ustring document_uri = "file:///home/dave/code/codesonnet/ingen/src/progs/ingenuity/test2.ingen.ttl";

	if (patch_uri == "")
		patch_uri = "<>"; // FIXME: Will load every patch in the file?

	cerr << "[Loader] Loading " << patch_uri << " from " << document_uri
		<< " under " << parent << endl;

	/* Get polyphony (mandatory) */

	// FIXME: polyphony datatype
	RDFQuery query(*_namespaces, Glib::ustring(
		"SELECT DISTINCT ?poly \nFROM <") + document_uri + ">\nWHERE {\n\t" +
		patch_uri + " ingen:polyphony ?poly .\n"
		"}");

	RDFQuery::Results results = query.run(document_uri);

	if (results.size() == 0) {
		cerr << "[Loader] ERROR: No polyphony found!" << endl;
		return false;
	}

	size_t patch_poly = atoi(((*results.begin())["poly"]).c_str());

	/* Get name (if available/necessary) */

	if (patch_name == "") {
		patch_name = string(filename.substr(filename.find_last_of("/")+1));

		query = RDFQuery(*_namespaces, Glib::ustring(
			"SELECT DISTINCT ?name FROM <") + document_uri + "> WHERE {\n" +
			patch_uri + " ingen:name ?name .\n"
			"}");

		results = query.run(document_uri);

		if (results.size() > 0)
			patch_name = string((*results.begin())["name"]);
	}

	Path patch_path = parent.base() + patch_name;
	cerr << "************ PATCH: " << patch_path << ", poly = " << patch_poly << endl;
	_engine->create_patch(patch_path, patch_poly);

	/* Load nodes */

	query = RDFQuery(*_namespaces, Glib::ustring(
		"SELECT DISTINCT ?name ?plugin ?floatkey ?floatval FROM <") + document_uri + "> WHERE {\n" +
		patch_uri + " ingen:node   ?node .\n"
		"?node        ingen:name   ?name ;\n"
		"             ingen:plugin ?plugin .\n"
		"OPTIONAL { ?node ?floatkey ?floatval . \n"
		"           FILTER ( datatype(?floatval) = xsd:decimal ) }\n"
		"}");

	results = query.run(document_uri);

	for (RDFQuery::Results::iterator i = results.begin(); i != results.end(); ++i) {

		const Glib::ustring& name   = (*i)["name"];
		const Glib::ustring& plugin = (*i)["plugin"];

		const Path node_path = patch_path.base() + (string)name;

		if (created.find(node_path) == created.end()) {
			_engine->create_node(node_path, plugin, false);
			created[node_path] = true;
		}

		Glib::ustring floatkey = _namespaces->qualify((*i)["floatkey"]);
		Glib::ustring floatval = (*i)["floatval"];

		if (floatkey != "" && floatval != "") {
			const float val = atof(floatval.c_str());
			_engine->set_metadata(patch_path.base() + name, floatkey, Atom(val));
		}
	}

	created.clear();

	/* Set node port control values */

	query = RDFQuery(*_namespaces, Glib::ustring(
		"SELECT DISTINCT ?nodename ?portname ?portval FROM <") + document_uri + "> WHERE {\n" +
		patch_uri + " ingen:node   ?node .\n"
		"?node        ingen:name   ?nodename ;\n"
		"             ingen:port   ?port .\n"
		"?port        ingen:name   ?portname ;\n"
		"             ingen:value  ?portval .\n"
		"}\n");

	results = query.run(document_uri);

	for (RDFQuery::Results::iterator i = results.begin(); i != results.end(); ++i) {

		const Glib::ustring& node_name = (*i)["nodename"];
		const Glib::ustring& port_name = (*i)["portname"];
		const Glib::ustring& portval   = (*i)["portval"];

		Path port_path = patch_path.base() + (const string&)node_name +"/"+ (const string&)port_name;

		if (portval != "") {
			const float val = atof(portval.c_str());
			cerr << port_path << " VALUE: " << val << endl;
			_engine->set_port_value(port_path, val);
		}
	}

	/* Load this patch's ports */

	query = RDFQuery(*_namespaces, Glib::ustring(
		"SELECT DISTINCT ?port ?type ?name ?datatype ?floatkey ?floatval ?portval FROM <") + document_uri + "> WHERE {\n" +
		patch_uri + " ingen:port     ?port .\n"
		"?port        a              ?type ;\n"
		"             ingen:name     ?name ;\n"
		"             ingen:dataType ?datatype .\n"
		"OPTIONAL { ?port ?floatkey ?floatval . \n"
		"           FILTER ( datatype(?floatval) = xsd:decimal ) }\n"
		"OPTIONAL { ?port ingen:value ?portval . \n"
		"           FILTER ( datatype(?portval) = xsd:decimal ) }\n"
		"}");

	results = query.run(document_uri);

	for (RDFQuery::Results::iterator i = results.begin(); i != results.end(); ++i) {
		const Glib::ustring& name     = (*i)["name"];
		const Glib::ustring& type     = _namespaces->qualify((*i)["type"]);
		const Glib::ustring& datatype = (*i)["datatype"];

		const Path port_path = patch_path.base() + (string)name;

		if (created.find(port_path) == created.end()) {
			//cerr << "TYPE: " << type << endl;
			bool is_output = (type == "ingen:OutputPort"); // FIXME: check validity
			_engine->create_port(port_path, datatype, is_output);
			created[port_path] = true;
		}

		const Glib::ustring& portval = (*i)["portval"];
		if (portval != "") {
			const float val = atof(portval.c_str());
			cerr << name << " VALUE: " << val << endl;
			_engine->set_port_value(patch_path.base() + name, val);
		}

		const Glib::ustring& floatkey = _namespaces->qualify((*i)["floatkey"]);
		const Glib::ustring& floatval = (*i)["floatval"];

		if (floatkey != "" && floatval != "") {
			const float val = atof(floatval.c_str());
			_engine->set_metadata(patch_path.base() + name, floatkey, Atom(val));
		}
	}

	created.clear();

	/* Load connections */

	// FIXME: path?
	query = RDFQuery(*_namespaces, Glib::ustring(
		"SELECT DISTINCT ?srcnode ?src ?dstnode ?dst FROM <") + document_uri + "> WHERE {\n" +
		"?srcnoderef ingen:port ?srcref .\n"
		"?dstnoderef ingen:port ?dstref .\n"
		"?dstref ingen:connectedTo ?srcref .\n"
		"?srcref ingen:name ?src .\n"
		"?dstref ingen:name ?dst .\n"
		"OPTIONAL { ?srcnoderef ingen:name ?srcnode } .\n"
		"OPTIONAL { ?dstnoderef ingen:name ?dstnode }\n"
		"}\n");

	results = query.run(document_uri);

	for (RDFQuery::Results::iterator i = results.begin(); i != results.end(); ++i) {
		Path src_node = patch_path;
		if ((*i).find("srcnode") != (*i).end())
			src_node = patch_path.base() + string((*i)["srcnode"]);
		Path src_port = src_node.base() + string((*i)["src"]);

		Path dst_node = patch_path;
		if ((*i).find("dstnode") != (*i).end())
			dst_node = patch_path.base() + string((*i)["dstnode"]);
		Path dst_port = dst_node.base() + string((*i)["dst"]);

		//cerr << "CONNECTION: " << src_port << " -> " << dst_port << endl;

		_engine->connect(src_port, dst_port);
	}

	// Set passed metadata last to override any loaded values
	for (MetadataMap::const_iterator i = data.begin(); i != data.end(); ++i)
		_engine->set_metadata(patch_path, i->first, i->second);

	return true;
}

} // namespace Client
} // namespace Ingen

<|endoftext|>


<commit_before>/*
 * Copyright 2017 Buck Baskin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */
#include <quirkd/libsemi_static_map.h>

#include <math.h>
#include <stdint.h>
#include <stdbool.h>

namespace quirkd
{
SemiStaticMap::SemiStaticMap(ros::NodeHandle nh) : n_(nh)
{
  ROS_INFO("WaitForService(\"static_map\");");
  ros::service::waitForService("static_map");
  static_map_client_ = n_.serviceClient<nav_msgs::GetMap>("static_map");
}
SemiStaticMap::~SemiStaticMap()
{
  ROS_INFO("Destroying SemiStaticMap");
}
void SemiStaticMap::run()
{
  ROS_INFO("SSM run");
  ros::Rate r(30);

  while (ros::ok())
  {
    ros::spinOnce();
  }
  ROS_INFO("SemiStaticMap Node Exited.");
}
bool SemiStaticMap::setMapCallback(nav_msgs::SetMap::Request& req, nav_msgs::SetMap::Response& res) {
  map_ = req.map;
  initial_pose_ = req.initial_pose;
  res.success = true;
  return true;
}
bool SemiStaticMap::updateMapCallback(quirkd::UpdateMap::Request& req, quirkd::UpdateMap::Response& res) {
  map_ = req.map;
  res.success = mergeMap(&map_, &req.map);
  return res.success;
}
bool SemiStaticMap::getMapCallback(nav_msgs::GetMap::Request& req, nav_msgs::GetMap::Response& res) {
  res.map = map_;
  return true;
}

bool SemiStaticMap::mergeMap(nav_msgs::OccupancyGrid* original, nav_msgs::OccupancyGrid* new_section) {
  if (original->header.frame_id != new_section->header.frame_id) {
    // TODO use transforms to align properly?
    // TODO make sure those transforms are translation only for now
    return false;
  }
  if (original->info.resolution != new_section->info.resolution) {
    // TODO make more educated checks and or use OpenCV to scale?
    return false;
  }
  if (original->info.origin.orientation.x != new_section->info.origin.orientation.x ||
    original->info.origin.orientation.y != new_section->info.origin.orientation.y ||
    original->info.origin.orientation.z != new_section->info.origin.orientation.z ||
    original->info.origin.orientation.w != new_section->info.origin.orientation.w) {
    // TODO consider allowing rotational alignments in the future
    return false;
  }
  // The maps are in the same frame (map?), the same resolution and oriented in the same direction
  cv::Mat og_mat = mapToMat(original);
  cv::Mat new_mat = mapToMat(new_section);
  cv::Rect og_rect = mapToRect(original);
  cv::Rect new_rect = mapToRect(new_section);

  cv::Rect both_rect = og_rect | new_rect;
  cv::Mat both; // TODO create an image with default -1

  matToMap(both, original);
  return true;
}
cv::Mat SemiStaticMap::mapToMat(nav_msgs::OccupancyGrid* map) {
  cv::Mat toReturn;
  return toReturn;
}
cv::Rect SemiStaticMap::mapToRect(nav_msgs::OccupancyGrid* map) {
  cv::Rect toReturn;
  return toReturn;
}
void SemiStaticMap::matToMap(cv::Mat as_img, nav_msgs::OccupancyGrid* map) {

}
}  // namespace quirkd
<commit_msg>Cleanup commit<commit_after>/*
 * Copyright 2017 Buck Baskin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */
#include <quirkd/libsemi_static_map.h>

#include <math.h>
#include <stdint.h>
#include <stdbool.h>

namespace quirkd
{
SemiStaticMap::SemiStaticMap(ros::NodeHandle nh) : n_(nh)
{
  ROS_INFO("WaitForService(\"static_map\");");
  ros::service::waitForService("static_map");
  static_map_client_ = n_.serviceClient<nav_msgs::GetMap>("static_map");
}
SemiStaticMap::~SemiStaticMap()
{
  ROS_INFO("Destroying SemiStaticMap");
}
void SemiStaticMap::run()
{
  ROS_INFO("SSM run");
  ros::Rate r(30);

  while (ros::ok())
  {
    ros::spinOnce();
  }
  ROS_INFO("SemiStaticMap Node Exited.");
}
bool SemiStaticMap::setMapCallback(nav_msgs::SetMap::Request& req, nav_msgs::SetMap::Response& res) {
  map_ = req.map;
  initial_pose_ = req.initial_pose;
  res.success = true;
  return true;
}
bool SemiStaticMap::updateMapCallback(quirkd::UpdateMap::Request& req, quirkd::UpdateMap::Response& res) {
  map_ = req.map;
  res.success = mergeMap(&map_, &req.map);
  return res.success;
}
bool SemiStaticMap::getMapCallback(nav_msgs::GetMap::Request& req, nav_msgs::GetMap::Response& res) {
  res.map = map_;
  return true;
}

bool SemiStaticMap::mergeMap(nav_msgs::OccupancyGrid* original, nav_msgs::OccupancyGrid* new_section) {
  if (original->header.frame_id != new_section->header.frame_id) {
    // TODO? use transforms to align properly?
    // TODO? make sure those transforms are translation only for now
    return false;
  }
  if (original->info.resolution != new_section->info.resolution) {
    // TODO? make more educated checks and or use OpenCV to scale?
    return false;
  }
  if (original->info.origin.orientation.x != new_section->info.origin.orientation.x ||
    original->info.origin.orientation.y != new_section->info.origin.orientation.y ||
    original->info.origin.orientation.z != new_section->info.origin.orientation.z ||
    original->info.origin.orientation.w != new_section->info.origin.orientation.w) {
    // TODO? consider allowing rotational alignments in the future
    return false;
  }
  double common_resolution = original->info.resolution;
  // The maps are in the same frame (map?), the same resolution and oriented in the same direction
  cv::Mat og_mat = mapToMat(original);
  cv::Mat new_mat = mapToMat(new_section);
  cv::Rect og_rect = mapToRect(original);
  cv::Rect new_rect = mapToRect(new_section);

  cv::Rect both_rect = og_rect | new_rect;
  cv::Mat both_mat(
    both_rect.height / common_resolution,
    both_rect.width / common_resolution,
    CV_8SC1,
    cv::Scalar(-1)
    );

  // TODO place the other two maps into both_mat

  matToMap(both_mat, original);
  return true;
}
cv::Mat SemiStaticMap::mapToMat(nav_msgs::OccupancyGrid* map) {
  // TODO implement
  cv::Mat toReturn;
  return toReturn;
}
cv::Rect SemiStaticMap::mapToRect(nav_msgs::OccupancyGrid* map) {
  // TODO implement
  cv::Rect toReturn;
  return toReturn;
}
void SemiStaticMap::matToMap(cv::Mat as_img, nav_msgs::OccupancyGrid* map) {
  // TODO implement

}
}  // namespace quirkd
<|endoftext|>


<commit_before>/**
 * @file localStorageManager.cc
 * @author Krzysztof Trzepla
 * @copyright (C) 2014 ACK CYFRONET AGH
 * @copyright This software is released under the MIT license cited in 'LICENSE.txt'
 */

#include "localStorageManager.h"
#include "config.h"
#include "veilfs.h"
#include "logging.h"
#include "communication_protocol.pb.h"
#include "fuse_messages.pb.h"
#include <google/protobuf/descriptor.h>

using boost::filesystem::path;
using namespace veil::protocol::fuse_messages;
using namespace veil::protocol::communication_protocol;

namespace veil {
namespace client {

LocalStorageManager::LocalStorageManager()
{
}

LocalStorageManager::~LocalStorageManager()
{
}

#ifdef __APPLE__

std::vector<path> LocalStorageManager::getMountPoints()
{
    std::vector<path> mountPoints;

    int fs_num;
    if((fs_num = getfsstat(NULL, 0, MNT_NOWAIT)) < 0) {
        LOG(ERROR) << "Can not count mounted filesystems.";
        return mountPoints;
    }

    int buf_size = sizeof(struct statfs) * fs_num;
    struct statfs *buf = (struct statfs*) malloc(buf_size);
    if(buf == NULL) {
        LOG(ERROR) << "Can not allocate memory for statfs structures.";
        return std::move(mountPoints);
    }

    int stat_num;
    if((stat_num = getfsstat(buf, buf_size, MNT_NOWAIT)) < 0) {
        LOG(ERROR) << "Can not get fsstat.";
        free(buf);
        return std::move(mountPoints);
    }

    for(int i = 0; i < stat_num; ++i) {
        std::string type(buf[i].f_fstypename);
        if(type.compare(0, 4, "fuse") != 0) {
            mountPoints.push_back(path(buf[i].f_mntonname).normalize());
        }
    }

    free(buf);
    return std::move(mountPoints);
}

#else

std::vector<path> LocalStorageManager::getMountPoints()
{
    std::vector<path> mountPoints;

    FILE *file;
    if((file = setmntent(MOUNTS_INFO_FILE_PATH, "r")) == NULL) {
        LOG(ERROR) << "Can not parse /proc/mounts file.";
        return mountPoints;
    }

    struct mntent *ent;
    while ((ent = getmntent(file)) != NULL) {
        std::string type(ent->mnt_type);
        if(type.compare(0, 4, "fuse") != 0) {
            mountPoints.push_back(path(ent->mnt_dir).normalize());
        }
    }

    endmntent(file);

    return std::move(mountPoints);
}

#endif

std::vector< std::pair<int, std::string> > LocalStorageManager::parseStorageInfo(path mountPoint)
{
    std::vector< std::pair<int, std::string> > storageInfo;
    path storageInfoPath = (mountPoint / STORAGE_INFO_FILENAME).normalize();

    if(boost::filesystem::exists(storageInfoPath) && boost::filesystem::is_regular_file(storageInfoPath)) {
        boost::filesystem::ifstream storageInfoFile(storageInfoPath);
        std::string line;

        while(std::getline(storageInfoFile, line)) {
            std::vector<std::string> tokens;
            boost::char_separator<char> sep(" ,{}");
            boost::tokenizer< boost::char_separator<char> > tok(line, sep);

            for(auto token: tok) tokens.push_back(token);

            // each line in file should by of form {storage id, relative path to storage against mount point}
            if(tokens.size() == 2) {
                try {
                    int storageId = boost::lexical_cast<int>(tokens[0]);
                    path absoluteStoragePath = mountPoint;

                    boost::algorithm::trim_left_if(tokens[1], boost::algorithm::is_any_of("./"));
                    if(!tokens[1].empty()) {
                        absoluteStoragePath /= tokens[1];
                    }
                    storageInfo.push_back(make_pair(storageId, absoluteStoragePath.string()));
                } catch(boost::bad_lexical_cast const&) {
                    LOG(ERROR) << "Wrong format of storage id in file: " << storageInfoPath;
                }
            }
        }
    }
    return std::move(storageInfo);
}

std::vector< std::pair<int, std::string> > LocalStorageManager::getClientStorageInfo(const std::vector<path> &mountPoints)
{
    std::vector< std::pair<int, std::string> > clientStorageInfo;

    for(auto mountPoint: mountPoints) {

        // Skip client mount point (just in case)
        if(mountPoint == Config::getMountPoint()) continue;

        std::vector< std::pair<int, std::string> > storageInfo = parseStorageInfo(mountPoint);

        for(std::vector< std::pair<int, std::string> >::iterator it = storageInfo.begin(); it != storageInfo.end(); ++it) {
            int storageId = it->first;
            std::string absolutePath = it->second;
            std::string relativePath = "";
            std::string text = "";

            if(!createStorageTestFile(storageId, relativePath, text)) {
                LOG(WARNING) << "Cannot create storage test file for storage with id: " << storageId;
                continue;
            }
            if(!hasClientStorageReadPermission(absolutePath, relativePath, text)) {
                LOG(WARNING) << "Client does not have read permission for storage with id: " << storageId;
                continue;
            }
            if(!hasClientStorageWritePermission(storageId, absolutePath, relativePath)) {
                LOG(WARNING) << "Client does not have write permission for storage with id: " << storageId;
                continue;
            }

            LOG(INFO) << "Storage with id: " << storageId << " is directly accessible to the client via: " << absolutePath;
            clientStorageInfo.push_back(make_pair(storageId, absolutePath));
        }
    }
    return std::move(clientStorageInfo);
}

bool LocalStorageManager::sendClientStorageInfo(std::vector< std::pair<int, std::string> > clientStorageInfo)
{
    ClusterMsg cMsg;
    ClientStorageInfo reqMsg;
    ClientStorageInfo::StorageInfo *info;
    Atom resMsg;
    Answer ans;

    MessageBuilder builder;
    boost::shared_ptr<CommunicationHandler> conn;

	conn = VeilFS::getConnectionPool()->selectConnection();
	if(conn) {
	    // Build CreateStorageTestFileRequest message
		for(std::vector< std::pair<int,std::string> >::iterator it = clientStorageInfo.begin(); it != clientStorageInfo.end(); ++it) {
		    info = reqMsg.add_storage_info();
		    info->set_storage_id(it->first);
		    info->set_absolute_path(it->second);
		    LOG(INFO) << "Sending client storage info: {" << it->first << ", " << it->second << "}";
		}
		ClusterMsg cMsg = builder.packFuseMessage(ClientStorageInfo::descriptor()->name(), Atom::descriptor()->name(), COMMUNICATION_PROTOCOL, reqMsg.SerializeAsString());
        // Send CreateStorageTestFileRequest message
		ans = conn->communicate(cMsg, 2);
		// Check answer
		if(ans.answer_status() == VOK && resMsg.ParseFromString(ans.worker_answer())) {
			return resMsg.value() == "ok";
		} else if(ans.answer_status() == NO_USER_FOUND_ERROR) {
            LOG(ERROR) << "Cannot find user in database.";
        } else {
            LOG(ERROR) << "Cannot send client storage info.";
        }
    } else {
        LOG(ERROR) << "Cannot select connection for storage test file creation";
    }
    return false;
}

bool LocalStorageManager::createStorageTestFile(int storageId, std::string& relativePath, std::string& text)
{
    ClusterMsg cMsg;
    CreateStorageTestFileRequest reqMsg;
    CreateStorageTestFileResponse resMsg;
    Answer ans;

    MessageBuilder builder;
    boost::shared_ptr<CommunicationHandler> conn;

    conn = VeilFS::getConnectionPool()->selectConnection();
    if(conn) {
        // Build CreateStorageTestFileRequest message
        reqMsg.set_storage_id(storageId);
        ClusterMsg cMsg = builder.packFuseMessage(CreateStorageTestFileRequest::descriptor()->name(), CreateStorageTestFileResponse::descriptor()->name(), FUSE_MESSAGES, reqMsg.SerializeAsString());
        // Send CreateStorageTestFileRequest message
        ans = conn->communicate(cMsg, 2);
    	// Check answer
        if(ans.answer_status() == VOK && resMsg.ParseFromString(ans.worker_answer())) {
            relativePath = resMsg.relative_path();
            text = resMsg.text();
			return resMsg.answer();
		} else if(ans.answer_status() == NO_USER_FOUND_ERROR) {
            LOG(ERROR) << "Cannot find user in database.";
        } else {
            LOG(ERROR) << "Cannot create test file for storage with id: " << storageId;
        }
    } else {
        LOG(ERROR) << "Cannot select connection for storage test file creation";
    }
    return false;
}

bool LocalStorageManager::hasClientStorageReadPermission(std::string absolutePath, std::string relativePath, std::string expectedText)
{
    int fd = open((absolutePath + "/" + relativePath).c_str(), O_RDONLY | O_FSYNC);
    if(fd == -1) {
        return false;
    }
    fsync(fd);
    void* buf = malloc(sizeof(char) * expectedText.size());
    if(read(fd, buf, expectedText.size()) != (int) expectedText.size()) {
        free(buf);
        close(fd);
        return false;
    }
    std::string actualText((char *) buf, expectedText.size());
    free(buf);
    close(fd);
    return expectedText == actualText;
}

bool LocalStorageManager::hasClientStorageWritePermission(int storageId, std::string absolutePath, std::string relativePath)
{
    int fd = open((absolutePath + "/" + relativePath).c_str(), O_WRONLY | O_FSYNC);
    if(fd == -1) {
        return false;
    }
    int length = 20;
    std::string text(length, ' ');
    srand(time(0));
    for(int i = 0; i < length; ++i) {
        text[i] = (char) (33 + rand() % 93);
    }
    if(write(fd, text.c_str(), length) != length) {
        close(fd);
        return false;
    }
    close(fd);

    ClusterMsg cMsg;
    StorageTestFileModifiedRequest reqMsg;
    StorageTestFileModifiedResponse resMsg;
    Answer ans;

    MessageBuilder builder;
    boost::shared_ptr<CommunicationHandler> conn;

    conn = VeilFS::getConnectionPool()->selectConnection();
    if(conn) {
        // Build CreateStorageTestFileRequest message
        reqMsg.set_storage_id(storageId);
        reqMsg.set_relative_path(relativePath);
        reqMsg.set_text(text);
        ClusterMsg cMsg = builder.packFuseMessage(StorageTestFileModifiedRequest::descriptor()->name(), StorageTestFileModifiedResponse::descriptor()->name(), FUSE_MESSAGES, reqMsg.SerializeAsString());
        // Send CreateStorageTestFileRequest message
        ans = conn->communicate(cMsg, 2);
    	// Check answer
        if(ans.answer_status() == VOK && resMsg.ParseFromString(ans.worker_answer())) {
            return resMsg.answer();
    	} else if(ans.answer_status() == NO_USER_FOUND_ERROR) {
            LOG(ERROR) << "Cannot find user in database.";
        } else {
            LOG(ERROR) << "Cannot check client write permission for storage with id: " << storageId;
        }
    } else {
        LOG(ERROR) << "Cannot select connection for storage test file creation";
    }
    return false;
}

} // namespace client
} // namespace veil
<commit_msg>VFS-689 Fix minor bug.<commit_after>/**
 * @file localStorageManager.cc
 * @author Krzysztof Trzepla
 * @copyright (C) 2014 ACK CYFRONET AGH
 * @copyright This software is released under the MIT license cited in 'LICENSE.txt'
 */

#include "localStorageManager.h"
#include "config.h"
#include "veilfs.h"
#include "logging.h"
#include "communication_protocol.pb.h"
#include "fuse_messages.pb.h"
#include <google/protobuf/descriptor.h>

using boost::filesystem::path;
using namespace veil::protocol::fuse_messages;
using namespace veil::protocol::communication_protocol;

namespace veil {
namespace client {

LocalStorageManager::LocalStorageManager()
{
}

LocalStorageManager::~LocalStorageManager()
{
}

#ifdef __APPLE__

std::vector<path> LocalStorageManager::getMountPoints()
{
    std::vector<path> mountPoints;

    int fs_num;
    if((fs_num = getfsstat(NULL, 0, MNT_NOWAIT)) < 0) {
        LOG(ERROR) << "Can not count mounted filesystems.";
        return mountPoints;
    }

    int buf_size = sizeof(struct statfs) * fs_num;
    struct statfs *buf = (struct statfs*) malloc(buf_size);
    if(buf == NULL) {
        LOG(ERROR) << "Can not allocate memory for statfs structures.";
        return std::move(mountPoints);
    }

    int stat_num;
    if((stat_num = getfsstat(buf, buf_size, MNT_NOWAIT)) < 0) {
        LOG(ERROR) << "Can not get fsstat.";
        free(buf);
        return std::move(mountPoints);
    }

    for(int i = 0; i < stat_num; ++i) {
        std::string type(buf[i].f_fstypename);
        if(type.compare(0, 4, "fuse") != 0) {
            mountPoints.push_back(path(buf[i].f_mntonname).normalize());
        }
    }

    free(buf);
    return std::move(mountPoints);
}

#else

std::vector<path> LocalStorageManager::getMountPoints()
{
    std::vector<path> mountPoints;

    FILE *file;
    if((file = setmntent(MOUNTS_INFO_FILE_PATH, "r")) == NULL) {
        LOG(ERROR) << "Can not parse /proc/mounts file.";
        return std::move(mountPoints);
    }

    struct mntent *ent;
    while ((ent = getmntent(file)) != NULL) {
        std::string type(ent->mnt_type);
        if(type.compare(0, 4, "fuse") != 0) {
            mountPoints.push_back(path(ent->mnt_dir).normalize());
        }
    }

    endmntent(file);

    return std::move(mountPoints);
}

#endif

std::vector< std::pair<int, std::string> > LocalStorageManager::parseStorageInfo(path mountPoint)
{
    std::vector< std::pair<int, std::string> > storageInfo;
    path storageInfoPath = (mountPoint / STORAGE_INFO_FILENAME).normalize();

    if(boost::filesystem::exists(storageInfoPath) && boost::filesystem::is_regular_file(storageInfoPath)) {
        boost::filesystem::ifstream storageInfoFile(storageInfoPath);
        std::string line;

        while(std::getline(storageInfoFile, line)) {
            std::vector<std::string> tokens;
            boost::char_separator<char> sep(" ,{}");
            boost::tokenizer< boost::char_separator<char> > tok(line, sep);

            for(auto token: tok) tokens.push_back(token);

            // each line in file should by of form {storage id, relative path to storage against mount point}
            if(tokens.size() == 2) {
                try {
                    int storageId = boost::lexical_cast<int>(tokens[0]);
                    path absoluteStoragePath = mountPoint;

                    boost::algorithm::trim_left_if(tokens[1], boost::algorithm::is_any_of("./"));
                    if(!tokens[1].empty()) {
                        absoluteStoragePath /= tokens[1];
                    }
                    storageInfo.push_back(make_pair(storageId, absoluteStoragePath.string()));
                } catch(boost::bad_lexical_cast const&) {
                    LOG(ERROR) << "Wrong format of storage id in file: " << storageInfoPath;
                }
            }
        }
    }
    return std::move(storageInfo);
}

std::vector< std::pair<int, std::string> > LocalStorageManager::getClientStorageInfo(const std::vector<path> &mountPoints)
{
    std::vector< std::pair<int, std::string> > clientStorageInfo;

    for(auto mountPoint: mountPoints) {

        // Skip client mount point (just in case)
        if(mountPoint == Config::getMountPoint()) continue;

        std::vector< std::pair<int, std::string> > storageInfo = parseStorageInfo(mountPoint);

        for(std::vector< std::pair<int, std::string> >::iterator it = storageInfo.begin(); it != storageInfo.end(); ++it) {
            int storageId = it->first;
            std::string absolutePath = it->second;
            std::string relativePath = "";
            std::string text = "";

            if(!createStorageTestFile(storageId, relativePath, text)) {
                LOG(WARNING) << "Cannot create storage test file for storage with id: " << storageId;
                continue;
            }
            if(!hasClientStorageReadPermission(absolutePath, relativePath, text)) {
                LOG(WARNING) << "Client does not have read permission for storage with id: " << storageId;
                continue;
            }
            if(!hasClientStorageWritePermission(storageId, absolutePath, relativePath)) {
                LOG(WARNING) << "Client does not have write permission for storage with id: " << storageId;
                continue;
            }

            LOG(INFO) << "Storage with id: " << storageId << " is directly accessible to the client via: " << absolutePath;
            clientStorageInfo.push_back(make_pair(storageId, absolutePath));
        }
    }
    return std::move(clientStorageInfo);
}

bool LocalStorageManager::sendClientStorageInfo(std::vector< std::pair<int, std::string> > clientStorageInfo)
{
    ClusterMsg cMsg;
    ClientStorageInfo reqMsg;
    ClientStorageInfo::StorageInfo *info;
    Atom resMsg;
    Answer ans;

    MessageBuilder builder;
    boost::shared_ptr<CommunicationHandler> conn;

	conn = VeilFS::getConnectionPool()->selectConnection();
	if(conn) {
	    // Build CreateStorageTestFileRequest message
		for(std::vector< std::pair<int,std::string> >::iterator it = clientStorageInfo.begin(); it != clientStorageInfo.end(); ++it) {
		    info = reqMsg.add_storage_info();
		    info->set_storage_id(it->first);
		    info->set_absolute_path(it->second);
		    LOG(INFO) << "Sending client storage info: {" << it->first << ", " << it->second << "}";
		}
		ClusterMsg cMsg = builder.packFuseMessage(ClientStorageInfo::descriptor()->name(), Atom::descriptor()->name(), COMMUNICATION_PROTOCOL, reqMsg.SerializeAsString());
        // Send CreateStorageTestFileRequest message
		ans = conn->communicate(cMsg, 2);
		// Check answer
		if(ans.answer_status() == VOK && resMsg.ParseFromString(ans.worker_answer())) {
			return resMsg.value() == "ok";
		} else if(ans.answer_status() == NO_USER_FOUND_ERROR) {
            LOG(ERROR) << "Cannot find user in database.";
        } else {
            LOG(ERROR) << "Cannot send client storage info.";
        }
    } else {
        LOG(ERROR) << "Cannot select connection for storage test file creation";
    }
    return false;
}

bool LocalStorageManager::createStorageTestFile(int storageId, std::string& relativePath, std::string& text)
{
    ClusterMsg cMsg;
    CreateStorageTestFileRequest reqMsg;
    CreateStorageTestFileResponse resMsg;
    Answer ans;

    MessageBuilder builder;
    boost::shared_ptr<CommunicationHandler> conn;

    conn = VeilFS::getConnectionPool()->selectConnection();
    if(conn) {
        // Build CreateStorageTestFileRequest message
        reqMsg.set_storage_id(storageId);
        ClusterMsg cMsg = builder.packFuseMessage(CreateStorageTestFileRequest::descriptor()->name(), CreateStorageTestFileResponse::descriptor()->name(), FUSE_MESSAGES, reqMsg.SerializeAsString());
        // Send CreateStorageTestFileRequest message
        ans = conn->communicate(cMsg, 2);
    	// Check answer
        if(ans.answer_status() == VOK && resMsg.ParseFromString(ans.worker_answer())) {
            relativePath = resMsg.relative_path();
            text = resMsg.text();
			return resMsg.answer();
		} else if(ans.answer_status() == NO_USER_FOUND_ERROR) {
            LOG(ERROR) << "Cannot find user in database.";
        } else {
            LOG(ERROR) << "Cannot create test file for storage with id: " << storageId;
        }
    } else {
        LOG(ERROR) << "Cannot select connection for storage test file creation";
    }
    return false;
}

bool LocalStorageManager::hasClientStorageReadPermission(std::string absolutePath, std::string relativePath, std::string expectedText)
{
    int fd = open((absolutePath + "/" + relativePath).c_str(), O_RDONLY | O_FSYNC);
    if(fd == -1) {
        return false;
    }
    fsync(fd);
    void* buf = malloc(sizeof(char) * expectedText.size());
    if(read(fd, buf, expectedText.size()) != (int) expectedText.size()) {
        free(buf);
        close(fd);
        return false;
    }
    std::string actualText((char *) buf, expectedText.size());
    free(buf);
    close(fd);
    return expectedText == actualText;
}

bool LocalStorageManager::hasClientStorageWritePermission(int storageId, std::string absolutePath, std::string relativePath)
{
    int fd = open((absolutePath + "/" + relativePath).c_str(), O_WRONLY | O_FSYNC);
    if(fd == -1) {
        return false;
    }
    int length = 20;
    std::string text(length, ' ');
    srand(time(0));
    for(int i = 0; i < length; ++i) {
        text[i] = (char) (33 + rand() % 93);
    }
    if(write(fd, text.c_str(), length) != length) {
        close(fd);
        return false;
    }
    close(fd);

    ClusterMsg cMsg;
    StorageTestFileModifiedRequest reqMsg;
    StorageTestFileModifiedResponse resMsg;
    Answer ans;

    MessageBuilder builder;
    boost::shared_ptr<CommunicationHandler> conn;

    conn = VeilFS::getConnectionPool()->selectConnection();
    if(conn) {
        // Build CreateStorageTestFileRequest message
        reqMsg.set_storage_id(storageId);
        reqMsg.set_relative_path(relativePath);
        reqMsg.set_text(text);
        ClusterMsg cMsg = builder.packFuseMessage(StorageTestFileModifiedRequest::descriptor()->name(), StorageTestFileModifiedResponse::descriptor()->name(), FUSE_MESSAGES, reqMsg.SerializeAsString());
        // Send CreateStorageTestFileRequest message
        ans = conn->communicate(cMsg, 2);
    	// Check answer
        if(ans.answer_status() == VOK && resMsg.ParseFromString(ans.worker_answer())) {
            return resMsg.answer();
    	} else if(ans.answer_status() == NO_USER_FOUND_ERROR) {
            LOG(ERROR) << "Cannot find user in database.";
        } else {
            LOG(ERROR) << "Cannot check client write permission for storage with id: " << storageId;
        }
    } else {
        LOG(ERROR) << "Cannot select connection for storage test file creation";
    }
    return false;
}

} // namespace client
} // namespace veil
<|endoftext|>