root/src/add-ons/kernel/debugger/netconsole/netconsole.cpp
/*
 * Copyright 2008-2016, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Adrien Destugues, pulkomandy@pulkomandy.tk
 */


#include <debug.h>
#include <driver_settings.h>
#include <netinet/in.h>
#include <sys/socket.h>


#define NETCONSOLE_PORT 6666


static int gSocket;


static int
createSocket()
{
        gSocket = socket(AF_INET, SOCK_DGRAM, 0);
        if (gSocket < 0)
                return B_ERROR;

        // bind socket
        sockaddr_in fSocketAddress;
        fSocketAddress.sin_family = AF_INET;
        fSocketAddress.sin_port = 0;
        fSocketAddress.sin_addr.s_addr = INADDR_ANY;
        fSocketAddress.sin_len = sizeof(sockaddr_in);
        if (bind(gSocket, (sockaddr*)&fSocketAddress, sizeof(fSocketAddress)) < 0) {
                return B_ERROR;
        }

        // set SO_BROADCAST on socket
        int soBroadcastValue = 1;
        if (setsockopt(gSocket, SOL_SOCKET, SO_BROADCAST, &soBroadcastValue,
                        sizeof(soBroadcastValue)) < 0) {
                return B_ERROR;
        }

        return B_OK;
}


// FIXME this can't work this way, because debugger_puts is called with
// interrupts disabled and can't send to the network directly. Must be reworked
// to use a buffer, and do the network access from another thread. A similar
// solution is implemented to get syslog data to the syslog_daemon.
static int
debugger_puts(const char* message, int32 length)
{
        if (gSocket < 0)
                createSocket();

        if (gSocket >= 0) {
                // init server address to broadcast
                sockaddr_in fServerAddress;
                fServerAddress.sin_family = AF_INET;
                fServerAddress.sin_port = htons(NETCONSOLE_PORT);
                fServerAddress.sin_addr.s_addr = htonl(INADDR_BROADCAST);
                fServerAddress.sin_len = sizeof(sockaddr_in);

                return sendto(gSocket, message, length, 0,
                        (sockaddr*)&fServerAddress, sizeof(fServerAddress));
        }

        return 0;
}


static status_t
std_ops(int32 op, ...)
{
        void* handle;
        bool load = false;

        switch (op) {
                case B_MODULE_INIT:
                        gSocket = -1;
                        return B_OK;
#if 0
                        handle = load_driver_settings("kernel");
                        if (handle) {
                                load = get_driver_boolean_parameter(handle,
                                        "netconsole_debug_output", load, true);
                                unload_driver_settings(handle);
                        }
                        if (load)
                                gSocket = -1;
                        return load ? B_OK : B_ERROR;
#endif
                case B_MODULE_UNINIT:
                        if (gSocket >= 0)
                                close(gSocket);
                        return B_OK;
        }
        return B_BAD_VALUE;
}


static struct debugger_module_info sModuleInfo = {
        {
                "debugger/netconsole/v1",
                0,
                &std_ops
        },
        NULL,
        NULL,
        debugger_puts,
        NULL
};

module_info* modules[] = {
        (module_info*)&sModuleInfo,
        NULL
};