//  Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
//
//  This file was part of the VNC system.
//
//  The VNC system 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.
//
// If the source code for the VNC system is not available from the place 
// whence you received this file, check http://www.uk.research.att.com/vnc or contact
// the authors on vnc@uk.research.att.com for information on obtaining it.
//
// This file has been adapted to the Win32 version of Bacula
// by Kern E. Sibbald.  Many thanks to ATT and James Weatherall,
// the original author, for providing an excellent template.
//
// Copyright (2000-2003) Kern E. Sibbald
//


// winService

// Implementation of service-oriented functionality of Bacula
// I.e. command line options that contact a running version of
// Bacula and ask it to do something (show about, show status,
// show events, ...)


#include <lmcons.h>
#include "winbacula.h"
#include "winservice.h"
#include "wintray.h"
                       

void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
                             LPSTR lpDesc);

// OS-SPECIFIC ROUTINES

// Create an instance of the bacService class to cause the static fields to be
// initialised properly

bacService init;

DWORD   g_platform_id;

bacService::bacService()
{
   OSVERSIONINFO osversioninfo;
   osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);

   // Get the current OS version
   if (!GetVersionEx(&osversioninfo)) {
      g_platform_id = 0;
   } else {
      g_platform_id = osversioninfo.dwPlatformId;
   }
}


// IsWin95 - returns a BOOL indicating whether the current OS is Win95
BOOL
bacService::IsWin95()
{
   return (g_platform_id == VER_PLATFORM_WIN32_WINDOWS);
}

// IsWinNT - returns a bool indicating whether the current OS is WinNT
BOOL
bacService::IsWinNT()
{
   return (g_platform_id == VER_PLATFORM_WIN32_NT);
}

// Internal routine to find the  Bacula menu class window and
// post a message to it!

BOOL
PostToBacula(UINT message, WPARAM wParam, LPARAM lParam)
{
  // Locate the hidden Bacula menu window
  HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
  if (hservwnd == NULL) {
     return FALSE;
  }

  // Post the message to Bacula
  PostMessage(hservwnd, message, wParam, lParam);
  return TRUE;
}


// Static routine to show the Properties dialog for a currently-running
// copy of Bacula, (usually a servicified version.)

BOOL
bacService::ShowProperties()
{
   return TRUE;
}

// Static routine to show the Default Properties dialog for a currently-running
// copy of Bacula, (usually a servicified version.)

BOOL
bacService::ShowDefaultProperties()
{
   return TRUE;
}

// Static routine to show the About dialog for a currently-running
// copy of Bacula, (usually a servicified version.)

BOOL
bacService::ShowAboutBox()
{
  // Post to the Bacula menu window
  if (!PostToBacula(MENU_ABOUTBOX_SHOW, 0, 0)) {
     MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
     return FALSE;
  }
  return TRUE;
}

// Static routine to show the Status dialog for a currently-running
// copy of Bacula, (usually a servicified version.)

BOOL
bacService::ShowStatus()
{
  // Post to the Bacula menu window
  if (!PostToBacula(MENU_STATUS_SHOW, 0, 0)) {
     MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
     return FALSE;
  }
  return TRUE;
}

#ifdef xxx_needed
// Static routine to show the Events dialog for a currently-running
// copy of Bacula, (usually a servicified version.)

BOOL
bacService::ShowEvents()
{
  // Post to the Bacula menu window
  if (!PostToBacula(MENU_EVENTS_SHOW, 0, 0)) {
     MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
     return FALSE;
  }
  return TRUE;
}


// Static routine to tell a locally-running instance of the server
// to connect out to a new client

BOOL
bacService::PostAddNewClient(unsigned long ipaddress)
{
  // Post to the Bacula menu window
  if (!PostToBacula(MENU_ADD_CLIENT_MSG, 0, ipaddress)) {
     MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
     return FALSE;
  }

  return TRUE;
}
#endif

// SERVICE-MODE ROUTINES

// Service-mode defines:

// Executable name
#define BAC_APPNAME            "bacula"

