/*****************************************************************************\
*                                                                             *
*  Name   : service_watch                                                     *
*  Author : Chris Koeritz                                                     *
*                                                                             *
*******************************************************************************
* Copyright (c) 2004-$now By Author.  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; either version 2 of   *
* the License or (at your option) any later version.  This is online at:      *
*     http://www.fsf.org/copyleft/gpl.html                                    *
* Please send any updates to: fred@gruntose.com                               *
\*****************************************************************************/

#include <basis/common_templates.h>
#include <basis/istring.h>
#include <opsystem/console_logger.h>
#include <opsystem/application_shell.h>
#include <opsystem/filename.h>
#include <opsystem/startup_code.h>
#include <processes/process_control.h>
#include <processes/process_entry.h>
#include <service_ext/service_control.h>

#undef LOG
#define LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger(), t)

class service_watch : public application_shell
{
public:
  service_watch() : application_shell(class_name()) {}

  virtual int execute();

  IMPLEMENT_CLASS_NAME("service_watch");

  int instruct();  // print the instructions.
};

////////////////////////////////////////////////////////////////////////////

int service_watch::instruct()
{
  log(filename(__argv[0]).rootname() + " Usage:");
  log("\
This program watches a process to see if it is still running.  If it finds\n\
that the process has exited, then it will restart a list of services that\n\
are associated with it.  To do this, the program needs an application name\n\
as the first parameter and one or more service names as the second and\n\
further parameters.  The application is the file that is actually seen\n\
running in the process list.  The service names must be the internal names\n\
of the service as listed in the windows services control panel.\n\
Example:\n\
\tservice_watch i_launcher.exe app_launcher mooptersvc\n");
  return 1;
}

int service_watch::execute()
{
  FUNCDEF("execute");
  SET_DEFAULT_CONSOLE_LOGGER;
  if (__argc < 3) return instruct();

  istring filename(__argv[1]);
  filename.to_lower();

  string_array service_names;

  for (int i = 2; i < __argc; i++) {
    istring service_name(__argv[i]);
    service_name.to_lower();
    service_names += service_name;
  }

  service_control ctl;
  process_control proc;
  process_entry_array proc_list;
  int_set pids;

  while (true) {
    portable::sleep_ms(5 * SECOND_ms);  // pause a bit before we do the check.

    if (!proc.query_processes(proc_list)) {
      // we don't know why that would fail, but let's assume we'll try again.
      LOG("failed to query process list?!");
      continue;
    }
    if (!proc.find_process_in_list(proc_list, filename, pids)) {
      // uh-oh, our main purpose in life has occurred.  we need to restart
      // this process.
      LOG(istring("Could not find the ") + filename + " process.");
      for (int i = 0; i < service_names.length(); i++) {
        istring service_name = service_names[i];
        LOG(istring("Now restarting the ") + service_name + " service.");
        outcome ret = ctl.control_service(service_name, true, false);
        if (ret != service_control::OKAY) {
          LOG(istring("A failure occurred when restarting the ")
              + service_name + " service.");
        }
      }
    }
  }
  return 0;
}

////////////////////////////////////////////////////////////////////////////

HOOPLE_MAIN(service_watch, )

