// Copyright, 2001-2003, Astra Network Inc.  All Rights Reserved

// This source code has been published by Astra Network Inc. However, any
// use, reproduction, modification, distribution or transfer of this
// software, or any software which includes or is based upon any of this
// code, is only permitted if expressly authorized by a written license
// agreement from Astra. Contact your Astra representative directly for
// more information.

#include <olmod.h>
#include <sys/procfs.h>

#include <sys/stat.h>
#include <fcntl.h>
#include <sys/neutrino.h>

typedef struct cpu_data_s
{
  pid_t process;
  int64_t cpu_clock;		/* cpu-time(in nanoseconds) for the process */
  time_t time;			/* time cpu-time was checked */
  short global;
  clockid_t clockid;
}
cpu_data_t;



olmod_blackbox_t *
cpu_module_inst (pid_t process, olmod_arglist_t * static_args)
{
  cpu_data_t *blackbox;

  if (!(blackbox = (cpu_data_t *) malloc (sizeof (blackbox))))
    {
      printf ("mod_cpu: malloc failed\n");
      return NULL;
    }

  blackbox->cpu_clock = 0;
  blackbox->time = 0;

  if (process == 0)		/* Check overall cpu usage */
    {
      blackbox->process = 0;
      blackbox->global = 1;
      blackbox->clockid = ClockId (1, 1);	/* Idle is PID 1 and TID 1 */
    }
  else
    {
      blackbox->process = process;
      blackbox->global = 0;
    }

  return blackbox;
}

int64_t
cpu_module_get (olmod_blackbox_t * data, olmod_arglist_t * dynamic_args)
{
  cpu_data_t *blackbox = (cpu_data_t *) data;
  procfs_info info;
  int64_t current_cpu;
  time_t current_time;
  int cpu_percent;
  int fd;
  char filename[50];

  if (blackbox->global)
    {
      /* 6.0 docs say this is unsupported but this is how Igor's spin
       * does it. This is a little cleaner than extracting the info from
       * procfs.
       * FIXME: verify that this is supported from QSSL.
       */

      ClockTime (blackbox->clockid, NULL, &current_cpu);
    }
  else
    {				/* Find cpu usage for a process.
				 * Source: Igor's spin.c
				 * FIXME: Verify with QSSL
				 */

      /* I could probably use ClockTime for individual processes as
       * well, it looks cleaner. FIXME: look into this.
       */

      /* FIXME: How big can this go? */
      snprintf (filename, 50, "/proc/%d/as", blackbox->process);

      if ((fd = open (filename, O_RDONLY)) == -1)
	{
	  fprintf (stderr, "mod_cpu: open of %s failed\n", filename);
	  return 0;
	}

      devctl (fd, DCMD_PROC_INFO, &info, sizeof (info), 0);
      current_cpu = info.stime + info.utime;
      close (fd);
    }

  current_time = time (NULL);

  if (blackbox->time == 0)
    {
      blackbox->time = current_time;
      blackbox->cpu_clock = current_cpu;
      return 0;
    }

  cpu_percent = (current_cpu - blackbox->cpu_clock) / 100000 /
    (current_time - blackbox->time);
  blackbox->time = current_time;
  blackbox->cpu_clock = current_cpu;

  if (blackbox->global)
    return (10000 - cpu_percent);	/* System's usage is 100%-idle */
  else
    return cpu_percent;
}