// Internal service name
#define BAC_SERVICENAME        "Bacula"

// Displayed service name
#define BAC_SERVICEDISPLAYNAME "Bacula File Server"

// List other required serves 
#define BAC_DEPENDENCIES       ""


// Internal service state
SERVICE_STATUS          g_srvstatus;       // current status of the service
SERVICE_STATUS_HANDLE   g_hstatus;
DWORD                   g_error = 0;
DWORD                   g_servicethread = 0;
char*                   g_errortext[256];


// Forward defines of internal service functions
void WINAPI ServiceMain(DWORD argc, char **argv);
DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
void ServiceStop();
void WINAPI ServiceCtrl(DWORD ctrlcode);
bool WINAPI CtrlHandler (DWORD ctrltype);
BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);

// ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT

BOOL    g_servicemode = FALSE;

BOOL
bacService::RunningAsService()
{
   return g_servicemode;
}

BOOL
bacService::KillRunningCopy()
{
  while (PostToBacula(WM_CLOSE, 0, 0))
      {  }
  return TRUE;
}

#ifdef xxx_needed

// ROUTINE TO POST THE HANDLE OF THE CURRENT USER TO THE RUNNING Bacula, IN ORDER
// THAT IT CAN LOAD THE APPROPRIATE SETTINGS.  THIS IS USED ONLY BY THE SVCHELPER
// OPTION, WHEN RUNNING UNDER NT
BOOL
bacService::PostUserHelperMessage()
{
  // - Check the platform type
  if (!IsWinNT()) {
     return TRUE;
  }

  // - Get the current process ID
  DWORD processId = GetCurrentProcessId();

  // - Post it to the existing Bacula
  if (!PostToBacula(MENU_SERVICEHELPER_MSG, 0, (LPARAM)processId)) {
     return FALSE;
  }

  // - Wait until it's been used
  return TRUE;
}

// ROUTINE TO PROCESS AN INCOMING INSTANCE OF THE ABOVE MESSAGE
BOOL
bacService::ProcessUserHelperMessage(WPARAM wParam, LPARAM lParam) {
   return TRUE;
}

#endif

