/*
 * Dynomite - A thin, distributed replication layer for multi non-distributed
 * storages. Copyright (C) 2014 Netflix, Inc.
 */

/*
 * twemproxy - A fast and lightweight proxy for memcached protocol.
 * Copyright (C) 2011 Twitter, Inc.
 *
 * 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 <signal.h>
#include <stdlib.h>

#include "dyn_core.h"
#include "dyn_signal.h"

static struct signal signals[] = {
    {SIGUSR1, "SIGUSR1", 0, signal_handler},
    {SIGUSR2, "SIGUSR2", 0, signal_handler},
    {SIGTTIN, "SIGTTIN", 0, signal_handler},
    {SIGTTOU, "SIGTTOU", 0, signal_handler},
    {SIGHUP, "SIGHUP", 0, signal_handler},
    {SIGINT, "SIGINT", 0, signal_handler},
    {SIGSEGV, "SIGSEGV", (int)SA_RESETHAND, signal_handler},
    {SIGPIPE, "SIGPIPE", 0, SIG_IGN},
    {0, NULL, 0, NULL}};

/**
 * Initialize the list of POSIX signals that dynomite can respond to and map
 * each signal to a handler function.
 * @return status
 */
rstatus_t signal_init(void) {
  struct signal *sig;

  for (sig = signals; sig->signo != 0; sig++) {
    rstatus_t status;
    struct sigaction sa;

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sig->handler;
    sa.sa_flags = sig->flags;
    sigemptyset(&sa.sa_mask);

    status = sigaction(sig->signo, &sa, NULL);
    if (status < 0) {
      log_error("sigaction(%s) failed: %s", sig->signame, strerror(errno));
      return DN_ERROR;
    }
  }

  return DN_OK;
}

void signal_deinit(void) {}

void signal_handler(int signo) {
  struct signal *sig;
  void (*action)(void);
  char *actionstr;
  bool done;

  for (sig = signals; sig->signo != 0; sig++) {
    if (sig->signo == signo) {
      break;
    }
  }
  ASSERT(sig->signo != 0);

  actionstr = "";
  action = NULL;
  done = false;

  switch (signo) {
    case SIGUSR1:
      break;

    case SIGUSR2:
      break;

    case SIGTTIN:
      actionstr = ", up logging level";
      action = log_level_up;
      break;

    case SIGTTOU:
      actionstr = ", down logging level";
      action = log_level_down;
      break;

    case SIGHUP:
      actionstr = ", reopening log file";
      action = log_reopen;
      break;

    case SIGINT:
      done = true;
      actionstr = ", exiting";
      break;

    case SIGSEGV:
      dn_stacktrace(1);
      actionstr = ", core dumping";
      raise(SIGSEGV);
      break;

    default:
      NOT_REACHED();
  }

  loga("signal %d (%s) received%s", signo, sig->signame, actionstr);

  if (action != NULL) {
    action();
  }

  if (done) {
    exit(1);
  }
}
