TOPIC: Running Suneido as a Service
#328
Running Suneido as a Service 4 Years ago
Running Suneido as a NT Service
Category: Administration

Ingredients

A C Compiler

Problem

Running Suneido as server requires to open a session and start Suneido with -s parameter.
The fact of having an opened session, even blocked, annoys you Network Manager who's in turn, annoys you.
The operator forget starting the server leaving the business without service, and the fact annoys the
boss, who's in turn, annoys you :-(

Recipe

1st Preferred method
--------------------
Grab a shotgun and shoot Network manager and operator :-)
If applicable law or Companies policies doesn't allow you to shoot coworkers keep on reading. :-))

2nd Preferred method
--------------------
Get a good C language compiler (I use PellesC and found it awesome, visit www.smorgasbordet.com)
Compile the following source code, make needed changes to suit your compiler antics.

WARNING: C Code ahead not suneido code

/*--------------------------------------------------------------------------------
Program: Suneido Service
Author : Santiago Ottonell 08/2006 ( This e-mail address is being protected from spam bots, you need JavaScript enabled to view it )

License: This program is free for any legal use, but you must keep this header
and give proper credits, as follows.

Thanks :

- Unknown?: An Introduction to NT Services
- Unknown?: Controlling the Freelancer server with an NT service

(If you are the autor/s of these articles contact me to give you proper credit)

Code:

 --------------------------------------------------------------------------------*/

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include "stdio.h"
#include "string.h"
#include "winsvc.h"



#define SERVICE_NAME "SuneidoService"
#define SERVICE_NAME_LABEL "Suneido Server"



/*--------------------------------------------------------------------------------
 Prototypes
 --------------------------------------------------------------------------------*/
void CallServiceDispatcher(void);
void ProcessCommandLine(int argc, char *argv[]);
void ShowHelp();
void InstallService();
void UnInstallService();
void PrintError(LPTSTR , LPTSTR );
void ServiceMain(DWORD, LPTSTR*);
BOOL UpdateSCMStatus (DWORD , DWORD, DWORD ,DWORD ,DWORD);
int  KillService();
void ServiceCtrlHandler (DWORD);

/*--------------------------------------------------------------------------------
 Global Vars
 --------------------------------------------------------------------------------*/
DWORD serviceCurrentStatus;
SERVICE_STATUS_HANDLE serviceStatusHandle ;
char SuneidoPath[MAX_PATH];


/*--------------------------------------------------------------------------------
 Program Entry Point When called from command line
 --------------------------------------------------------------------------------*/

void main(int argc, char *argv[])
{
char * SuneidoExe;

//SuneidoService.exe must reside in the same folder od suneido.exe
GetFullPathName(argv[0],MAX_PATH,SuneidoPath,&SuneidoExe);
//Cut program name
SuneidoExe[0]=0;

  //Check parameters
if (argc == 1) CallServiceDispatcher();//See 
if (argc >  2) ShowHelp();

ProcessCommandLine(argc, argv);

return ;
}

/*--------------------------------------------------------------------------------
 Call Servvice Dispatcher
 When service dispatcher call SuneidoService.exe it uses no parameters.
 At this time we must register our ServiceMain function with it.
 If the registration fails, the most likely situation is because it was called
 without parameters from the command line, so it's a good place to call ShowHelp

 --------------------------------------------------------------------------------*/
void CallServiceDispatcher(void)
{

SERVICE_TABLE_ENTRY serviceTable[] =
  {
    {SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
   { NULL, NULL }
  };

  BOOL success;
  
// Register the service with the Service Control Manager
  success = StartServiceCtrlDispatcher(serviceTable);
  if (!success)
  {
ShowHelp();
exit(0);
  }
return;
}

/*--------------------------------------------------------------------------------
 ServiceMain:«»Service Entry Point
 --------------------------------------------------------------------------------*/
void ServiceMain(DWORD argc, LPTSTR *argv)
{

BOOL success;

  // First we must call the Registration function and register a EventHandler
// to handle Start and Stop Events
  serviceStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME,
                           (LPHANDLER_FUNCTION) ServiceCtrlHandler);
  if (!serviceStatusHandle)
  {
   PrintError("ServiceMain","RegisterServiceCtrlHandler"«»);
    return;
  }
  // Next Notify the Service Control Manager of progress
  success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 1, 5000);
  if (!success)
  {
   PrintError("ServiceMain","UpdateSCMStatus"«»);
    return;
  }
  // Notify the SCM of progress again
  success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 2, 1000);
  if (!success)
  {
   PrintError("ServiceMain","UpdateSCMStatus"«»);
    return;
  }
  // Start the service execution creating a process
