/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc.  Used by permission.
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; version 2
 * of the License.
 *
 * 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 General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * END COPYRIGHT BLOCK **/
/*
 * Restart Admin Server
 *
 * The CGI supports two commands. The first one is 'restart' which enables
 * the client to make a restart request. Because the restart does not
 * take place immediately, the client can poll to detect the actual restart
 * by issuing the second command 'getpid' and comparing the pid with the one
 * returned by 'restart' op.
 *
 * Restart operation
 * in:  op=restart
 * out: pid=<Current admin server PID>
 *
 * Get PID  Operation
 * in:  op=getpid
 * out: pid=<Current admin server PID>
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <prtypes.h>

#include "libadminutil/admutil.h"
#include "libadminutil/distadm.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#ifdef XP_UNIX
#include <unistd.h>
#include "prthread.h"
#endif
#include "libadminutil/resource.h"

#include "libadmin/libadmin.h"

#define RESOURCE_FILE "restartsrv"

#define resource_key(a,b)   a b

#define DBT_NO_NEW_PROCESS 	resource_key(RESOURCE_FILE, "1")
#define DBT_RESTART_ACK 	resource_key(RESOURCE_FILE, "2")
#define DBT_CANT_STOP_SRV 	resource_key(RESOURCE_FILE, "3")


static char * get_admserv_pid();
static char * get_pid_file();

#if defined(XP_UNIX)

static void
restart(char const * line) {
  int i;
  int pid;

  PR_Sleep(5);
  
  setsid();
  
  for (i = 0; i < 5; i++) {
    pid = fork();
    switch (pid) {
    case -1:
      PR_Sleep(1);
      continue;
      
    case 0:
      break;
      
    default: 
      _exit (0);
    }
    break;
  }
  
  if (pid) {
    fprintf(stderr, "Environment would not permit fork to succeed.");
    _exit(0);
  }

  // if we're here then we're the child of the child and we've
  // lost our controlling terminal and are disassociated from our
  // process group which is exactly what we want.
  
  system(line); /* now call the script */
}

#endif

int main(int argc, char *argv[])
{
  int _ai=ADMUTIL_Init();
  char *method = getenv("REQUEST_METHOD");
  char error_info[128];
  char *qs = 0, *op=0;
#ifdef XP_UNIX
  char line[BIG_LINE];
#endif
  char msgbuf[BUFSIZ];

  char           *acceptLanguage = (char*)"en", *lang=getenv((char*)"HTTP_ACCEPT_LANGUAGE");
  Resource       *i18nResource = NULL;

  (void)_ai; /* get rid of unused variable warning */
  i18nResource = res_find_and_init_resource(PROPERTYDIR, RESOURCE_FILE);

  if (lang) acceptLanguage = strdup(lang);

	/* GET or POST method */
    if (!method || !*method) {
        /* non UI CGI */
        rpt_err(SYSTEM_ERROR, "No method is specified", NULL, NULL);
    }
    else if (!strcmp(method, "GET")) {
        /* UI CGI - either upgrade or normal admin CGI */
        qs = getenv("QUERY_STRING");
	
        if (!qs || !*qs) {
            rpt_err(INCORRECT_USAGE, "NO QUERY_STRING DATA", NULL, NULL);
        }
        else {
            get_begin(qs);
        }
    }
    else if (!strcmp(method, "POST")) {
        post_begin(stdin);
    }
    else {
        PR_snprintf(error_info, sizeof(error_info), "Unknown Method (%s)", method);
        rpt_err(INCORRECT_USAGE, error_info, NULL, NULL);
    }
	
    op=get_cgi_var("op", NULL, NULL);
	
    if (op == NULL) {
        rpt_err(INCORRECT_USAGE, "No operation defined", NULL, NULL);
    }
    else if (strcmp(op,"getpid")==0) {
        rpt_success(NULL);
        fprintf(stdout, "pid: %s\n", get_admserv_pid());
        return 0;
    }
    else if (strcmp(op,"restart")!=0) {		
        PR_snprintf(error_info, sizeof(error_info), "Unknown Operation (%s)", op);
        rpt_err(INCORRECT_USAGE, error_info, NULL, NULL);
    }

#ifdef XP_UNIX
  switch(fork()) {
  case -1:
    if (i18nResource) 
      rpt_err(SYSTEM_ERROR, 
	      res_getstring(i18nResource, 
			    DBT_NO_NEW_PROCESS, 
			    acceptLanguage,
			    msgbuf, sizeof(msgbuf), NULL),
	      NULL, NULL);
    else rpt_err(SYSTEM_ERROR,
		 "Couldn't create a new process to stop admin server",
		 NULL, NULL);
    break;
  case 0:
#if defined(WITH_SYSTEMD)
    PL_strncpyz(line, "/bin/systemctl restart " PACKAGE_NAME ".service", sizeof(line));
    restart(line);
#elif defined(ENABLE_SERVICE)
    PL_strncpyz(line, "service " PACKAGE_NAME " restart", sizeof(line));
    restart(line);
#else
    if (util_find_file_in_paths(line,  sizeof(line), "restart-ds-admin", CMDBINDIR, "../..", "")) {
        restart(line);
    }
#endif
    exit(0);
    break;
  default:
    /* comment out temporily, should be back later 
    log_change(TO_ADMIN, "Admin server restart on user request");
    */
    if (i18nResource) 
      rpt_unknown(res_getstring(i18nResource, 
				DBT_RESTART_ACK, 
				acceptLanguage,
				msgbuf, sizeof(msgbuf), NULL));
    else rpt_unknown("Admin server should restart on user request");
    break;
  }
 
  /* Return current server pid so that restart can be detected */
  fprintf(stdout, "pid: %s\n", get_admserv_pid());
  fflush(stdout);

  return 0;	
#else /* XP_WIN32 */
  
  if (i18nResource) 
    rpt_unknown(res_getstring(i18nResource, 
			      DBT_RESTART_ACK, 
			      acceptLanguage));
  else rpt_unknown("Admin server should restart on user request");

  /* Return current server pid so that restart can be detected */
  fprintf(stdout, "pid: %s\n", get_admserv_pid());

  { // bug 105053
	STARTUPINFO sui;
	PROCESS_INFORMATION pi;

	sui.cb               = sizeof(STARTUPINFO);
	sui.lpReserved       = 0;
	sui.lpDesktop        = NULL;
	sui.lpTitle          = NULL;
	sui.dwX              = 0;
	sui.dwY              = 0;
	sui.dwXSize          = 0;
	sui.dwYSize          = 0;
	sui.dwXCountChars    = 0;
	sui.dwYCountChars    = 0;
	sui.dwFillAttribute  = 0;
	sui.dwFlags          = STARTF_USESHOWWINDOW;
	sui.wShowWindow      = SW_HIDE;
	sui.cbReserved2      = 0;
	sui.lpReserved2      = 0;
	sui.hStdInput        = 0;
	sui.hStdOutput       = 0;
	sui.hStdError        = 0;

	if(CreateProcess(NULL, "NTRestartService.exe", NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP, NULL, NULL, &sui, &pi) > 0)
		return 0; // success
  }

  /* comment out temporily, should be back later 
     log_change(TO_ADMIN, "Admin server restart on user request");
  */

  return 1;	
#endif /* XP_WIN32 */
}

