[omniORB] Making omniNames a service on Windows NT

Jan Lessner jan@c-lab.de
Mon, 07 Sep 1998 18:15:57 +0200


This is a multi-part message in MIME format.

--------------5D9E120A2824
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Hi Folks
It took me quite a while to find an easy way for making omniNames a
"real" Windows service, which you can register in the Control Panel and
keep running beyond login sessions. I could image that this is of
interest for others here too, so I attached the modified omniNames.cc to
this email.
As I didn't find a way to register services on Windows including
particular command line options, the modified version *by default* tries
to connect to the service control manager and complains if it can't. For
the old standard initialization, omniNames must be started with option
-appl, meaning it should behave like an ordinary application rather than
a service. In "service mode" any standard error output is redirected to
a log file for error analysis. For UNIX this redirection is the only
difference between normal and service mode.

Would be a nice extension for omniORB 2.6 I think (after a little code
clean-up, of course).

Regards,

	Jan Lessner, C-LAB

--------------5D9E120A2824
Content-Type: text/plain; charset=us-ascii; name="omniNames.cc"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="omniNames.cc"

// -*- Mode: C++; -*-
//                          Package   : omniNames
// omniNames.cc             Author    : Tristan Richardson (tjr)
//
//    Copyright (C) 1997 Olivetti & Oracle Research Laboratory
//
//  This file is part of omniNames.
//
//  omniNames 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 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
//  USA.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream.h>
#include <omnithread.h>
#include <NamingContext_i.h>


// Minimum idle period before we take a checkpoint (15 mins)
#define DEFAULT_IDLE_TIME_BTW_CHKPT  (15*60)


void
usage()
{
  cerr << "\nusage: omniNames [-start <port>] [<omniORB2-options>...]" << endl;
  cerr << "\nUse -start option to start omniNames for the first time."
       << endl;
  cerr << "\nYou can set the environment variable " << LOGDIR_ENV_VAR
       << " to specify the\ndirectory where the log files are kept.\n"
       << endl;
  exit(1);
}


static void
removeArgs(int& argc, char** argv, int idx, int nargs)
{
  if ((idx+nargs) > argc) return;
  for (int i = idx+nargs; i < argc; i++) {
    argv[i-nargs] = argv[i];
  }
  argc -= nargs;
}


static void
insertArgs(int& argc, char**& argv, int idx, int nargs)
{
  char** newArgv = new char*[argc+nargs];
  int i;
  for (i = 0; i < idx; i++) {
    newArgv[i] = argv[i];
  }
  for (i = idx; i < argc; i++) {
    newArgv[i+nargs] = argv[i];
  }
  argv = newArgv;
  argc += nargs;
}

//
// main loop
//
static log *serverlog = NULL;
static CORBA::BOA_ptr boa;

void mainloop()
{
  boa->impl_is_ready(0,1);

  //
  // Now this thread has nothing much to do.  Simply take a checkpoint once
  // every so often.
  //

  int idle_time_btw_chkpt;
  char *itbc = getenv("OMNINAMES_ITBC");
  if (itbc == NULL || sscanf(itbc,"%d",&idle_time_btw_chkpt) != 1)
    idle_time_btw_chkpt = DEFAULT_IDLE_TIME_BTW_CHKPT;

  omni_mutex m;
  omni_condition c(&m);

  m.lock();
  while (1) {
    serverlog->checkpoint();
    unsigned long s, n;
    omni_thread::get_time(&s, &n, idle_time_btw_chkpt);
    c.timedwait(s,n);
  }
  m.unlock();
}


#include <time.h>
#include <stdarg.h>
/* Convenience function for standard error output preceeded
 * by the current date and time. This is especially of interest
 * for Windows service applications
 */
void syslog(char *fmt, ...)
{
  va_list ap;
  va_start(ap, fmt);
  char buf[1024];
  time_t t;
  time(&t);
  vsprintf(buf, fmt, ap);
  cerr << ctime(&t) << buf << flush;
  va_end(ap);	
}

#ifdef __WIN32__
//
// Windows service management stuff
//
static SERVICE_STATUS ssStatus;
static SERVICE_STATUS_HANDLE sshStatusHandle;
static BOOL was_stopped = FALSE;