STARTUPINFO si;
  PROCESS_INFORMATION pi;
// Standar good practice
  ZeroMemory( &si, sizeof(si) );
  si.cb = sizeof(si);
  ZeroMemory( &pi, sizeof(pi) );
//Build command line to start suneido server
char szCmdLine[MAX_PATH];
sprintf(szCmdLine,"%ssuneido.exe -n -u -s ",SuneidoPath  );
  // Start the child process. 
  if(!CreateProcess( NULL,   // No module name (use command line). 
       szCmdLine, // Command line. 
       NULL,             // Process handle not inheritable. 
       NULL,             // Thread handle not inheritable. 
       FALSE,            // Set handle inheritance to FALSE. 
       0,                // No creation flags. 
       NULL,             // Use parent's environment block. 
       SuneidoPath,      // Use database directory
       &si,              // Pointer to STARTUPINFO structure.
       &pi )             // Pointer to PROCESS_INFORMATION structure.
   ) 
   {
 PrintError("ServiceMain", "CreateProcess failed"«»);
     return;
   }
   // The service is now running.  Notify the SCM of this fact.
   serviceCurrentStatus = SERVICE_RUNNING;
   success = UpdateSCMStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0);
   if (!success)
   {
     PrintError("ServiceMain","Service Not Started"«»);
     return;
   }
   // Wait until child process exits.ServiceMain must not return until
 // Server has ended
   WaitForSingleObject( pi.hProcess, INFINITE );

   // When server ends, close process and thread handles. 
   CloseHandle( pi.hProcess );
   CloseHandle( pi.hThread );
 // The service is not running anymore.  Notify the SCM of this fact.
 serviceCurrentStatus = SERVICE_STOPPED;
   UpdateSCMStatus(SERVICE_STOPPED, NO_ERROR, 0, 1, 0);
 // End suneidoservice.exe
   exit(0);
 return;
}
/*--------------------------------------------------------------------------------
 Parse Command Line
 --------------------------------------------------------------------------------*/
void ProcessCommandLine(int argc, char *argv[])
{
if ((strcmp(argv[1],"/h"«»)==0) || (strcmp(argv[1],"/H"«»)==0))
{
ShowHelp();
return;
}

if ((strcmp(argv[1],"/i"«»)==0) || (strcmp(argv[1],"/I"«»)==0))
{
InstallService();
return;
}
if ((strcmp(argv[1],"/u"«»)==0) || (strcmp(argv[1],"/U"«»)==0)) 
{
UnInstallService();
return;
}
// If parameters not recognized, show help.
ShowHelp();
}

/*--------------------------------------------------------------------------------
 Show a message box with help
 --------------------------------------------------------------------------------*/
void ShowHelp()
{
MessageBox(NULL,"Suneido NT Service SupportnBy Santiago Ottonello ( This e-mail address is being protected from spam bots, you need JavaScript enabled to view it )nnCommand line parameters:n/i Install Suneido as a servicen/u Uninstall Suneido servicen","Suneido Service",0);
}

/*--------------------------------------------------------------------------------
 Install service
 --------------------------------------------------------------------------------*/
void InstallService()
{
SC_HANDLE myService, scm;
  // open a connection to the SCM
  scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
  if (!scm)
  {
   PrintError("InstallService","Open SCM Manager"«»);return;
  }
  // Get Program path
char szExe[MAX_PATH];
GetModuleFileName(NULL,szExe,MAX_PATH); 
  // Install the new service
  myService = CreateService(
      scm, SERVICE_NAME, // the internal service name used by the SCM
      SERVICE_NAME_LABEL,  // the external label seen in the Service Control applet
      SERVICE_ALL_ACCESS,  // We want full access to control the service
      SERVICE_WIN32_OWN_PROCESS,  // The service is a single app and not a driver
      SERVICE_DEMAND_START,  // The service will be started by us manually
      SERVICE_ERROR_NORMAL,  // If error during service start, don't misbehave.
      szExe,
      0, 0, 0, 0, 0);
  if (!myService)
  {
    PrintError("InstallService","CreateService failed"«»);return;
  }
  else
  {
  char szMsg[MAX_PATH];
sprintf(szMsg,"Suneido Service at:%s",szExe);
  MessageBox(NULL,szMsg,"Suneido Service",0);     
  }
  // clean up
  CloseServiceHandle(myService);
  CloseServiceHandle(scm);

return;
}

