From 99f4467124bc322b3cebd19b19f5150f79f144f3 Mon Sep 17 00:00:00 2001
From: rswindell <>
Date: Thu, 9 Oct 2003 08:35:55 +0000
Subject: [PATCH] Support for starting, stopping, recycling, and monitoring
 Synchronet NT services.

---
 src/sbbs3/ctrl/MainFormUnit.cpp | 279 +++++++++++++++++++++++++-------
 src/sbbs3/ctrl/MainFormUnit.dfm |  56 ++++---
 src/sbbs3/ctrl/MainFormUnit.h   |   2 +
 3 files changed, 257 insertions(+), 80 deletions(-)

diff --git a/src/sbbs3/ctrl/MainFormUnit.cpp b/src/sbbs3/ctrl/MainFormUnit.cpp
index adabb0ee6a..eae74287c3 100644
--- a/src/sbbs3/ctrl/MainFormUnit.cpp
+++ b/src/sbbs3/ctrl/MainFormUnit.cpp
@@ -83,6 +83,31 @@ TMainForm *MainForm;
 
 #define LOG_TIME_FMT "  m/d  hh:mm:ssa/p"
 
+/* Service functions are NT-only, must call dynamically :-( */
+typedef WINADVAPI SC_HANDLE (WINAPI *OpenSCManager_t)(LPCTSTR,LPCTSTR,DWORD);
+typedef WINADVAPI SC_HANDLE (WINAPI *OpenService_t)(SC_HANDLE,LPCTSTR,DWORD);
+typedef WINADVAPI BOOL (WINAPI *StartService_t)(SC_HANDLE,DWORD,LPCTSTR*);
+typedef WINADVAPI BOOL (WINAPI *ControlService_t)(SC_HANDLE,DWORD,LPSERVICE_STATUS);
+typedef WINADVAPI BOOL (WINAPI *QueryServiceStatus_t)(SC_HANDLE,LPSERVICE_STATUS);
+typedef WINADVAPI BOOL (WINAPI *CloseServiceHandle_t)(SC_HANDLE);
+
+OpenSCManager_t 		openSCManager;
+OpenService_t 			openService;
+StartService_t  		startService;
+ControlService_t    	controlService;
+QueryServiceStatus_t    queryServiceStatus;
+CloseServiceHandle_t	closeServiceHandle;
+
+SC_HANDLE       hSCManager;
+SC_HANDLE	    bbs_svc;
+SC_HANDLE	    ftp_svc;
+SC_HANDLE	    mail_svc;
+SC_HANDLE	    services_svc;
+SERVICE_STATUS	bbs_svc_status;
+SERVICE_STATUS	ftp_svc_status;
+SERVICE_STATUS	mail_svc_status;
+SERVICE_STATUS	services_svc_status;
+
 DWORD	MaxLogLen=20000;
 int     threads=1;
 
@@ -724,7 +749,35 @@ __fastcall TMainForm::TMainForm(TComponent* Owner)
     SpyTerminalHeight=365;
     SpyTerminalKeyboardActive=true;
 
-    Application->OnMinimize=FormMinimize;    
+    Application->OnMinimize=FormMinimize;
+
+    /* Get pointers to NT Service functions */
+	HANDLE hSCMlib = LoadLibrary("ADVAPI32.DLL");
+	if(hSCMlib!=NULL) {
+		openSCManager=(OpenSCManager_t)GetProcAddress(hSCMlib, "OpenSCManagerA");
+		openService=(OpenService_t)GetProcAddress(hSCMlib,"OpenServiceA");
+		startService=(StartService_t)GetProcAddress(hSCMlib,"StartServiceA");
+		controlService=(ControlService_t)GetProcAddress(hSCMlib,"ControlService");
+		queryServiceStatus=(QueryServiceStatus_t)GetProcAddress(hSCMlib,"QueryServiceStatus");
+		closeServiceHandle=(CloseServiceHandle_t)GetProcAddress(hSCMlib,"CloseServiceHandle");
+		FreeLibrary(hSCMlib);
+    }
+
+    /* Open Service Manager */
+    if(openSCManager!=NULL)
+        hSCManager = openSCManager(
+                            NULL,                   // machine (NULL == local)
+                            NULL,                   // database (NULL == default)
+                            SC_MANAGER_ALL_ACCESS   // access required
+                            );
+
+	/* Open Synchronet Services */
+    if(hSCManager!=NULL) {
+		bbs_svc =  openService(hSCManager, NTSVC_NAME_BBS, SERVICE_ALL_ACCESS);
+		ftp_svc =  openService(hSCManager, NTSVC_NAME_FTP, SERVICE_ALL_ACCESS);
+		mail_svc =  openService(hSCManager, NTSVC_NAME_MAIL, SERVICE_ALL_ACCESS);
+		services_svc =  openService(hSCManager, NTSVC_NAME_SERVICES, SERVICE_ALL_ACCESS);
+    }
 }
 //---------------------------------------------------------------------------
 void __fastcall TMainForm::FileExitMenuItemClick(TObject *Sender)
