/*
 * Copyright (C) 2008 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.
 */

#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>

#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <dirent.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#define LOG_TAG "PppController"
#include <cutils/log.h>

#include "PppController.h"

PppController::PppController() {
    mTtys = new TtyCollection();
    mPid = 0;
}

PppController::~PppController() {
    TtyCollection::iterator it;

    for (it = mTtys->begin(); it != mTtys->end(); ++it) {
        free(*it);
    }
    mTtys->clear();
}

int PppController::attachPppd(const char *tty, struct in_addr local,
                              struct in_addr remote, struct in_addr dns1,
                              struct in_addr dns2) {
    pid_t pid;

    if (mPid) {
        ALOGE("Multiple PPPD instances not currently supported");
        errno = EBUSY;
        return -1;
    }

    TtyCollection::iterator it;
    for (it = mTtys->begin(); it != mTtys->end(); ++it) {
        if (!strcmp(tty, *it)) {
            break;
        }
    }
    if (it == mTtys->end()) {
        ALOGE("Invalid tty '%s' specified", tty);
        errno = -EINVAL;
        return -1;
    }

    if ((pid = fork()) < 0) {
        ALOGE("fork failed (%s)", strerror(errno));
        return -1;
    }

    if (!pid) {
        char *l = strdup(inet_ntoa(local));
        char *r = strdup(inet_ntoa(remote));
        char *d1 = strdup(inet_ntoa(dns1));
        char *d2 = strdup(inet_ntoa(dns2));
        char dev[32];
        char *lr;

        asprintf(&lr, "%s:%s", l, r);
        free(l);
        free(r);

        snprintf(dev, sizeof(dev), "/dev/%s", tty);

        // TODO: Deal with pppd bailing out after 99999 seconds of being started
        // but not getting a connection
        if (execl("/system/bin/pppd", "/system/bin/pppd", "-detach", dev, "115200",
                  lr, "ms-dns", d1, "ms-dns", d2, "lcp-max-configure", "99999", (char *) NULL)) {
            ALOGE("execl failed (%s)", strerror(errno));
        }
        free(lr);
        free(d1);
        free(d2);
        ALOGE("Should never get here!");
        return 0;
    } else {
        mPid = pid;
    }
    return 0;
}

int PppController::detachPppd(const char *tty) {

    if (mPid == 0) {
        ALOGE("PPPD already stopped");
        return 0;
    }

    ALOGD("Stopping PPPD services on port %s", tty);
    kill(mPid, SIGTERM);
    waitpid(mPid, NULL, 0);
    mPid = 0;
    ALOGD("PPPD services on port %s stopped", tty);
    return 0;
}

TtyCollection *PppController::getTtyList() {
    updateTtyList();
    return mTtys;
}

int PppController::updateTtyList() {
    TtyCollection::iterator it;

    for (it = mTtys->begin(); it != mTtys->end(); ++it) {
        free(*it);
    }
    mTtys->clear();

    DIR *d = opendir("/sys/class/tty");
    if (!d) {
        ALOGE("Error opening /sys/class/tty (%s)", strerror(errno));
        return -1;
    }

    struct dirent *de;
    while ((de = readdir(d))) {
        if (de->d_name[0] == '.')
            continue;
        if ((!strncmp(de->d_name, "tty", 3)) && (strlen(de->d_name) > 3)) {
            mTtys->push_back(strdup(de->d_name));
        }
    }
    closedir(d);
    return 0;
}