/*
 * Return the current admin server PID. Client need this to detect the actual
 * restart. For NT, return ADMSERV_PID environment variable. For Unix, get
 * pid from the log file, as ADMSERV_PID is 1 (parent has exited).
*/
#ifdef XP_UNIX
static char * get_admserv_pid() {
	char path[BIG_LINE];
	char *pidlog = (char*)get_pid_file();
	char inbuf[16];
	FILE *f;

	PR_snprintf(path, sizeof(path), "%s", pidlog);

	f = fopen(path, "r");

	if (f==NULL) {
		rpt_err(SYSTEM_ERROR, "Can not open pidlog file", NULL, NULL);
	}

	if(fgets(inbuf, sizeof(inbuf), f) == NULL) {
		rpt_err(SYSTEM_ERROR, "pidlog file is empty", NULL, NULL);
	}

	fclose(f);
	
	/* Remove trailing '\n' */
	if (inbuf[strlen(inbuf)-1] == '\n') {
		inbuf[strlen(inbuf)-1] = 0;
	}
	return strdup(inbuf);
}

#else /* WIN NT */
static char * get_admserv_pid() {
	return getenv("ADMSERV_PID");
}
#endif

static char * get_pid_file() {

  FILE *f;
  static char inbuf[BIG_LINE];
  static char filename[BIG_LINE];
  static char buf[BIG_LINE];
  char * p = NULL;
  const char *configdir = util_get_conf_dir();
  const char *piddir = util_get_pid_dir();

  util_find_file_in_paths(filename, sizeof(filename), "console.conf", configdir, "", "admin-serv/config");

  f = fopen(filename, "r");
  if (f==NULL) {
    rpt_err(SYSTEM_ERROR, "Can not open file console.conf for reading", NULL, NULL);
  }

  while(fgets(inbuf, sizeof(inbuf), f) != NULL) {
    if (strncasecmp(inbuf,"PidFile",7) == 0) {
      char *t;
      p = strtok(inbuf, " ");
      p = strdup(strtok(NULL, " "));
      t = strchr(p, '\n');
      if (*t) *t='\0';
    }
  }
  fclose(f);

  if (p && *p != '\0') {
    if (*p == '/') {
      PR_snprintf(buf, sizeof(buf),"%s",p);
    }
    else {
      util_find_file_in_paths(buf, sizeof(buf), p, piddir, "..", "admin-serv");
    }
    free(p);
    p = strdup(buf);
  }

  return p;
}