@@ -804,8 +857,10 @@ void __fastcall TMainForm::FormClose(TObject *Sender, TCloseAction &Action)
 
 	StatusBar->Panels->Items[4]->Text="Closing...";
     time_t start=time(NULL);
-	while(TelnetStop->Enabled || MailStop->Enabled || FtpStop->Enabled
-    	|| ServicesStop->Enabled) {
+	while( (bbs_svc==NULL && TelnetStop->Enabled)
+        || (mail_svc==NULL && MailStop->Enabled)
+        || (ftp_svc==NULL && FtpStop->Enabled)
+    	|| (services_svc==NULL && ServicesStop->Enabled)) {
         if(time(NULL)-start>30)
             break;
         Application->ProcessMessages();
@@ -817,6 +872,10 @@ void __fastcall TMainForm::FormClose(TObject *Sender, TCloseAction &Action)
         Application->ProcessMessages();
         YIELD();
     }
+#if 0
+    if(hSCManager!=NULL)
+    	closeServiceHandle(hSCManager);
+#endif
 }
 //---------------------------------------------------------------------------
 
@@ -824,7 +883,7 @@ void __fastcall TMainForm::FormCloseQuery(TObject *Sender, bool &CanClose)
 {
 	CanClose=false;
 
-    if(TelnetStop->Enabled) {
+    if(TelnetStop->Enabled && bbs_svc==NULL) {
      	if(TelnetForm->ProgressBar->Position
 	        && Application->MessageBox("Shut down the Telnet Server?"
         	,"Telnet Server In Use", MB_OKCANCEL)!=IDOK)
@@ -832,7 +891,7 @@ void __fastcall TMainForm::FormCloseQuery(TObject *Sender, bool &CanClose)
         TelnetStopExecute(Sender);
 	}
 
-    if(MailStop->Enabled) {
+    if(MailStop->Enabled && mail_svc==NULL) {
     	if(MailForm->ProgressBar->Position
     		&& Application->MessageBox("Shut down the Mail Server?"
         	,"Mail Server In Use", MB_OKCANCEL)!=IDOK)
@@ -840,27 +899,40 @@ void __fastcall TMainForm::FormCloseQuery(TObject *Sender, bool &CanClose)
         MailStopExecute(Sender);
     }
 
-    if(FtpStop->Enabled) {
+    if(FtpStop->Enabled && ftp_svc==NULL) {
     	if(FtpForm->ProgressBar->Position
     		&& Application->MessageBox("Shut down the FTP Server?"
 	       	,"FTP Server In Use", MB_OKCANCEL)!=IDOK)
             return;
         FtpStopExecute(Sender);
     }
-    if(ServicesStop->Enabled)
+    if(ServicesStop->Enabled && services_svc==NULL)
 	    ServicesStopExecute(Sender);
 
     CanClose=true;
 }
 //---------------------------------------------------------------------------
-
+BOOL StartNTsvc(SC_HANDLE svc, SERVICE_STATUS* status)
+{
+	if(svc==NULL || startService==NULL || queryServiceStatus==NULL)
+    	return(FALSE);
+    if(!queryServiceStatus(svc,status))
+    	return(FALSE);
+    if(status->dwCurrentState!=SERVICE_STOPPED)
+    	return(TRUE);
+    return startService(svc,0,NULL);
+}
+//---------------------------------------------------------------------------
 void __fastcall TMainForm::TelnetStartExecute(TObject *Sender)
 {
-	bbs_start();
+	if(!StartNTsvc(bbs_svc,&bbs_svc_status))
+		bbs_start();
 }
 //---------------------------------------------------------------------------
 void __fastcall TMainForm::ServicesStartExecute(TObject *Sender)
 {
+	if(StartNTsvc(services_svc,&services_svc_status))
+    	return;
 	Screen->Cursor=crAppStart;
     services_status(NULL, "Starting");
 
@@ -882,20 +954,34 @@ void __fastcall TMainForm::ServicesStartExecute(TObject *Sender)
 }
 //---------------------------------------------------------------------------
 
+BOOL StopNTsvc(SC_HANDLE svc, SERVICE_STATUS* status)
+{
+	if(svc==NULL || controlService==NULL)
+    	return(FALSE);
+
+    return controlService(svc,SERVICE_CONTROL_STOP,status);
+}
+
+//---------------------------------------------------------------------------
+
 void __fastcall TMainForm::ServicesStopExecute(TObject *Sender)
 {
-	Screen->Cursor=crAppStart;
+	if(StopNTsvc(services_svc,&services_svc_status))
+    	return;
+    Screen->Cursor=crAppStart;
     services_status(NULL, "Terminating");
-	services_terminate();
+    services_terminate();
     Application->ProcessMessages();
 }
 //---------------------------------------------------------------------------
 
 void __fastcall TMainForm::TelnetStopExecute(TObject *Sender)
 {
-	Screen->Cursor=crAppStart;
+	if(StopNTsvc(bbs_svc,&bbs_svc_status))
+    	return;
+    Screen->Cursor=crAppStart;
     bbs_status(NULL, "Terminating");
-	bbs_terminate();
+    bbs_terminate();
     Application->ProcessMessages();
 }
 //---------------------------------------------------------------------------
@@ -948,19 +1034,21 @@ void __fastcall TMainForm::MailConfigureExecute(TObject *Sender)
 
 void __fastcall TMainForm::MailStartExecute(TObject *Sender)
 {
-    mail_start();
+	if(!StartNTsvc(mail_svc,&mail_svc_status))
+	    mail_start();
 }
 //---------------------------------------------------------------------------
 
 void __fastcall TMainForm::MailStopExecute(TObject *Sender)
 {
-	Screen->Cursor=crAppStart;
+	if(StopNTsvc(mail_svc,&mail_svc_status))
+    	return;
+    Screen->Cursor=crAppStart;
     mail_status(NULL, "Terminating");
-	mail_terminate();
+    mail_terminate();
     Application->ProcessMessages();
 }
 //---------------------------------------------------------------------------
-
 void __fastcall TMainForm::ViewTelnetExecute(TObject *Sender)
 {
 	TelnetForm->Visible=!TelnetForm->Visible;
@@ -997,15 +1085,18 @@ void __fastcall TMainForm::ViewFtpServerExecute(TObject *Sender)
 
 void __fastcall TMainForm::FtpStartExecute(TObject *Sender)
 {
-	ftp_start();
+	if(!StartNTsvc(ftp_svc,&ftp_svc_status))
+		ftp_start();
 }
 //---------------------------------------------------------------------------
 
 void __fastcall TMainForm::FtpStopExecute(TObject *Sender)
 {
-	Screen->Cursor=crAppStart;
+	if(StopNTsvc(ftp_svc,&ftp_svc_status))
+    	return;
+    Screen->Cursor=crAppStart;
     ftp_status(NULL, "Terminating");
-	ftp_terminate();
+    ftp_terminate();
     Application->ProcessMessages();
 }
 //---------------------------------------------------------------------------
@@ -1769,12 +1860,14 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender)
         LowerLeftPageControl->ActivePageIndex=i;
     LowerLeftPageControl->ActivePageIndex=0;
 
+    LogTimerTick(Sender);	/* Open Log Mailslots */
+
     if(SysAutoStart)
-        bbs_start();
+       TelnetStartExecute(Sender);
     if(MailAutoStart)
-        mail_start();
+        MailStartExecute(Sender);
     if(FtpAutoStart)
-        ftp_start();
+        FtpStartExecute(Sender);
     if(ServicesAutoStart)
         ServicesStartExecute(Sender);
 
@@ -3024,17 +3117,18 @@ void __fastcall TMainForm::UserTruncateMenuItemClick(TObject *Sender)
     sprintf(str,"%u Deleted User Records Removed",deleted);
    	Application->MessageBox(str,"Users Truncated",MB_OK);
 }
-//---------------------------------------------------------------------------
 
+BOOL RecycleService(SC_HANDLE svc, SERVICE_STATUS* status)
+{
+	if(controlService==NULL)
+    	return(FALSE);
+
+	return controlService(svc, SERVICE_CONTROL_RECYCLE, status);
+}
+//---------------------------------------------------------------------------
 void __fastcall TMainForm::MailRecycleExecute(TObject *Sender)
 {
-	HANDLE recycle_sem;
-    char name[256];
-    sprintf(name,"%sRecycle",NTSVC_NAME_MAIL);
-	if((recycle_sem=OpenSemaphore(SEMAPHORE_MODIFY_STATE,FALSE,name))!=NULL) {
-		ReleaseSemaphore(recycle_sem,1,NULL);
-        CloseHandle(recycle_sem);
-    } else {
+	if(!RecycleService(mail_svc,&mail_svc_status)) {
 		mail_startup.recycle_now=true;
 	    MailRecycle->Enabled=false;
     }
@@ -3043,13 +3137,7 @@ void __fastcall TMainForm::MailRecycleExecute(TObject *Sender)
 
 void __fastcall TMainForm::FtpRecycleExecute(TObject *Sender)
 {
-	HANDLE recycle_sem;
-    char name[256];
-    sprintf(name,"%sRecycle",NTSVC_NAME_FTP);
-	if((recycle_sem=OpenSemaphore(SEMAPHORE_MODIFY_STATE,FALSE,name))!=NULL) {
-		ReleaseSemaphore(recycle_sem,1,NULL);
-        CloseHandle(recycle_sem);
-    } else {
+	if(!RecycleService(ftp_svc,&ftp_svc_status)) {
 		ftp_startup.recycle_now=true;
 	    FtpRecycle->Enabled=false;
     }
@@ -3058,13 +3146,7 @@ void __fastcall TMainForm::FtpRecycleExecute(TObject *Sender)
 
 void __fastcall TMainForm::ServicesRecycleExecute(TObject *Sender)
 {
-	HANDLE recycle_sem;
-    char name[256];
-    sprintf(name,"%sRecycle",NTSVC_NAME_SERVICES);
-	if((recycle_sem=OpenSemaphore(SEMAPHORE_MODIFY_STATE,FALSE,name))!=NULL) {
-		ReleaseSemaphore(recycle_sem,1,NULL);
-        CloseHandle(recycle_sem);
-    } else {
+	if(!RecycleService(services_svc,&services_svc_status)) {
 		services_startup.recycle_now=true;
 	    ServicesRecycle->Enabled=false;
     }
@@ -3073,16 +3155,7 @@ void __fastcall TMainForm::ServicesRecycleExecute(TObject *Sender)
 
 void __fastcall TMainForm::TelnetRecycleExecute(TObject *Sender)
 {
-	HANDLE recycle_sem;
-    char name[256];
-    sprintf(name,"%sRecycle",NTSVC_NAME_BBS);
-	if((recycle_sem=OpenSemaphore(SEMAPHORE_MODIFY_STATE,FALSE,name))!=NULL) {
-		ReleaseSemaphore(recycle_sem,1,NULL);
-        CloseHandle(recycle_sem);
-    } else {
-    	char error[256];
-        sprintf(error,"ERROR %d opening semaphore: %s",GetLastError(),NTSVC_NAME_BBS);
-        bbs_lputs(NULL,error);
+    if(!RecycleService(bbs_svc,&bbs_svc_status)) {
 		bbs_startup.recycle_now=true;
 	    TelnetRecycle->Enabled=false;
     }
@@ -3248,5 +3321,101 @@ void __fastcall TMainForm::LogTimerTick(TObject *Sender)
 	while(GetServerLogLine(services_log,NTSVC_NAME_SERVICES,line,sizeof(line)))
     	service_lputs(NULL,line);
 }
+
+//---------------------------------------------------------------------------
+void CheckServiceStatus(
+	 SC_HANDLE svc
+	,SERVICE_STATUS* status
+	,TStaticText* text
+    ,TAction* start
+    ,TAction* stop
+    ,TAction* recycle
+    ,TProgressBar* bar
+    )
+{
+	if(svc==NULL)
+    	return;
+
+	if(!queryServiceStatus(svc,status)) {
+    	text->Caption="Error " + GetLastError();
+        return;
+	}
+
+    bool running=false;
+
+	switch(status->dwCurrentState) {
+    	case SERVICE_STOPPED:
+        	text->Caption="Stopped NT Service";
+            break;
+        case SERVICE_STOP_PENDING:
+        	text->Caption="Stopping NT Service";
+            break;
+        case SERVICE_RUNNING:
+        	text->Caption="Running NT Service";
+            running=true;
+            break;
+        case SERVICE_START_PENDING:
+        	text->Caption="Starting NT Service";
+            running=true;
+            break;
+        default:
+        	text->Caption="!UNKNOWN: " +  status->dwCurrentState;
+            break;
+    }
+
+    start->Enabled=!running;
+    stop->Enabled=running;
+    recycle->Enabled=running;
+}
+//---------------------------------------------------------------------------
+
+void __fastcall TMainForm::ServiceStatusTimerTick(TObject *Sender)
+{
+	if(queryServiceStatus==NULL
+    	|| (bbs_svc==NULL
+        && ftp_svc==NULL
+        && mail_svc==NULL
+        && services_svc==NULL)) {
+    	ServiceStatusTimer->Enabled=false;
+        return;
+    }
+
+    CheckServiceStatus(
+    	 bbs_svc
+    	,&bbs_svc_status
+        ,TelnetForm->Status
+        ,TelnetStart
+        ,TelnetStop
+        ,TelnetRecycle
+        ,TelnetForm->ProgressBar
+        );
+    CheckServiceStatus(
+    	 ftp_svc
+    	,&ftp_svc_status
+        ,FtpForm->Status
+        ,FtpStart
+        ,FtpStop
+        ,FtpRecycle
+        ,FtpForm->ProgressBar
+        );
+    CheckServiceStatus(
+    	 mail_svc
+    	,&mail_svc_status
+        ,MailForm->Status
+        ,MailStart
+        ,MailStop
+        ,MailRecycle
+        ,MailForm->ProgressBar
+        );
+    CheckServiceStatus(
+    	 services_svc
+    	,&services_svc_status
+        ,ServicesForm->Status
+        ,ServicesStart
+        ,ServicesStop
+        ,ServicesRecycle
+        ,NULL
+        );
+}
 //---------------------------------------------------------------------------
 
diff --git a/src/sbbs3/ctrl/MainFormUnit.dfm b/src/sbbs3/ctrl/MainFormUnit.dfm
index 6680f71a3d..5a9d3ec7ea 100644
--- a/src/sbbs3/ctrl/MainFormUnit.dfm
+++ b/src/sbbs3/ctrl/MainFormUnit.dfm
@@ -10,7 +10,7 @@ object MainForm: TMainForm
   DragMode = dmAutomatic
   Font.Charset = DEFAULT_CHARSET
   Font.Color = clWindowText
-  Font.Height = -11
+  Font.Height = -14
   Font.Name = 'MS Sans Serif'
   Font.Style = []
   Icon.Data = {
@@ -46,11 +46,11 @@ object MainForm: TMainForm
   OnCloseQuery = FormCloseQuery
   OnCreate = FormCreate
   OnShow = FormShow
-  PixelsPerInch = 96
-  TextHeight = 13
+  PixelsPerInch = 120
+  TextHeight = 16
   object HorizontalSplitter: TSplitter
     Left = 0
-    Top = 164
+    Top = 195
     Width = 632
     Height = 1
     Cursor = crVSplit
@@ -61,9 +61,9 @@ object MainForm: TMainForm
   end
   object Logo: TImage
     Left = 0
-    Top = 165
+    Top = 196
     Width = 632
-    Height = 162
+    Height = 131
     Align = alClient
     Center = True
     IncrementalDisplay = True
@@ -1957,7 +1957,7 @@ object MainForm: TMainForm
     Left = 0
     Top = 30
     Width = 632
-    Height = 134
+    Height = 165
     Align = alTop
     BevelOuter = bvNone
     Constraints.MinHeight = 100
@@ -1965,18 +1965,18 @@ object MainForm: TMainForm
     TabOrder = 1
     Visible = False
     object TopVerticalSplitter: TSplitter
-      Left = 235
+      Left = 289
       Top = 0
-      Width = 2
-      Height = 134
+      Width = 3
+      Height = 165
       Cursor = crHSplit
       MinSize = 1
     end
     object UpperLeftPageControl: TPageControl
       Left = 0
       Top = 0
-      Width = 235
-      Height = 134
+      Width = 289
+      Height = 165
       Align = alLeft
       DockSite = True
       TabOrder = 0
@@ -1984,10 +1984,10 @@ object MainForm: TMainForm
       OnUnDock = PageControlUnDock
     end
     object UpperRightPageControl: TPageControl
-      Left = 237
+      Left = 292
       Top = 0
-      Width = 395
-      Height = 134
+      Width = 340
+      Height = 165
       Align = alClient
       DockSite = True
       TabOrder = 1
@@ -1997,26 +1997,26 @@ object MainForm: TMainForm
   end
   object BottomPanel: TPanel
     Left = 0
-    Top = 165
+    Top = 196
     Width = 632
-    Height = 162
+    Height = 131
     Align = alClient
     BevelOuter = bvNone
     TabOrder = 2
     Visible = False
     object BottomVerticalSplitter: TSplitter
-      Left = 235
+      Left = 289
       Top = 0
-      Width = 2
-      Height = 162
+      Width = 3
+      Height = 131
       Cursor = crHSplit
       MinSize = 1
     end
     object LowerLeftPageControl: TPageControl
       Left = 0
       Top = 0
-      Width = 235
-      Height = 162
+      Width = 289
+      Height = 131
       Align = alLeft
       DockSite = True
       TabOrder = 0
@@ -2024,10 +2024,10 @@ object MainForm: TMainForm
       OnUnDock = PageControlUnDock
     end
     object LowerRightPageControl: TPageControl
-      Left = 237
+      Left = 292
       Top = 0
-      Width = 395
-      Height = 162
+      Width = 340
+      Height = 131
       Align = alClient
       DockSite = True
       TabOrder = 1
@@ -5302,4 +5302,10 @@ object MainForm: TMainForm
     Left = 536
     Top = 40
   end
+  object ServiceStatusTimer: TTimer
+    Interval = 250
+    OnTimer = ServiceStatusTimerTick
+    Left = 504
+    Top = 40
+  end
 end
diff --git a/src/sbbs3/ctrl/MainFormUnit.h b/src/sbbs3/ctrl/MainFormUnit.h
index 98dee9221f..ec8458e56b 100644
--- a/src/sbbs3/ctrl/MainFormUnit.h
+++ b/src/sbbs3/ctrl/MainFormUnit.h
@@ -274,6 +274,7 @@ __published:	// IDE-managed Components
 	TMenuItem *SpamBaitList;
 	TMenuItem *SpamBlockList;
 	TTimer *LogTimer;
+	TTimer *ServiceStatusTimer;
 	void __fastcall FileExitMenuItemClick(TObject *Sender);
 	void __fastcall ViewToolbarMenuItemClick(TObject *Sender);
 	void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
@@ -349,6 +350,7 @@ __published:	// IDE-managed Components
 	void __fastcall FileEditJavaScriptClick(TObject *Sender);
 	void __fastcall FileEditConfigFilesClick(TObject *Sender);
 	void __fastcall BBSEditFileClick(TObject *Sender);
+	void __fastcall ServiceStatusTimerTick(TObject *Sender);
 private:	// User declarations
 public:		// User declarations
     __fastcall TMainForm(TComponent* Owner);
-- 
GitLab