/*--------------------------------------------------------------------------------
 Uninstall the Service
 --------------------------------------------------------------------------------*/
void UnInstallService()
{

SC_HANDLE myService, scm;
  BOOL success;
  SERVICE_STATUS status;
  
  
// Open a Service Control Manager connection
  scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
  if (!scm)
  {
   PrintError("UnInstallService","Open SCM Manager failed"«»);return;
  }
   
  // Get the service's handle
  myService = OpenService(scm, SERVICE_NAME, SERVICE_ALL_ACCESS | DELETE);
  
if (!myService)
  {
   PrintError("UnInstallService","OpenService"«»);return;
  }
  
// Stop the service if necessary
  success = QueryServiceStatus(myService, &status);
  if (!success)
  {
   PrintError("UnInstallService","QueryServiceStatus"«»);return;
  }
  
if (status.dwCurrentState != SERVICE_STOPPED)
  {
   MessageBox(NULL,"Service currently active.  Stopping service...n", "Suneido Service",0);
   
success = ControlService(myService, SERVICE_CONTROL_STOP, &status);
    if (!success)
    {
     PrintError("UnInstallService","ControlService fails to stop service!"«»);return;
    }
    Sleep(500);
  }
  
// Remove the service
  success = DeleteService(myService);
  if (success)
  {
      MessageBox(NULL,"Service successfully removed.n","Suneido Service",0);
  }
  else
  {
      PrintError("UnIstallService","DeleteService Fails"«»);return;
  }
//Clean Up
  CloseServiceHandle(myService);
  CloseServiceHandle(scm);

return;
}
/*--------------------------------------------------------------------------------
 Format and show Errors. These errors will not show up unless
 Allow interact with dektop is checked in service properties
 --------------------------------------------------------------------------------*/
void PrintError(LPTSTR lpszFunction, LPTSTR msg) 

    
LPVOID lpMsgBuf;
char szMsg[256];
  DWORD dw = GetLastError(); 

  FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );
sprintf(szMsg,"Error in function:%s Step:%s.nFailed with error %d:n%sn", lpszFunction, msg, dw, lpMsgBuf);

MessageBox(NULL,szMsg,SERVICE_NAME_LABEL,0);
  UpdateSCMStatus(SERVICE_STOPPED, NO_ERROR, 0, 0, 0);
 return;  
}
/*--------------------------------------------------------------------------------
 Convenience function to update the service status
 --------------------------------------------------------------------------------*/
BOOL UpdateSCMStatus (DWORD dwCurrentState,
                      DWORD dwWin32ExitCode,
                      DWORD dwServiceSpecificExitCode,
                      DWORD dwCheckPoint,
                      DWORD dwWaitHint)
{
BOOL success;
  SERVICE_STATUS serviceStatus;
// Standard good practice
ZeroMemory( &serviceStatus, sizeof(serviceStatus) );
  // Fill in all of the SERVICE_STATUS fields
  serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  serviceStatus.dwCurrentState = dwCurrentState;
serviceStatus.dwWin32ExitCode=dwWin32ExitCode;
serviceStatus.dwServiceSpecificExitCode=dwServiceSpecificExitCode;
serviceStatus.dwCheckPoint=dwCheckPoint;
serviceStatus.dwWaitHint=dwWaitHint;
  // If in the process of something, then accept
  // no control events, else accept anything
  if (dwCurrentState == SERVICE_START_PENDING)
  {
serviceStatus.dwControlsAccepted = 0;
  }
  else
  {
//Suneido Server only accepts start and stop
serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
  }
  // Pass the status record to the SCM
  success = SetServiceStatus (serviceStatusHandle, &serviceStatus);
   
  return success;
}
/*--------------------------------------------------------------------------------
 Stop server
 --------------------------------------------------------------------------------*/

int KillService()
{
STARTUPINFO si;
  PROCESS_INFORMATION pi;

  ZeroMemory( &si, sizeof(si) );
  si.cb = sizeof(si);
  ZeroMemory( &pi, sizeof(pi) );

//Build a command line to start a client requesting shutdown
char szCmdLine[MAX_PATH];
sprintf(szCmdLine,"%ssuneido.exe -n -u -c localhost Shutdown(true)",SuneidoPath);

  // Start the child process. 
  CreateProcess( NULL,   // No module name (use command line). 
        szCmdLine, // Command line. 
        NULL,             // Process handle not inheritable. 
        NULL,             // Thread handle not inheritable. 
        FALSE,            // Set handle inheritance to FALSE. 
        0,                // No creation flags. 
        NULL,             // Use parent's environment block. 
        SuneidoPath,             // Use parent's starting directory. 
        &si,              // Pointer to STARTUPINFO structure.
        &pi );             // Pointer to PROCESS_INFORMATION structure.
  // Wait until child process exits.
  WaitForSingleObject( pi.hProcess, INFINITE );

  // Close process and thread handles. 
  CloseHandle( pi.hProcess );
  CloseHandle( pi.hThread );

  return 0;

}
/*--------------------------------------------------------------------------------
 Handles the events dispatched by the Service Control Manager.
 --------------------------------------------------------------------------------*/