// SERVICE MAIN ROUTINE
int
bacService::BaculaServiceMain()
{
   // Mark that we are a service
   g_servicemode = TRUE;

   // How to run as a service depends upon the OS being used
   switch (g_platform_id) {

   // Windows 95/98/Me
   case VER_PLATFORM_WIN32_WINDOWS:
      {
      // Obtain a handle to the kernel library
      HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
      if (kerneldll == NULL) {
         MessageBox(NULL, "KERNEL32.DLL not found: Bacula service not started", 
             "Bacula Service", MB_OK);
         break;
      }

      // And find the RegisterServiceProcess function
      DWORD (WINAPI *RegisterService)(DWORD, DWORD);
      RegisterService = (DWORD (WINAPI *)(DWORD, DWORD))
              GetProcAddress(kerneldll, "RegisterServiceProcess");
      if (RegisterService == NULL) {
         MessageBox(NULL, "Registry service not found: Bacula service not started",
            "Bacula Service", MB_OK);
         log_error_message("Registry service not found"); 
         break;
      }
      
      // Register this process with the OS as a service!
      RegisterService(0, 1);

      // Run the main program as a service
      BaculaAppMain();

      // Then remove the service from the system service table
      RegisterService(0, 0);

      // Free the kernel library
      FreeLibrary(kerneldll);
      break;
      }


   // Windows NT, Win2K, WinXP 
   case VER_PLATFORM_WIN32_NT:
      {
      // Create a service entry table
      SERVICE_TABLE_ENTRY dispatchTable[] = {
         {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
         {NULL, NULL}
      };

      // Call the service control dispatcher with our entry table
      if (!StartServiceCtrlDispatcher(dispatchTable)) {
         log_error_message("StartServiceCtrlDispatcher failed.");
      }
      break;
      } /* end case */
   } /* end switch */
   return 0;
}

// SERVICE MAIN ROUTINE - NT ONLY !!!
// NT/Win2K/WinXP ONLY !!!
void WINAPI ServiceMain(DWORD argc, char **argv)
{
    DWORD dwThreadID;

    // Register the service control handler
    g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);

    if (g_hstatus == 0) {
       log_error_message("RegisterServiceCtlHandler failed"); 
       MessageBox(NULL, "Contact Register Service Handler failure",
          "Bacula service", MB_OK);
       return;
    }

     // Set up some standard service state values
    g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
    g_srvstatus.dwServiceSpecificExitCode = 0;

        // Give this status to the SCM
    if (!ReportStatus(
            SERVICE_START_PENDING,          // Service state
            NO_ERROR,                       // Exit code type
            45000)) {                       // Hint as to how long Bacula should have hung before you assume error

        ReportStatus(SERVICE_STOPPED, g_error,  0);
        log_error_message("ReportStatus STOPPED failed 1"); 
        return;
    }

        // Now start the service for real
    (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
    return;
}

// SERVICE START ROUTINE - thread that calls BaculaAppMain
//   NT ONLY !!!!
DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
{

    // Save the current thread identifier
    g_servicethread = GetCurrentThreadId();

    // report the status to the service control manager.
    //
    if (!ReportStatus(
          SERVICE_RUNNING,       // service state
          NO_ERROR,              // exit code
          0)) {                  // wait hint
       MessageBox(NULL, "Report Service failure", "Bacula Service", MB_OK);
       log_error_message("ReportStatus RUNNING failed"); 
       return 0;
    }

    /* Call Bacula main code */
    BaculaAppMain();

    /* Mark that we're no longer running */
    g_servicethread = 0;

    /* Tell the service manager that we've stopped */
    ReportStatus(SERVICE_STOPPED, g_error, 0);
    return 0;
}


// SERVICE STOP ROUTINE - post a quit message to the relevant thread
void ServiceStop()
{
   // Post a quit message to the main service thread
   if (g_servicethread != 0) {
      PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
   }
}

// SERVICE INSTALL ROUTINE
int
bacService::InstallService()
{
   const int pathlength = 2048;
   char path[pathlength];
   char servicecmd[pathlength];
   int len;

   // Get the filename of this executable
   if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
      MessageBox(NULL, "Unable to install Bacula service", szAppName, MB_ICONEXCLAMATION | MB_OK);
      return 0;
   }

   // Append the service-start flag to the end of the path:
   if ((int)strlen(path) + 20 + (int)strlen(BaculaRunService) < pathlength) {
      sprintf(servicecmd, "\"%s\" %s -c \"%s\"", path, BaculaRunService, path);
      len = strlen(servicecmd) - 1;
      for ( ; len > 0; len--) {
         if (servicecmd[len] == '\\') {
            servicecmd[len] = 0;
            break;
         }
         servicecmd[len] = 0;
      }
      strcat(servicecmd, "\\bacula-fd.conf");

   } else {
      log_error_message("Service command length too long"); 
      MessageBox(NULL, "Service command length too long. Service not registered.",
          szAppName, MB_ICONEXCLAMATION | MB_OK);
      return 0;
   }

   // How to add the Bacula service depends upon the OS
   switch (g_platform_id) {

   // Windows 95/98/Me
   case VER_PLATFORM_WIN32_WINDOWS:
      // Locate the RunService registry entry
      HKEY runservices;
      if (RegCreateKey(HKEY_LOCAL_MACHINE, 
              "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
              &runservices) != ERROR_SUCCESS) {
         log_error_message("Cannot write System Registry"); 
         MessageBox(NULL, "The System Registry could not be updated - the Bacula service was not installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
         break;
      }

      // Attempt to add a Bacula key
      if (RegSetValueEx(runservices, szAppName, 0, REG_SZ, (unsigned char *)servicecmd, strlen(servicecmd)+1) != ERROR_SUCCESS) {
         RegCloseKey(runservices);
         log_error_message("Cannot add Bacula key to System Registry"); 
         MessageBox(NULL, "The Bacula service could not be installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
         break;
      }

      RegCloseKey(runservices);

      // We have successfully installed the service!
      MessageBox(NULL,
              "The Bacula File service was successfully installed.\n"
              "The service may be started by double clicking on the\n"
              "Bacula \"Start\" icon and will be automatically\n"
              "be run the next time this machine is rebooted. ",
              szAppName,
              MB_ICONINFORMATION | MB_OK);
      break;

   // Windows NT, Win2K, WinXP
   case VER_PLATFORM_WIN32_NT:
      SC_HANDLE   hservice;
      SC_HANDLE   hsrvmanager;

      // Open the default, local Service Control Manager database
      hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
      if (hsrvmanager == NULL) {
         log_error_message("OpenSCManager failed"); 
         MessageBox(NULL,
            "The Service Control Manager could not be contacted - the Bacula service was not installed",
            szAppName, MB_ICONEXCLAMATION | MB_OK);
         break;
      }

      // Create an entry for the Bacula service
      hservice = CreateService(
              hsrvmanager,                    // SCManager database
              BAC_SERVICENAME,                // name of service
              BAC_SERVICEDISPLAYNAME,         // name to display
              SERVICE_ALL_ACCESS,             // desired access
              SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
                                                                      // service type
              SERVICE_AUTO_START,             // start type
              SERVICE_ERROR_NORMAL,           // error control type
              servicecmd,                     // service's binary
              NULL,                           // no load ordering group
              NULL,                           // no tag identifier
              BAC_DEPENDENCIES,               // dependencies
              NULL,                           // LocalSystem account
              NULL);                          // no password
      if (hservice == NULL) {
         CloseServiceHandle(hsrvmanager);
         log_error_message("CreateService failed"); 
         MessageBox(NULL,
             "The Bacula service could not be installed",
              szAppName, MB_ICONEXCLAMATION | MB_OK);
         break;
      }

      set_service_description(hsrvmanager,hservice, 
"Provides file backup and restore services. Bacula -- the network backup solution.");

      CloseServiceHandle(hsrvmanager);
      CloseServiceHandle(hservice);

      // Now install the servicehelper registry setting...
      // Locate the RunService registry entry
      HKEY runapps;
      if (RegCreateKey(HKEY_LOCAL_MACHINE, 
              "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
              &runapps) != ERROR_SUCCESS) {
         MessageBox(NULL, "WARNING: Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded", 
            szAppName, MB_ICONEXCLAMATION | MB_OK);
      } else {
         char servicehelpercmd[pathlength];

         // Append the service-helper-start flag to the end of the path:
         if ((int)strlen(path) + 4 + (int)strlen(BaculaRunServiceHelper) < pathlength) {
            sprintf(servicehelpercmd, "\"%s\" %s", path, BaculaRunServiceHelper);

            // Add the Bacula Service Helper entry
             if (RegSetValueEx(runapps, szAppName, 0, REG_SZ,
                 (unsigned char *)servicehelpercmd, strlen(servicehelpercmd)+1) != ERROR_SUCCESS) {
                MessageBox(NULL, "WARNING:Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded", szAppName, MB_ICONEXCLAMATION | MB_OK);
             }
             RegCloseKey(runapps);
         }
      }

      // Everything went fine
      MessageBox(NULL,
              "The Bacula File service was successfully installed.\n"
              "The service may be started from the Control Panel and will\n"
              "automatically be run the next time this machine is rebooted.",
              szAppName,
              MB_ICONINFORMATION | MB_OK);
      break;
   default:
      log_error_message("Unknown Windows System version"); 
      MessageBox(NULL, 
                 "Unknown Windows operating system.\n"     
                 "Cannot install Bacula service.\n",
                 szAppName, MB_ICONEXCLAMATION | MB_OK);
       break;     
   };

   return 0;
}


// SERVICE REMOVE ROUTINE
int
bacService::RemoveService()
{
   // How to remove the Bacula service depends upon the OS
   switch (g_platform_id) {

   // Windows 95/98/Me
   case VER_PLATFORM_WIN32_WINDOWS:
      // Locate the RunService registry entry
      HKEY runservices;
      if (RegOpenKey(HKEY_LOCAL_MACHINE, 
              "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
              &runservices) != ERROR_SUCCESS) {
         MessageBox(NULL, 
            "Could not find registry entry.\nService probably not registerd - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
      } else {
         // Attempt to delete the Bacula key
         if (RegDeleteValue(runservices, szAppName) != ERROR_SUCCESS) {
            RegCloseKey(runservices);
            MessageBox(NULL, "Could not delete Registry key.\nThe Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
         }

         RegCloseKey(runservices);
         break;
      }

      // Try to kill any running copy of Bacula
      if (!KillRunningCopy()) {
         MessageBox(NULL,
             "Bacula could not be contacted, probably not running",
             szAppName, MB_ICONEXCLAMATION | MB_OK);
         break;
      }

      // We have successfully removed the service!
      MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
      break;

   // Windows NT, Win2K, WinXP
   case VER_PLATFORM_WIN32_NT:
      SC_HANDLE   hservice;
      SC_HANDLE   hsrvmanager;

      // Attempt to remove the service-helper hook
      HKEY runapps;
      if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
              &runapps) == ERROR_SUCCESS) {
         // Attempt to delete the Bacula key
         if (RegDeleteValue(runapps, szAppName) != ERROR_SUCCESS) {
            MessageBox(NULL, "WARNING:The ServiceHelper hook entry could not be removed from the registry", szAppName, MB_ICONEXCLAMATION | MB_OK);
         }
         RegCloseKey(runapps);
      }

      // Open the SCM
      hsrvmanager = OpenSCManager(
         NULL,                   // machine (NULL == local)
         NULL,                   // database (NULL == default)
         SC_MANAGER_ALL_ACCESS   // access required
         );
      if (hsrvmanager) {
         hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
         if (hservice != NULL) {
            SERVICE_STATUS status;

            // Try to stop the Bacula service
            if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
               while(QueryServiceStatus(hservice, &status)) {
                  if (status.dwCurrentState == SERVICE_STOP_PENDING) {
                     Sleep(1000);
                  } else {
                     break;
                  }
               }

               if (status.dwCurrentState != SERVICE_STOPPED) {
                  MessageBox(NULL, "The Bacula service could not be stopped", szAppName, MB_ICONEXCLAMATION | MB_OK);
               }
            }

            // Now remove the service from the SCM
            if(DeleteService(hservice)) {
               MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
            } else {
               MessageBox(NULL, "The Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
            }

            CloseServiceHandle(hservice);
         } else {
            MessageBox(NULL, "The Bacula service could not be found", szAppName, MB_ICONEXCLAMATION | MB_OK);
         }

         CloseServiceHandle(hsrvmanager);
      } else {
         MessageBox(NULL, "The SCM could not be contacted - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
      }
      break;
   }
   return 0;
}

// USEFUL SERVICE SUPPORT ROUTINES

// Service control routine
void WINAPI ServiceCtrl(DWORD ctrlcode)
{
    // What control code have we been sent?
    switch(ctrlcode) {
    case SERVICE_CONTROL_STOP:
        // STOP : The service must stop
        g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
        ServiceStop();
        break;

    case SERVICE_CONTROL_INTERROGATE:
        // QUERY : Service control manager just wants to know our state
        break;

     default:
        // Control code not recognised
        break;
    }

    // Tell the control manager what we're up to.
    ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
}

// Service manager status reporting
BOOL ReportStatus(DWORD state,
                  DWORD exitcode,
                  DWORD waithint)
{
    static DWORD checkpoint = 1;
    BOOL result = TRUE;

    // If we're in the start state then we don't want the control manager
    // sending us control messages because they'll confuse us.
    if (state == SERVICE_START_PENDING) {
       g_srvstatus.dwControlsAccepted = 0;
    } else {
       g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    }

    // Save the new status we've been given
    g_srvstatus.dwCurrentState = state;
    g_srvstatus.dwWin32ExitCode = exitcode;
    g_srvstatus.dwWaitHint = waithint;

    // Update the checkpoint variable to let the SCM know that we
    // haven't died if requests take a long time
    if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
       g_srvstatus.dwCheckPoint = 0;
    } else {
       g_srvstatus.dwCheckPoint = checkpoint++;
    }

    // Tell the SCM our new status
    if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
       log_error_message("SetServiceStatus failed");
    }

    return result;
}

// Error reporting
void LogErrorMsg(char *message, char *fname, int lineno)
{
   char        msgbuff[256];
   HANDLE      heventsrc;
   char *      strings[32];
   LPTSTR      msg;

   // Get the error code
   g_error = GetLastError();
   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
                 FORMAT_MESSAGE_FROM_SYSTEM,
                 NULL,
                 g_error,
                 0,
                 (LPTSTR)&msg,
                 0,
                 NULL);

   // Use event logging to log the error
   heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);

   sprintf(msgbuff, "\n\n%s error: %ld at %s:%d", 
      BAC_SERVICENAME, g_error, fname, lineno);
   strings[0] = msgbuff;
   strings[1] = message;
   strings[2] = msg;

   if (heventsrc != NULL) {
      MessageBeep(MB_OK);

      ReportEvent(
              heventsrc,              // handle of event source
              EVENTLOG_ERROR_TYPE,    // event type
              0,                      // event category
              0,                      // event ID
              NULL,                   // current user's SID
              3,                      // strings in 'strings'
              0,                      // no bytes of raw data
              (const char **)strings, // array of error strings
              NULL);                  // no raw data

      DeregisterEventSource(heventsrc);
   }
   LocalFree(msg);
}
typedef BOOL  (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);

