/****************************************************************************
 * apps/system/resmonitor/fillcpu.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you 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.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define PATH    "/proc"
#define CPULOAD "cpuload"
#define LOADAVG "loadavg"

/****************************************************************************
 * Private Data
 ****************************************************************************/

static int go = 1;

/****************************************************************************
 * Private Functions
 ****************************************************************************/

static void handler(int sig)
{
  go = 0;
}

static void show_usages(void)
{
  syslog(LOG_WARNING,
        "Usage: CMD [-c <cpu>] [-f <filepath>] [-a]\n"
        "\t\t-c: set cpu occupation that you except, default 80\n"
        "\t\t-f: set write payload path, eg. /tmp/payload, program will "
        "write to memory if -f not set\n"
        "\t\t-a: the cpu (set by -c) is the cpu occupied by this program\n");
  exit(1);
}

static float get_cpu(int pid)
{
  float cpu = 0;
  char filepath[20];
  int ret;
  if (pid <= 0)
    {
      ret = snprintf(filepath, 20, "%s/%s", PATH, CPULOAD);
    }
  else
    {
      ret = snprintf(filepath, 20, "%s/%d/%s", PATH, pid, LOADAVG);
    }

  if (ret < 0)
    {
      /* syslog(LOG_ERR, "snprintf error\n"); */

      return cpu;
    }

  FILE *fp = fopen(filepath, "r");
  if (!fp)
    {
      return cpu;
    }

  char buf[8];
  fgets(buf, 8, fp);
  sscanf(buf, "%f", &cpu);
  fclose(fp);
  return cpu;
}

static int writefile(char *filepath, char *buffer1, char *buffer2)
{
  if (strlen(filepath) == 0)
    {
      memset(buffer2, '*', 1024);
      memcpy(buffer1, buffer2, 1024);
      return 0;
    }
  else
    {
      int fd;
      memset(buffer1, '*', 1024);
      if ((fd = open(filepath, O_WRONLY | O_CREAT, 0700)) <= 0)
        {
          syslog(LOG_ERR, "open file error\n");
          return -1;
        }

      if (write(fd, buffer1, 1024) <= 0)
        {
          syslog(LOG_ERR, "write file error\n");
          close(fd);
          return -1;
        }

      return close(fd);
    }
}

int main(int argc, char *argv[])
{
  char buf1[1024];
  char buf2[1024];
  char filepath[40];
  struct timeval sleeptime;
  int n        = 0;
  int lowcount = 0;
  int cpu      = 80;
  bool ispid   = false;
  int time     = 10000;
  float fcpu;
  int o;

  memset(filepath, 0, 40);
  go       = 1;
  if (argc == 1)
    {
      show_usages();
    }

  while ((o = getopt(argc, argv, "c:f:a")) != EOF)
    {
      switch (o)
        {
          case 'c':
            cpu = atoi(optarg);
            break;
          case 'f':
            snprintf(filepath, 40, "%s", optarg);
            break;
          case 'a':
            ispid = true;
            break;
          default:
            show_usages();
            break;
        }
    }

  signal(SIGINT, handler);
  signal(SIGKILL, handler);

  while (go)
    {
      if (time < 1000)
        {
          time = 1000;
          lowcount++;
          if (lowcount > 4)
            {
              lowcount = 0;
              n += 2;
            }
        }

      else if (time > 10000)
        {
          time = 10000;
          n -= 1;
          n = (n < 0 ? 0 : n);
        }
      else
        {
          lowcount = 0;
        }

      sleeptime.tv_sec  = 0;
      sleeptime.tv_usec = time;
      select(0, NULL, NULL, NULL, &sleeptime);
      if (ispid)
        {
          fcpu = get_cpu(getpid());
        }
      else
        {
          fcpu = get_cpu(0);
        }

      if (fcpu > cpu)
        {
          time += 1000;
        }
      else
        {
          time -= 1000;
        }

      for (int i = 0; i < n; i++)
        {
          if (writefile(filepath, buf1, buf2) != 0)
            {
              break;
            }
        }
    }

  syslog(LOG_INFO, "program complete!\n");
  return 0;
}