Update nsi to include svcctrl.exe, update service code in shell to execute the svchelper rather than relying on having admin rights.

This commit is contained in:
Ron Pedde 2007-03-12 22:45:53 +00:00
parent 7ef720c407
commit 63b49ffd04
5 changed files with 325 additions and 284 deletions

View File

@ -4,6 +4,7 @@
Version="8.00" Version="8.00"
Name="FireflyShell" Name="FireflyShell"
ProjectGUID="{ED38F171-854B-4EA3-B3A0-7681648969FC}" ProjectGUID="{ED38F171-854B-4EA3-B3A0-7681648969FC}"
RootNamespace="FireflyShell"
Keyword="Win32Proj" Keyword="Win32Proj"
> >
<Platforms> <Platforms>

View File

@ -1,217 +1,253 @@
/* /*
*(C) 2006 Roku LLC *(C) 2006 Roku LLC
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License Version 2 as published * under the terms of the GNU General Public License Version 2 as published
* by the Free Software Foundation. * by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, but * This program is distributed in the hope that it will be useful, but
* without any warranty; without even the implied warranty of * without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the GNU General * merchantability or fitness for a particular purpose. See the GNU General
* Public License for more details. * Public License for more details.
* *
* Please read README.txt in the same directory as this source file for * Please read README.txt in the same directory as this source file for
* further license information. * further license information.
*/ */
#include "stdafx.h" #include "stdafx.h"
#include "ServiceControl.h" #include "ServiceControl.h"
#include "DosPath.h"
bool Service::ExecHelper(const TCHAR *szAction) {
PROCESS_INFORMATION pi;
STARTUPINFO si;
CString commandline;
CDosPath path = CDosPath::AppPath();
CDosPath filename(_T("svcctrl.exe"));
DWORD dwResult;
LPTSTR cmd;
filename |= path;
commandline.Format(_T("%s %s \"%s\""),filename.GetPath(),szAction,m_name);
cmd = commandline.GetBuffer(commandline.GetLength() + 1);
ZeroMemory(&si,sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi,sizeof(pi));
BOOL bStarted = CreateProcess(NULL,cmd,NULL,NULL,
FALSE,0,NULL,NULL,&si,&pi);
commandline.ReleaseBuffer();
if(!bStarted)
return false;
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess,&dwResult);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
if(dwResult)
return false;
return true;
}
bool Service::Open(const TCHAR *name) bool Service::Open(const TCHAR *name)
{ {
Close(); Close();
const DWORD ADMIN_ACCESS = SC_MANAGER_ALL_ACCESS; const DWORD USER_ACCESS = SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE | STANDARD_RIGHTS_READ;
const DWORD USER_ACCESS = SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE | STANDARD_RIGHTS_READ;
DWORD dwDesiredAccess = ADMIN_ACCESS; DWORD dwDesiredAccess = USER_ACCESS;
m_sc_manager = ::OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, dwDesiredAccess); m_sc_manager = ::OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, dwDesiredAccess);
if (!m_sc_manager) if (!m_sc_manager)
{ {
dwDesiredAccess = USER_ACCESS; m_can_control = false;
m_sc_manager = ::OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, dwDesiredAccess); }
m_can_control = false; else
} {
else m_can_control = true;
{ }
m_can_control = true;
}
if (m_sc_manager) if (m_sc_manager)
{ {
m_sc_service = ::OpenService(m_sc_manager, name, dwDesiredAccess); m_sc_service = ::OpenService(m_sc_manager, name, dwDesiredAccess);
if (m_sc_service) if (m_sc_service) {
return true; m_name = name;
} return true;
}
}
Close(); Close();
return false; return false;
} }
void Service::Close() void Service::Close()
{ {
if (m_sc_service) if (m_sc_service)
{ {
::CloseServiceHandle(m_sc_service); ::CloseServiceHandle(m_sc_service);
m_sc_service = NULL; m_sc_service = NULL;
} }
if (m_sc_manager) if (m_sc_manager)
{ {
::CloseServiceHandle(m_sc_manager); ::CloseServiceHandle(m_sc_manager);
m_sc_manager = NULL; m_sc_manager = NULL;
} }
} }
bool Service::GetStatus(Status *status) const bool Service::GetStatus(Status *status) const
{ {
if (::QueryServiceStatus(m_sc_service, status)) if (::QueryServiceStatus(m_sc_service, status))
return true; return true;
else else
return false; return false;
} }
bool Service::Start() bool Service::Start()
{ {
if (::StartService(m_sc_service, 0, NULL)) if (ExecHelper(_T("start")))
return true; return true;
else else
return false; return false;
} }
bool Service::StartAndWait() bool Service::StartAndWait()
{ {
if (Start()) if (Start())
{ {
Status status; Status status;
return WaitPending(&status, SERVICE_START_PENDING); return WaitPending(&status, SERVICE_START_PENDING);
} }
else else
return false; return false;
} }
bool Service::Stop() bool Service::Stop()
{ {
Status status; Status status;
if (::ControlService(m_sc_service, SERVICE_CONTROL_STOP, &status)) if (ExecHelper(_T("stop")))
return true; return true;
else else
return false; return false;
} }
bool Service::StopAndWait() bool Service::StopAndWait()
{ {
Status status; Status status;
if (::ControlService(m_sc_service, SERVICE_CONTROL_STOP, &status)) if (Stop())
{ return WaitPending(&status, SERVICE_STOP_PENDING);
return WaitPending(&status, SERVICE_STOP_PENDING); else
} return false;
else
return false;
} }
bool Service::WaitPending(Status *status, DWORD existing_state) bool Service::WaitPending(Status *status, DWORD existing_state)
{ {
ATLTRACE(_T("Enter Service::WaitPending\n")); ATLTRACE(_T("Enter Service::WaitPending\n"));
if (GetStatus(status)) if (GetStatus(status))
{ {
DWORD dwStartTickCount = GetTickCount(); DWORD dwStartTickCount = GetTickCount();
DWORD dwOldCheckPoint = status->dwCheckPoint; DWORD dwOldCheckPoint = status->dwCheckPoint;
while (status->dwCurrentState == existing_state) while (status->dwCurrentState == existing_state)
{ {
ATLTRACE(_T("Service::WaitPending in loop\n")); ATLTRACE(_T("Service::WaitPending in loop\n"));
DWORD dwWaitTime = status->dwWaitHint / 10; DWORD dwWaitTime = status->dwWaitHint / 10;
if (dwWaitTime < 1000) if (dwWaitTime < 1000)
dwWaitTime = 1000; dwWaitTime = 1000;
else if (dwWaitTime > 10000) else if (dwWaitTime > 10000)
dwWaitTime = 10000; dwWaitTime = 10000;
ATLTRACE(_T("Sleeping\n")); ATLTRACE(_T("Sleeping\n"));
::Sleep(dwWaitTime); ::Sleep(dwWaitTime);
if (!GetStatus(status)) if (!GetStatus(status))
{ {
ATLTRACE(_T("Service::WaitPending - Failed to get status\n")); ATLTRACE(_T("Service::WaitPending - Failed to get status\n"));
return false; return false;
} }
// If we haven't changed state yet then check to see that the // If we haven't changed state yet then check to see that the
// service is actually making progress. // service is actually making progress.
if (status->dwCurrentState == existing_state) if (status->dwCurrentState == existing_state)
{ {
if (status->dwCheckPoint != dwOldCheckPoint) if (status->dwCheckPoint != dwOldCheckPoint)
{ {
// The service is making progress // The service is making progress
dwStartTickCount = GetTickCount(); dwStartTickCount = GetTickCount();
dwOldCheckPoint = status->dwCheckPoint; dwOldCheckPoint = status->dwCheckPoint;
} }
else if (GetTickCount() - dwStartTickCount > status->dwWaitHint) else if (GetTickCount() - dwStartTickCount > status->dwWaitHint)
{ {
ATLTRACE(_T("Service::WaitPending - No progress\n")); ATLTRACE(_T("Service::WaitPending - No progress\n"));
/// Hmm. No progress /// Hmm. No progress
return false; return false;
} }
} }
} }
} }
ATLTRACE(_T("Service::WaitPending success\n")); ATLTRACE(_T("Service::WaitPending success\n"));
return true; return true;
} }
CString Service::GetBinaryPath() const CString Service::GetBinaryPath() const
{ {
CString path; CString path;
DWORD bytes_required; DWORD bytes_required;
::QueryServiceConfig(m_sc_service, NULL, 0, &bytes_required); ::QueryServiceConfig(m_sc_service, NULL, 0, &bytes_required);
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{ {
void *buffer = operator new(bytes_required); void *buffer = operator new(bytes_required);
LPQUERY_SERVICE_CONFIG config = reinterpret_cast<LPQUERY_SERVICE_CONFIG>(buffer); LPQUERY_SERVICE_CONFIG config = reinterpret_cast<LPQUERY_SERVICE_CONFIG>(buffer);
if (::QueryServiceConfig(m_sc_service, config, bytes_required, &bytes_required)) if (::QueryServiceConfig(m_sc_service, config, bytes_required, &bytes_required))
{ {
path = config->lpBinaryPathName; path = config->lpBinaryPathName;
} }
delete buffer; delete buffer;
} }
return path; return path;
} }
DWORD Service::GetStartup() const DWORD Service::GetStartup() const
{ {
DWORD result = 0xffffffff; DWORD result = 0xffffffff;
const size_t BUFFER_SIZE = 8192; const size_t BUFFER_SIZE = 8192;
void *buffer = operator new(BUFFER_SIZE); void *buffer = operator new(BUFFER_SIZE);
LPQUERY_SERVICE_CONFIG config = reinterpret_cast<LPQUERY_SERVICE_CONFIG>(buffer); LPQUERY_SERVICE_CONFIG config = reinterpret_cast<LPQUERY_SERVICE_CONFIG>(buffer);
DWORD bytes_required;
if (::QueryServiceConfig(m_sc_service, config, BUFFER_SIZE, &bytes_required))
result = config->dwStartType;
delete buffer; DWORD bytes_required;
return result; if (::QueryServiceConfig(m_sc_service, config, BUFFER_SIZE, &bytes_required))
result = config->dwStartType;
delete buffer;
return result;
} }
bool Service::ConfigureStartup(DWORD startup) bool Service::ConfigureStartup(DWORD startup)
{ {
if (::ChangeServiceConfig(m_sc_service, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) if (::ChangeServiceConfig(m_sc_service, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
return true; return true;
else else
return false; return false;
} }
void ServiceStatusMonitor::Poll(Service *service) void ServiceStatusMonitor::Poll(Service *service)
{ {
Service::Status new_status; Service::Status new_status;
if (service->GetStatus(&new_status)) if (service->GetStatus(&new_status))
{ {
if (!m_service_status.IsValid() || (m_service_status != new_status)) if (!m_service_status.IsValid() || (m_service_status != new_status))
{ {
FireServiceStatus(m_service_status, new_status); FireServiceStatus(m_service_status, new_status);
m_service_status = new_status; m_service_status = new_status;
} }
} }
} }

View File

@ -1,171 +1,174 @@
/* /*
*(C) 2006 Roku LLC *(C) 2006 Roku LLC
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License Version 2 as published * under the terms of the GNU General Public License Version 2 as published
* by the Free Software Foundation. * by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, but * This program is distributed in the hope that it will be useful, but
* without any warranty; without even the implied warranty of * without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the GNU General * merchantability or fitness for a particular purpose. See the GNU General
* Public License for more details. * Public License for more details.
* *
* Please read README.txt in the same directory as this source file for * Please read README.txt in the same directory as this source file for
* further license information. * further license information.
*/ */
#ifndef SERVICECONTROL_H #ifndef SERVICECONTROL_H
#define SERVICECONTROL_H #define SERVICECONTROL_H
class Service class Service
{ {
SC_HANDLE m_sc_manager; SC_HANDLE m_sc_manager;
SC_HANDLE m_sc_service; SC_HANDLE m_sc_service;
bool m_can_control; bool m_can_control;
CString m_name;
public: public:
class Status : public SERVICE_STATUS
{
bool WaitPending();
public: class Status : public SERVICE_STATUS
Status() {
{ bool WaitPending();
SERVICE_STATUS *clearable_this = this;
ZeroMemory(clearable_this, sizeof(SERVICE_STATUS));
}
bool operator==(const Status &r) const public:
{ Status()
const SERVICE_STATUS *lhs = this; {
const SERVICE_STATUS *rhs = &r; SERVICE_STATUS *clearable_this = this;
return memcmp(lhs, rhs, sizeof(SERVICE_STATUS)) == 0; ZeroMemory(clearable_this, sizeof(SERVICE_STATUS));
} }
bool operator!=(const Status &r) const bool operator==(const Status &r) const
{ {
const SERVICE_STATUS *lhs = this; const SERVICE_STATUS *lhs = this;
const SERVICE_STATUS *rhs = &r; const SERVICE_STATUS *rhs = &r;
return memcmp(lhs, rhs, sizeof(SERVICE_STATUS)) != 0; return memcmp(lhs, rhs, sizeof(SERVICE_STATUS)) == 0;
} }
void AssertValid() const bool operator!=(const Status &r) const
{ {
// If we've been written then this shouldn't be zero. const SERVICE_STATUS *lhs = this;
ATLASSERT(dwCurrentState != 0); const SERVICE_STATUS *rhs = &r;
} return memcmp(lhs, rhs, sizeof(SERVICE_STATUS)) != 0;
}
bool IsValid() const void AssertValid() const
{ {
return dwCurrentState != 0; // If we've been written then this shouldn't be zero.
} ATLASSERT(dwCurrentState != 0);
}
bool IsRunning() const bool IsValid() const
{ {
AssertValid(); return dwCurrentState != 0;
return dwCurrentState == SERVICE_RUNNING; }
}
bool IsStopped() const bool IsRunning() const
{ {
AssertValid(); AssertValid();
return dwCurrentState == SERVICE_STOPPED; return dwCurrentState == SERVICE_RUNNING;
} }
bool IsPaused() const bool IsStopped() const
{ {
AssertValid(); AssertValid();
return dwCurrentState == SERVICE_PAUSED; return dwCurrentState == SERVICE_STOPPED;
} }
bool IsPending() const bool IsPaused() const
{ {
AssertValid(); AssertValid();
switch (dwCurrentState) return dwCurrentState == SERVICE_PAUSED;
{ }
case SERVICE_CONTINUE_PENDING:
case SERVICE_PAUSE_PENDING:
case SERVICE_START_PENDING:
case SERVICE_STOP_PENDING:
return true;
default:
return false;
}
}
};
Service() : m_sc_manager(NULL), m_sc_service(NULL), m_can_control(false) {} bool IsPending() const
~Service() { Close(); } {
AssertValid();
switch (dwCurrentState)
{
case SERVICE_CONTINUE_PENDING:
case SERVICE_PAUSE_PENDING:
case SERVICE_START_PENDING:
case SERVICE_STOP_PENDING:
return true;
default:
return false;
}
}
};
bool IsOpen() const Service() : m_sc_manager(NULL), m_sc_service(NULL), m_can_control(false) {}
{ ~Service() { Close(); }
return m_sc_service != NULL;
}
bool Open(const TCHAR *name);
void Close();
bool GetStatus(Status *status) const; bool IsOpen() const
{
return m_sc_service != NULL;
}
bool Open(const TCHAR *name);
void Close();
bool Start(); bool GetStatus(Status *status) const;
bool StartAndWait();
bool Stop(); bool Start();
bool StopAndWait(); bool StartAndWait();
bool WaitPending(Status *status, DWORD existing_state); bool Stop();
bool PollPending(Status *status, DWORD existing_state); bool StopAndWait();
bool WaitPending(Status *status, DWORD existing_state);
bool PollPending(Status *status, DWORD existing_state);
bool CanControl() const bool CanControl() const
{ {
// For the time being - need to deal with running as a user that can't control. // For the time being - need to deal with running as a user that can't control.
return IsOpen() && m_can_control; return IsOpen() && m_can_control;
} }
CString GetBinaryPath() const; CString GetBinaryPath() const;
/// Pass SERVICE_AUTO_START, SERVICE_BOOT_START, SERVICE_DEMAND_START, /// Pass SERVICE_AUTO_START, SERVICE_BOOT_START, SERVICE_DEMAND_START,
/// SERVICE_DISABLED or SERVICE_SYSTEM_START. /// SERVICE_DISABLED or SERVICE_SYSTEM_START.
bool ConfigureStartup(DWORD dwStartup); bool ConfigureStartup(DWORD dwStartup);
DWORD GetStartup() const;
DWORD GetStartup() const; private:
bool ExecHelper(const TCHAR *szAction);
}; };
class ServiceStatusObserver class ServiceStatusObserver
{ {
public: public:
virtual void OnServiceStatus(Service::Status old_status, Service::Status new_status) = 0; virtual void OnServiceStatus(Service::Status old_status, Service::Status new_status) = 0;
}; };
class ServiceStatusMonitor class ServiceStatusMonitor
{ {
Service::Status m_service_status; Service::Status m_service_status;
std::vector<ServiceStatusObserver *> m_service_observers; std::vector<ServiceStatusObserver *> m_service_observers;
void FireServiceStatus(Service::Status old_status, Service::Status new_status) void FireServiceStatus(Service::Status old_status, Service::Status new_status)
{ {
std::vector<ServiceStatusObserver *>::iterator i = m_service_observers.begin(); std::vector<ServiceStatusObserver *>::iterator i = m_service_observers.begin();
while (i != m_service_observers.end()) while (i != m_service_observers.end())
{ {
(*i)->OnServiceStatus(old_status, new_status); (*i)->OnServiceStatus(old_status, new_status);
++i; ++i;
} }
} }
public: public:
void Poll(Service *service); void Poll(Service *service);
void Subscribe(ServiceStatusObserver *obs) void Subscribe(ServiceStatusObserver *obs)
{ {
m_service_observers.push_back(obs); m_service_observers.push_back(obs);
} }
void Unsubscribe(ServiceStatusObserver *obs) void Unsubscribe(ServiceStatusObserver *obs)
{ {
std::vector<ServiceStatusObserver *>::iterator i = std::find(m_service_observers.begin(), m_service_observers.end(), obs); std::vector<ServiceStatusObserver *>::iterator i = std::find(m_service_observers.begin(), m_service_observers.end(), obs);
ATLASSERT(i != m_service_observers.end()); ATLASSERT(i != m_service_observers.end());
if (i != m_service_observers.end()) if (i != m_service_observers.end())
{ {
m_service_observers.erase(i); m_service_observers.erase(i);
} }
} }
}; };
#endif // SERVICECONTROL_H #endif // SERVICECONTROL_H

View File

@ -16,8 +16,8 @@
!define MTDROOT "..\.." !define MTDROOT "..\.."
!define MTD_SOURCE "${MTDROOT}\win32\Release" !define MTD_SOURCE "${MTDROOT}\win32\Release"
!define DLL_SOURCE "${PROJROOT}\win32\dll"
!define CONFIG_SOURCE "${MTDROOT}\win32\FireflyShell\Release" !define CONFIG_SOURCE "${MTDROOT}\win32\FireflyShell\Release"
!define DLL_SOURCE "${PROJROOT}\win32\dll"
!define ADMIN_ROOT "${MTDROOT}\admin-root" !define ADMIN_ROOT "${MTDROOT}\admin-root"
!define REDIST_SOURCE "${PROJROOT}\win32\redist" !define REDIST_SOURCE "${PROJROOT}\win32\redist"
@ -171,6 +171,7 @@ NoProgramItems:
File "${CONFIG_SOURCE}\FireflyShell-11.dll" File "${CONFIG_SOURCE}\FireflyShell-11.dll"
File "${CONFIG_SOURCE}\FireflyShell-10.dll" File "${CONFIG_SOURCE}\FireflyShell-10.dll"
File "${CONFIG_SOURCE}\..\FireflyShell.exe.manifest" File "${CONFIG_SOURCE}\..\FireflyShell.exe.manifest"
File "${CONFIG_SOURCE}\svcctrl.exe"
File "${DLL_SOURCE}\gnu_regex.dll" File "${DLL_SOURCE}\gnu_regex.dll"
File "${DLL_SOURCE}\pthreadVC2.dll" File "${DLL_SOURCE}\pthreadVC2.dll"
File "${DLL_SOURCE}\sqlite.dll" File "${DLL_SOURCE}\sqlite.dll"

View File

@ -17,7 +17,7 @@
<Configurations> <Configurations>
<Configuration <Configuration
Name="Debug|Win32" Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)" OutputDirectory="$(SolutionDir)FireflyShell\$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)" IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1" ConfigurationType="1"
CharacterSet="2" CharacterSet="2"
@ -92,7 +92,7 @@
</Configuration> </Configuration>
<Configuration <Configuration
Name="Release|Win32" Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)" OutputDirectory="$(SolutionDir)FireflyShell\$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)" IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1" ConfigurationType="1"
CharacterSet="2" CharacterSet="2"