/*
 * Copyright (c) 1998-2001 Caucho Technology -- all rights reserved
 *
 * Caucho Technology permits redistribution, modification and use
 * of this file in source and binary form ("the Software") under the
 * Caucho Developer Source License ("the License").  The following
 * conditions must be met:
 *
 * 1. Each copy or derived work of the Software must preserve the copyright
 *    notice and this notice unmodified.
 *
 * 2. Redistributions of the Software in source or binary form must include 
 *    an unmodified copy of the License, normally in a plain ASCII text
 *
 * 3. The names "Resin" or "Caucho" are trademarks of Caucho Technology and
 *    may not be used to endorse products derived from this software.
 *    "Resin" or "Caucho" may not appear in the names of products derived
 *    from this software.
 *
 * 4. Caucho Technology requests that attribution be given to Resin
 *    in any manner possible.  We suggest using the "Resin Powered"
 *    button or creating a "powered by Resin(tm)" link to
 *    http://www.caucho.com for each page served by Resin.
 *
 * This Software is provided "AS IS," without a warranty of any kind. 
 * ALL EXPRESS OR IMPLIED REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
 *
 * CAUCHO TECHNOLOGY AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE OR ANY THIRD PARTY AS A RESULT OF USING OR
 * DISTRIBUTING SOFTWARE. IN NO EVENT WILL CAUCHO OR ITS LICENSORS BE LIABLE
 * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
 * INABILITY TO USE SOFTWARE, EVEN IF HE HAS BEEN ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGES.      
 *
 * @author Scott Ferguson
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/smp_lock.h>
#include <linux/smp.h>
#include <linux/in.h>

#include "hardcore.h"

int port = 80;
MODULE_PARM(port, "i");

resin_t g_resin;

static int g_is_dead = 0;
static int g_resind_pid = 0;

/* true when resind has a task waiting for execution */
static int g_has_task = 0;

static wait_queue_head_t resind_idle;
static wait_queue_head_t resind_killer;

/**
 * Wake the resind daemon.
 *
 * No locking is necessary even though this is a test-and-set because
 * resind will only clear g_has_task before it starts executing any tasks.
 */
void
resind_wake()
{
  if (! g_has_task) {
    g_has_task = 1;
    wake_up(&resind_idle);
  }
}

/*
 * This is the resin daemon
 */
static int
resind(void *ptr)
{
  int count = 0;

  cache_init();
  
  /*
   * Let our maker know we're running ...
   */
  kernel_lock();
  g_resind_pid = current->pid;

  exit_mm(current);

  spin_lock_irq(&current->sigmask_lock);
  siginitsetinv(&current->blocked, sigmask(SIGKILL));
  recalc_sigpending(current);
  spin_unlock_irq(&current->sigmask_lock);

  current->session = 1;
  current->pgrp = 1;
  strcpy(current->comm, "resind");

  LOG(("resind: start (pid %d)\n", g_resind_pid));
  
  browser_init(&g_resin);
  
  resin_proc_init(&g_resin);

  while (! g_is_dead) {
    if (signal_pending(current)) {
      LOG(("dead\n"));
      g_is_dead = 1;
      flush_signals(current);
      break;
    }

    /* Execute any connection activity. */
    browser_execute();

    /* Execute any srun activity (cleanup). */
    srun_execute(&g_resin);

    if (++count >= 64) {	/* safeguard */
      schedule();
      count = 0;
    }

    if (! g_has_task) {
      LOG(("resind: sleep %p\n", resind_idle));
      count = 0;
      wait_event_interruptible(resind_idle, g_has_task);
      LOG(("resind: wake\n"));
    }
    g_has_task = 0;
  }

  LOG(("resin: start shutdown\n"));

  g_resind_pid = 0;

  resin_proc_exit(&g_resin);

  browser_cleanup(&g_resin);

  LOG(("resin: finish shutdown\n"));

  wake_up(&resind_killer);
  
  return 0;
}

/**
 * Initialize the module
 */
int init_module()
{
  int error = 0;

  g_resin.tty = current->tty;

  LOG(("initializing %d\n", port));

  g_resin.accept_port = port;
  
  init_waitqueue_head(&resind_idle);
  init_waitqueue_head(&resind_killer);

  /*
   * Create the daemon thread
   */
  error = kernel_thread(resind, &resind_killer, 0);
  if (error < 0)
    LOG(("resind: create thread failed, error=%d\n", error));

  return 0;
}

/**
 * Cleanup the module on exit
 */
void
cleanup_module()
{
  g_resin.is_dead = 1;
  g_is_dead = 1;
  
  if (g_resind_pid <= 0)
    return;

  resind_wake();
  
  wait_event_interruptible(resind_killer, g_resind_pid <= 0);
  // kill_proc(g_resind_pid, SIGKILL, 1);
}