void ServiceCtrlHandler (DWORD controlCode)
{
switch(controlCode)
  {
// There is no START option because
   // ServiceMain gets called on a start
    case SERVICE_CONTROL_INTERROGATE:
      // This does nothing, here we will just fall through to the end
      // and send our current status.
     break;
   // For a shutdown, we can do cleanup but it must take place quickly
   // because the system will go down out from under us.
   // For this app we have time to stop here, which I do by just falling
   // through to the stop message.
   case SERVICE_CONTROL_SHUTDOWN:
   // Stop the service
   case SERVICE_CONTROL_STOP:
    // Tell the SCM we're about to Stop.
    serviceCurrentStatus = SERVICE_STOP_PENDING;
    UpdateSCMStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000);
// Kill server
    KillService();
   default:
break;
  }
//Update SCM
  UpdateSCMStatus(serviceCurrentStatus, NO_ERROR, 0, 0, 0);
//if (serviceCurrentStatus == SERVICE_STOPPED) exit(0);
 
}



You should compile it as a console mode program.
You will obtain an .exe called SuneidoServer.exe
If you downloaded PellesC you can use the project file included in the
attached .zip file to build it from the IDE.

Alternatively you can use the ready made binary included in the same
.zip file.

NOTE OF CAUTION
----------------
You should NEVER run an exe you don't trust.
Even knowledgeable programmers with the best intentions
can have it's machine infected by viruses.
My advice is to COMPILE THE SOURCE YOURSELF, you will get security and
KNOWLEDGE about what are you about to run.
If you still want to use my included binary, please bear in mind that
I have a VERY OUTDATED ANTIVIRUS (AVG Free edition 2004 !!!!:-( ) and ...
I don't Practice what I preach :-)
If you still want to go with my ready made binary...
USE AT YOUR OWN RISK I SHALL NOT BE LIABLE FOR ANY
CONSECUENCES IN NO CIRCUNSTANCES.

Installing the service
----------------------
1) Copy SuneidoService.exe in the same directory where suneido.exe and suneido.db
resides.

2) Open a command line window and change to suneido directory.
3) Run suneidoservice.exe /i
4) The service is now installed, you can use service manager to start and stop the server

If you need to uninstall the service you can use suneidoservice.exe /u

Service configuration
---------------------
I will recommend to swith to start type automatic (Now we can get rid of that lousy operator :-) )
In the service properties uncheck (if checked ) the box "Allow to interact with desktop"
If you don't do this, the Suneido Server Window will show on desktop.
In case you have any trouble, you can check the same box("Allow to interact with desktop")
and a MessageBox will show up informing any error it founds.
Once corrected, don't forget to uncheck it back.
Enjoy ;-)
File Attachment:
File Name: SuneidoService-875e89e70832e93781f3e8ecff84732a.zip
File Size: 28259


Post edited by: sanotto, at: 2006/08/22 16:23
 
 
#330
Re:Running Suneido as a Service 4 Years ago
Again, very nice!

We use AppToService which does a similar thing, but it is not free and it is more complicated. We have had a few problems but I am not sure if AppToService is the cause.

See my forum post on Bounties.

Post edited by: andrew, at: 2006/12/17 16:54
 
 
andrew
 
#492
Re:Running Suneido as a Service 3 Years, 8 Months ago
Hi, Andrew, the 'Bounties' link redirects to this page...
Anyway, I think this 'Suneido as service' mod could be included in the next Suneido release. ;)

Nice work, sanotto!
 
 
Mauro
 
#493
Re:Running Suneido as a Service 3 Years, 8 Months ago
I fixed the link - thanks. I had the right url, but I had put quotes on it, as you would in html, but that seems to confuse the forum.
 
 
andrew
 
#501
Re:Running Suneido as a Service 3 Years, 8 Months ago
Hey Mauro...

Thanks for your compliments...

:)