/*
 * Service control function, accepting only STOP messages
 */
void service_ctrl(DWORD dwCtrlCode)
{
	if (dwCtrlCode == SERVICE_CONTROL_STOP) {
      ssStatus.dwCurrentState = SERVICE_STOPPED;
	  was_stopped = TRUE;
    }
	else {
	  // ignore any other control message but STOP
      ssStatus.dwCurrentState = SERVICE_RUNNING;
	}
    ssStatus.dwWin32ExitCode = NO_ERROR;
    if (!SetServiceStatus(sshStatusHandle, &ssStatus)) {
		syslog("SetServiceStatus failed: %d\n", GetLastError());
    }
}

void service_main(int argc, char *argv[])
{
  /* register our service control handler */
  if (!(sshStatusHandle = RegisterServiceCtrlHandler
	("omniNames", (LPHANDLER_FUNCTION)service_ctrl))) {
    syslog("RegisterServiceCtrlHandler failed: %d\n", GetLastError());
    return;
  }

  /* report running status to Service Control Manager */
  ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  ssStatus.dwCurrentState = SERVICE_RUNNING;
  ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  ssStatus.dwWin32ExitCode = NO_ERROR;
  ssStatus.dwServiceSpecificExitCode = 0;
  ssStatus.dwCheckPoint = 1;
  ssStatus.dwWaitHint = 5000;
  if (!SetServiceStatus(sshStatusHandle, &ssStatus)) {
    syslog("SetServiceStatus failed: %d\n", GetLastError());
    ssStatus.dwCurrentState = SERVICE_STOPPED;
    SetServiceStatus(sshStatusHandle, &ssStatus);
    return;
  }

  /* enter the main loop */
  mainloop();
}
#endif

//
// main
//
int main(int argc, char **argv)
{
  //
  // If we have a "-start" option, get the given port number.
  //

  int port = 0;
  unsigned char service_init = 1;

  if (argc > 1) {
    if (strcmp(argv[1], "-start") == 0) {
      if (argc < 3) usage();
      port = atoi(argv[2]);
      removeArgs(argc, argv, 1, 2);
    } else if (strcmp(argv[1], "-appl") == 0) {
	  service_init = 0;
      removeArgs(argc, argv, 1, 1);
    } else if ((strncmp(argv[1], "-BOA", 4) != 0) &&
	       (strncmp(argv[1], "-ORB", 4) != 0)) {
      usage();
    }
  }

  /* In case omniNames runs as a real service application, redirect
   * any standard error output to a log file.
   */
  if (service_init) {
	cerr = *new ofstream("omniNames.log");
	syslog("Name service initializing\n");
  }

  //
  // Set up an instance of class log.  This also gives us back the port
  // number from the log file if "-start" wasn't specified.
  //

  log l(port);
  serverlog = &l;

  //
  // Add a fake command line option to tell the BOA which port to use.
  //

  insertArgs(argc, argv, 1, 2);
  argv[1] = strdup("-BOAiiop_port");
  argv[2] = new char[16];
  sprintf(argv[2], "%d", port);


  //
  // Initialize the ORB and the object adaptor.
  //

  CORBA::ORB_ptr orb = CORBA::ORB_init(argc,argv,"omniORB2");
  boa = orb->BOA_init(argc,argv,"omniORB2_BOA");

  //
  // Read the log file and set up all the naming contexts described in it.
  //

  l.init(orb, boa);

#ifdef __WIN32__
  if (service_init) {
    /* register at the service control manager */
    SERVICE_TABLE_ENTRY   DispatchTable[] = 
    {
        { "omniNames", (LPSERVICE_MAIN_FUNCTION)service_main }, 
        { NULL,        NULL }
    };
    if (!StartServiceCtrlDispatcher(DispatchTable)) 
    {
		if (was_stopped) {
			syslog("Name service stopped\n");
		}
		else {
			syslog("StartServiceCtrlDispatcher failed: %d\n", GetLastError());
		}
    }
  }
  else
#endif
  mainloop();

  return 0;
}

--------------5D9E120A2824--