/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* this program is used to read a set of system properties and their values
 * from the emulator program and set them in the currently-running emulated
 * system. It does so by connecting to the 'boot-properties' qemud service.
 *
 * This program should be run as root and called from
 * /system/etc/init.goldfish.rc exclusively.
 */

#define LOG_TAG  "qemu-props"

#define DEBUG  1

#if DEBUG
#  include <log/log.h>
#  define  DD(...)    ALOGI(__VA_ARGS__)
#else
#  define  DD(...)    ((void)0)
#endif

#include <cutils/properties.h>
#include <unistd.h>
#include "qemud.h"

/* Name of the qemud service we want to connect to.
 */
#define  QEMUD_SERVICE  "boot-properties"

#define  MAX_TRIES      5

#define QEMU_MISC_PIPE "QemuMiscPipe"

int s_QemuMiscPipe = -1;
void static notifyHostBootComplete();

int  main(void)
{
    int  qemud_fd, count = 0;

    /* try to connect to the qemud service */
    {
        int  tries = MAX_TRIES;

        while (1) {
            qemud_fd = qemud_channel_open( "boot-properties" );
            if (qemud_fd >= 0)
                break;

            if (--tries <= 0) {
                DD("Could not connect after too many tries. Aborting");
                return 1;
            }

            DD("waiting 1s to wait for qemud.");
            sleep(1);
        }
    }

    DD("connected to '%s' qemud service.", QEMUD_SERVICE);

    /* send the 'list' command to the service */
    if (qemud_channel_send(qemud_fd, "list", -1) < 0) {
        DD("could not send command to '%s' service", QEMUD_SERVICE);
        return 1;
    }

    /* read each system property as a single line from the service,
     * until exhaustion.
     */
    for (;;)
    {
#define  BUFF_SIZE   (PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 2)
        DD("receiving..");
        char* q;
        char  temp[BUFF_SIZE];
        char  vendortemp[BUFF_SIZE];
        int   len = qemud_channel_recv(qemud_fd, temp, sizeof temp - 1);

        /* lone NUL-byte signals end of properties */
        if (len < 0 || len > BUFF_SIZE-1 || temp[0] == '\0')
            break;

        temp[len] = '\0';  /* zero-terminate string */

        DD("received: %.*s", len, temp);

        /* separate propery name from value */
        q = strchr(temp, '=');
        if (q == NULL) {
            DD("invalid format, ignored.");
            continue;
        }
        *q++ = '\0';

        char* final_prop_name = NULL;
        if (strcmp(temp, "qemu.sf.lcd.density") == 0 ) {
            final_prop_name = temp;
        } else if (strcmp(temp, "qemu.hw.mainkeys") == 0 ) {
            final_prop_name = temp;
        } else if (strcmp(temp, "qemu.cmdline") == 0 ) {
            final_prop_name = temp;
        } else if (strcmp(temp, "dalvik.vm.heapsize") == 0 ) {
            continue; /* cannot set it here */
        } else if (strcmp(temp, "ro.opengles.version") == 0 ) {
            continue; /* cannot set it here */
        } else {
            snprintf(vendortemp, sizeof(vendortemp), "vendor.%s", temp);
            final_prop_name = vendortemp;
        }
        if (property_set(temp, q) < 0) {
            ALOGW("could not set property '%s' to '%s'", final_prop_name, q);
        } else {
            ALOGI("successfully set property '%s' to '%s'", final_prop_name, q);
            count += 1;
        }
    }

    char temp[BUFF_SIZE];
    for (;;) {
        usleep(5000000); /* 5 seconds */
        property_get("vendor.qemu.dev.bootcomplete", temp, "");
        int is_boot_completed = (strncmp(temp, "1", 1) == 0) ? 1 : 0;
        if (is_boot_completed) {
            ALOGI("tell the host boot completed");
            notifyHostBootComplete();
            break;
        }
    }

    /* finally, close the channel and exit */
    if (s_QemuMiscPipe >= 0) {
        close(s_QemuMiscPipe);
        s_QemuMiscPipe = -1;
    }
    close(qemud_fd);
    DD("exiting (%d properties set).", count);
    return 0;
}

void notifyHostBootComplete() {
   if (s_QemuMiscPipe < 0) {
        s_QemuMiscPipe = qemu_pipe_open(QEMU_MISC_PIPE);
        if (s_QemuMiscPipe < 0) {
            ALOGE("failed to open %s", QEMU_MISC_PIPE);
            return;
        }
    }
    char set[] = "bootcomplete";
    int pipe_command_length = sizeof(set);
    WriteFully(s_QemuMiscPipe, &pipe_command_length, sizeof(pipe_command_length));
    WriteFully(s_QemuMiscPipe, set, pipe_command_length);
    ReadFully(s_QemuMiscPipe, &pipe_command_length, sizeof(pipe_command_length));
    if (pipe_command_length > (int)(sizeof(set)) || pipe_command_length <= 0)
        return;
    ReadFully(s_QemuMiscPipe, set, pipe_command_length);
}