/*---------------------------------------------------------------------------*/
/* system.cpp                                                                */
/* copyright (c) innovaphone 2017                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include "platform/platform.h"
#include "common/interface/system.h"
#include "common/ilib/str.h"
#include <sys/utsname.h>
#ifdef __linux__
#include <sys/capability.h>
#endif
#include <unistd.h>
#include <pwd.h>

#define FILE_LABEL                  "/mnt/sda1/label"
#define FILE_LABEL_ROOTFS           "/mnt/sda1/rootfs.version"
#define FILE_LABEL_DOCKER           "/home/root/label"
#define FILE_LABEL_ROOTFS_DOCKER    "/home/root/rootfs.version"
#define FILE_RELEASE_STATE          "/mnt/sda1/releasestate"
#define FILE_RELEASE_STATE_DOCKER   "/home/root/releasestate"

void ISystem::GetArchitecture(char * buffer, size_t size)
{
    if (buffer && size) {
        struct utsname u;
        buffer[0] = 0;
        if (uname(&u) == 0) {
            str::to_str(u.machine, buffer, size);
        }
    }
}

architecture_t ISystem::GetArchitecture()
{
    struct utsname u;
    if (uname(&u) == 0) {
        if (str::n_casecmp(u.machine, "arm64", 5) == 0 || str::n_casecmp(u.machine, "aarch64", 7) == 0) return ARM64;
        else if (str::n_casecmp(u.machine, "arm", 3) == 0) return ARM;
    }
    return X86_64;
}

const char * ISystem::GetArchitectureName()
{
    switch (ISystem::GetArchitecture())
    {
    case ARM: return "arm";
    case X86_64: return "x86_64";
    case ARM64: return "arm64";
    }
    return nullptr; // can never happen
}

bool ISystem::GetHostname(char * buffer, size_t len)
{
    return (gethostname(buffer, len) == 0);
}

bool ISystem::GetUsername(char * buffer, size_t len)
{
    const passwd *pw = getpwuid(geteuid());
    if (!pw) return false;
    str::to_str(pw->pw_name, buffer, len);
    return true;
}

bool ISystem::GetClientname(char * buffer, size_t len)
{
    return false;
}

bool ISystem::GetSessionname(char * buffer, size_t len)
{
    return false;
}

bool ISystem::GetOperatingSystem(char * buffer, size_t len)
{
    // int uname(struct utsname *buf);
    return false;
}

bool ISystem::IsVirtualMachine()
{
    return ISystem::GetArchitecture() == architecture_t::X86_64;
}

void ISystem::Sync()
{
    sync();
}

const char * ISystem::Label()
{
    static char label[12] = { '\0' };
    if (!*label) {
        FILE * fp = fopen(FILE_LABEL, "r");
        if (fp) {
            fgets(label, sizeof(label), fp);
            fclose(fp);
        }
        else {
            FILE * fp = fopen(FILE_LABEL_DOCKER, "r");
            if (fp) {
                fgets(label, sizeof(label), fp);
                fclose(fp);
            }
            else {
                sprintf(label, "0");
            }
        }
    }
    return label;
}

const char * ISystem::LabelRootfs()
{
    static char label[12] = { '\0' };
    static bool checked = false;
    if (!checked) {
        checked = true;
        FILE * fp = fopen(FILE_LABEL_ROOTFS, "r");
        if (fp) {
            fgets(label, sizeof(label), fp);
            fclose(fp);
        }
        else {
            FILE * fp = fopen(FILE_LABEL_ROOTFS_DOCKER, "r");
            if (fp) {
                fgets(label, sizeof(label), fp);
                fclose(fp);
            }
            else {
                *label = '\0';
            }
        }
    }
    return label;
}

const char * ISystem::ReleaseState()
{
    static char releaseState[128] = { '\0' };
    static bool checked = false;
    if (!checked) {
        checked = true;
        // the "normal" app platform has no release states, but the docker container has one
        FILE * fp = fopen(FILE_RELEASE_STATE_DOCKER, "r");
        if (fp) {
            fgets(releaseState, sizeof(releaseState), fp);
            fclose(fp);
        }
        else {
            *releaseState = '\0';
        }
    }
    return releaseState;
}

bool ISystem::SetBindServiceCapability(const char * absolutePath)
{                                                      
#if (defined(__linux__) && !defined(__ANDROID__) && !defined(_IOS_))
	cap_t cap = cap_init();
	cap_value_t caps[1];
	caps[0] = CAP_NET_BIND_SERVICE;

	cap_set_flag(cap, CAP_PERMITTED, 1, &caps[0], CAP_SET);
	cap_set_flag(cap, CAP_EFFECTIVE, 1, &caps[0], CAP_SET);
    int status = cap_set_file(absolutePath, cap);
	cap_free(cap);
	
	return (status == 0);
#else
    return false;
#endif
}