/*
 * Copyright (C) Tildeslash Ltd. All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3.
 *
 * This program 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 more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations
 * including the two.
 *
 * You must obey the GNU Affero General Public License in all respects
 * for all of the code used other than OpenSSL.  
 */

/**
 *  System independent /proc & kvm methods.
 *
 *  @file
 */

#include "config.h"

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

#ifdef HAVE_STRING_H
#include <string.h>
#endif

#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

#ifdef HAVE_LOADAVG_H
#include <sys/loadavg.h>
#endif

#ifdef HAVE_STRING_H
#include <string.h>
#endif

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif

#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif

#include "monit.h"
#include "process.h"
#include "process_sysdep.h"


/**
 * Reads an process dependent entry or the proc filesystem
 * @param buf buffer to write to
 * @param buf_size size of buffer "buf"
 * @param name name of proc service
 * @param pid number of the process / or <0 if main directory
 * @param bytes_read number of bytes read to buffer
 * @return TRUE if succeeded otherwise FALSE.
 */
int read_proc_file(char *buf, int buf_size, char *name, int pid, int *bytes_read) {
        int fd;
        char filename[STRLEN];
        int bytes;
        int rv = FALSE;

        ASSERT(buf);
        ASSERT(name);

        if (pid < 0)
                snprintf(filename, STRLEN, "/proc/%s", name);
        else
                snprintf(filename, STRLEN, "/proc/%d/%s", pid, name);

        if ((fd = open(filename, O_RDONLY)) < 0) {
                DEBUG("%s: Cannot open proc file %s -- %s\n", prog, filename, STRERROR);
                return rv;
        }

        if ((bytes = (int)read(fd, buf, buf_size-1)) < 0) {
                DEBUG("%s: Cannot read proc file %s -- %s\n", prog, filename, STRERROR);
                goto error;
        }
        if (bytes_read)
                *bytes_read = bytes;

        /* In case it is a string we have to 0 terminate it our self */
        buf[bytes]='\0';
        rv = TRUE;

error:
        if (close(fd) < 0)
                LogError("%s: Socket close failed -- %s\n", prog, STRERROR);

        return rv;
}

/**
 * Get the actual time as a floating point number
 * @return time in seconds
 */
double get_float_time(void) {    
        struct timeval t;

        gettimeofday(&t, NULL);
        return (double) t.tv_sec * 10 + (double) t.tv_usec / 100000.0;
}


/**
 * Connects child and parent in a process tree
 * @param pt process tree
 * @param parent index
 * @param child index
 * @return TRUE if succeeded otherwise FALSE.
 */
int connectchild(ProcessTree_T *pt, int parent, int child) {

        ASSERT(pt);

        if (pt[parent].pid == pt[child].pid)
                return FALSE;
        RESIZE(pt[parent].children, sizeof(ProcessTree_T *) * (pt[parent].children_num + 1));
        pt[parent].children[pt[parent].children_num] = child;
        pt[parent].children_num++;

        return TRUE;
}


/**
 * Fill data in the process tree by recusively walking through it
 * @param pt process tree
 * @param i process index
 * @return TRUE if succeeded otherwise FALSE.
 */
void fillprocesstree(ProcessTree_T *pt, int index) {
        int            i;
        ProcessTree_T *parent_pt;

        ASSERT(pt);

        if (pt[index].visited == 1)
                return;

        pt[index].visited         = 1;
        pt[index].children_sum    = pt[index].children_num;
        pt[index].mem_kbyte_sum   = pt[index].mem_kbyte;
        pt[index].cpu_percent_sum = pt[index].cpu_percent;

        for (i = 0; i < pt[index].children_num; i++)
                fillprocesstree(pt, pt[index].children[i]);

        if (pt[index].parent != -1 && pt[index].parent != index) {
                parent_pt                   = &pt[pt[index].parent];
                parent_pt->children_sum    += pt[index].children_sum;
                parent_pt->mem_kbyte_sum   += pt[index].mem_kbyte_sum;
                parent_pt->cpu_percent_sum += pt[index].cpu_percent_sum;
                parent_pt->cpu_percent_sum  = (pt[index].cpu_percent_sum > 1000) ? 1000 : parent_pt->cpu_percent_sum;
        } 
}