void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
                             LPSTR lpDesc) 
{ 
    SC_LOCK sclLock; 
    LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf; 
    SERVICE_DESCRIPTION sdBuf;
    DWORD dwBytesNeeded;
    WinAPI ChangeServiceDescription;
 
    HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
    if (!hLib) {
       return;
    }
    ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
       "ChangeServiceConfig2A");
    FreeLibrary(hLib);
    if (!ChangeServiceDescription) {
       return;
    }
    
    // Need to acquire database lock before reconfiguring. 
    sclLock = LockServiceDatabase(hSCManager); 
 
    // If the database cannot be locked, report the details. 
    if (sclLock == NULL) {
       // Exit if the database is not locked by another process. 
       if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
          log_error_message("LockServiceDatabase"); 
          return;
       }
 
       // Allocate a buffer to get details about the lock. 
       lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc( 
            LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256); 
       if (lpqslsBuf == NULL) {
          log_error_message("LocalAlloc"); 
          return;
       }
 
       // Get and print the lock status information. 
       if (!QueryServiceLockStatus( 
              hSCManager, 
              lpqslsBuf, 
              sizeof(QUERY_SERVICE_LOCK_STATUS)+256, 
              &dwBytesNeeded)) {
          log_error_message("QueryServiceLockStatus"); 
       }
 
       if (lpqslsBuf->fIsLocked) {
          printf("Locked by: %s, duration: %ld seconds\n", 
                lpqslsBuf->lpLockOwner, 
                lpqslsBuf->dwLockDuration); 
       } else {
          printf("No longer locked\n"); 
       }
 
       LocalFree(lpqslsBuf); 
       log_error_message("Could not lock database"); 
       return;
    } 
 
    // The database is locked, so it is safe to make changes. 
 
    sdBuf.lpDescription = lpDesc;

    if(!ChangeServiceDescription(
         hService,                   // handle to service
         SERVICE_CONFIG_DESCRIPTION, // change: description
         &sdBuf) ) {                 // value: new description
       log_error_message("ChangeServiceConfig2");
    } else {
       printf("ChangeServiceConfig2 SUCCESS\n");
    }

    // Release the database lock. 
    UnlockServiceDatabase(sclLock); 
}
