diff --git a/.gitignore b/.gitignore index 259148f..a90c05d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,8 @@ *.exe *.out *.app + +/.tmp +/.vs +/_out +/packages diff --git a/ClassicParentalControl.sln b/ClassicParentalControl.sln new file mode 100644 index 0000000..276f50a --- /dev/null +++ b/ClassicParentalControl.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.779 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LogonHoursService", "LogonHoursService\LogonHoursService.vcxproj", "{D6E78055-7BC4-4301-BDFC-C4DDA35E959C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D6E78055-7BC4-4301-BDFC-C4DDA35E959C}.Debug|x86.ActiveCfg = Debug|Win32 + {D6E78055-7BC4-4301-BDFC-C4DDA35E959C}.Debug|x86.Build.0 = Debug|Win32 + {D6E78055-7BC4-4301-BDFC-C4DDA35E959C}.Release|x86.ActiveCfg = Release|Win32 + {D6E78055-7BC4-4301-BDFC-C4DDA35E959C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BE80D131-F0B2-4139-BE24-81BB40DD2B89} + EndGlobalSection +EndGlobal diff --git a/LogonHoursService.exe.log4cpp b/LogonHoursService.exe.log4cpp new file mode 100644 index 0000000..19118e7 --- /dev/null +++ b/LogonHoursService.exe.log4cpp @@ -0,0 +1,5 @@ +# log4cpp.properties +# Put this file into the same dir where EXE resides + +# By default Release build show INFO and more important levels +log4cpp.rootCategory=DEBUG diff --git a/LogonHoursService/.gitignore b/LogonHoursService/.gitignore new file mode 100644 index 0000000..d7a4d4d --- /dev/null +++ b/LogonHoursService/.gitignore @@ -0,0 +1 @@ +/*.user diff --git a/LogonHoursService/Logger.h b/LogonHoursService/Logger.h new file mode 100644 index 0000000..6128d78 --- /dev/null +++ b/LogonHoursService/Logger.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +// See also https://github.com/msly/codemanage/blob/master/A%20useful%20log4cpp%20example.cpp + +#define CODE_LOCATION __FILE__ + +// DEBUG < INFO < NOTICE < WARN < ERROR < CRIT < ALERT < FATAL = EMERG + +#define LOG_EMERG(__this) Logger(__this) << log4cpp::Priority::EMERG //<< CODE_LOCATION +#define LOG_ALERT(__this) Logger(__this) << log4cpp::Priority::ALERT //<< CODE_LOCATION +#define LOG_CRIT(__this) Logger(__this) << log4cpp::Priority::CRIT //<< CODE_LOCATION +#define LOG_ERROR(__this) Logger(__this) << log4cpp::Priority::ERROR //<< CODE_LOCATION +#define LOG_WARN(__this) Logger(__this) << log4cpp::Priority::WARN //<< CODE_LOCATION +#define LOG_NOTICE(__this) Logger(__this) << log4cpp::Priority::NOTICE //<< CODE_LOCATION +#define LOG_INFO(__this) Logger(__this) << log4cpp::Priority::INFO //<< CODE_LOCATION +#define LOG_DEBUG(__this) Logger(__this) << log4cpp::Priority::DEBUG //<< CODE_LOCATION + +template +struct LoggerTraits +{ + static const char* Category() + { + return typeid(T).name(); + } +}; + +template +log4cpp::Category& Logger() +{ + static log4cpp::Category& logger = log4cpp::Category::getInstance(LoggerTraits::Category()); + return logger; +} + +template +log4cpp::Category& Logger(T* this_) +{ + return Logger(); +} + +inline log4cpp::Category& Logger(const char* func) +{ + return log4cpp::Category::getInstance(func); +} + +inline log4cpp::Category& Logger(const void* p = NULL) +{ + return log4cpp::Category::getRoot(); +} diff --git a/LogonHoursService/LogonHoursService.rc b/LogonHoursService/LogonHoursService.rc new file mode 100644 index 0000000..fe28c32 --- /dev/null +++ b/LogonHoursService/LogonHoursService.rc @@ -0,0 +1,115 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include + +#include "config.h" +#include "version.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Neutral resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include \r\n" + "\r\n" + "#include ""config.h""\r\n" + "#include ""version.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Neutral resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (United Kingdom) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_REV,0 + PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_REV,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000904b0" + BEGIN + VALUE "CompanyName", "KAnVasSOFT" + VALUE "FileDescription", SERVICE_DESC + VALUE "FileVersion", VERSION_STRING + VALUE "InternalName", SERVICE_NAME ".exe" + VALUE "LegalCopyright", "Copyright (C) 2021 Anton-V-K" + VALUE "OriginalFilename", SERVICE_NAME ".exe" + VALUE "ProductName", SERVICE_FULL_NAME + VALUE "ProductVersion", VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x9, 1200 + END +END + +#endif // English (United Kingdom) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/LogonHoursService/LogonHoursService.vcxproj b/LogonHoursService/LogonHoursService.vcxproj new file mode 100644 index 0000000..caabd3c --- /dev/null +++ b/LogonHoursService/LogonHoursService.vcxproj @@ -0,0 +1,109 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + LogonHoursService + {D6E78055-7BC4-4301-BDFC-C4DDA35E959C} + SampleService + Win32Proj + + + + + Application + Unicode + true + + + Application + Unicode + + + + + + + + + + + + + <_ProjectFileVersion>15.0.28307.799 + + + true + + + false + + + + _CONSOLE;WIN32;_DEBUG;%(PreprocessorDefinitions) + EnableFastChecks + Use + + + MachineX86 + Console + + + + + _CONSOLE;WIN32;NDEBUG;%(PreprocessorDefinitions) + true + Use + true + + + true + true + MachineX86 + Console + + + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/LogonHoursService/LogonHoursService.vcxproj.filters b/LogonHoursService/LogonHoursService.vcxproj.filters new file mode 100644 index 0000000..bf9d477 --- /dev/null +++ b/LogonHoursService/LogonHoursService.vcxproj.filters @@ -0,0 +1,65 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + + + + Resource Files + + + \ No newline at end of file diff --git a/LogonHoursService/ServiceMain.cpp b/LogonHoursService/ServiceMain.cpp new file mode 100644 index 0000000..8208c89 --- /dev/null +++ b/LogonHoursService/ServiceMain.cpp @@ -0,0 +1,145 @@ +#include "stdafx.h" + +#include "config.h" +#include "Logger.h" +#include "WorkerThread.h" + +SERVICE_STATUS g_ServiceStatus = { 0 }; +SERVICE_STATUS_HANDLE g_StatusHandle = NULL; + + +VOID WINAPI ServiceCtrlHandler(DWORD CtrlCode) +{ + LOG_DEBUG(__func__) << "Entry"; + + switch (CtrlCode) + { + case SERVICE_CONTROL_STOP: + + LOG_DEBUG(__func__) << "SERVICE_CONTROL_STOP Request"; + + if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING) + break; + + /* + * Perform tasks neccesary to stop the service here + */ + + g_ServiceStatus.dwControlsAccepted = 0; + g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + g_ServiceStatus.dwWin32ExitCode = 0; + g_ServiceStatus.dwCheckPoint = 4; + + if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) + { + LOG_ERROR(__func__) << "SetServiceStatus returned error"; + } + + // This will signal the worker thread to start shutting down + SetEvent(g_WorkerData.hStopEvent); + + break; + + default: + break; + } + + LOG_DEBUG(__func__) << "Exit"; +} + +VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv) +{ + DWORD Status = E_FAIL; + + LOG_DEBUG(__func__) << "Entry"; + + g_StatusHandle = RegisterServiceCtrlHandler(_T(SERVICE_NAME), ServiceCtrlHandler); + + if (g_StatusHandle == NULL) + { + LOG_ERROR(__func__) << "RegisterServiceCtrlHandler failed"; + } + else + { + // Tell the service controller we are starting + ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus)); + g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + g_ServiceStatus.dwControlsAccepted = 0; + g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; + g_ServiceStatus.dwWin32ExitCode = 0; + g_ServiceStatus.dwServiceSpecificExitCode = 0; + g_ServiceStatus.dwCheckPoint = 0; + + if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) + { + LOG_ERROR(__func__) << "SetServiceStatus(SERVICE_START_PENDING) failed"; + } + + /* + * Perform tasks neccesary to start the service here + */ + LOG_DEBUG(__func__) << "Performing Service Start Operations"; + + // Create stop event to wait on later. + g_WorkerData.hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (g_WorkerData.hStopEvent == NULL) + { + LOG_ERROR(__func__) << "CreateEvent() for hStopEvent failed"; + + g_ServiceStatus.dwControlsAccepted = 0; + g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; + g_ServiceStatus.dwWin32ExitCode = GetLastError(); + g_ServiceStatus.dwCheckPoint = 1; + + if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) + { + LOG_ERROR(__func__) << "SetServiceStatus(SERVICE_STOPPED) failed"; + } + } + else + { + + // Tell the service controller we are started + g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; + g_ServiceStatus.dwWin32ExitCode = 0; + g_ServiceStatus.dwCheckPoint = 0; + + if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) + { + LOG_ERROR(__func__) << "SetServiceStatus(SERVICE_RUNNING) failed"; + } + + // Start the thread that will perform the main task of the service + const HANDLE hThread = CreateThread(NULL, 0, WorkerThread, &g_WorkerData, 0, NULL); + if (hThread) + { + LOG_DEBUG(__func__) << "Waiting for Worker Thread to finish"; + + // Wait until our worker thread exits effectively signaling that the service needs to stop + WaitForSingleObject(hThread, INFINITE); + + LOG_DEBUG(__func__) << "Worker Thread is over"; + } + else + LOG_ERROR(__func__) << "CreateThread failed!"; + + // Perform any cleanup tasks + LOG_DEBUG(__func__) << "Performing Cleanup Operations"; + + CloseHandle(g_WorkerData.hStopEvent); + + g_ServiceStatus.dwControlsAccepted = 0; + g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; + g_ServiceStatus.dwWin32ExitCode = 0; + g_ServiceStatus.dwCheckPoint = 3; + + if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) + { + LOG_DEBUG(__func__) << "SetServiceStatus(SERVICE_STOPPED) failed"; + } + } + } + + LOG_DEBUG(__func__) << "Exit"; +} diff --git a/LogonHoursService/ServiceMain.h b/LogonHoursService/ServiceMain.h new file mode 100644 index 0000000..4be8813 --- /dev/null +++ b/LogonHoursService/ServiceMain.h @@ -0,0 +1,3 @@ +#pragma once + +VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv); diff --git a/LogonHoursService/ServiceManager.cpp b/LogonHoursService/ServiceManager.cpp new file mode 100644 index 0000000..6410212 --- /dev/null +++ b/LogonHoursService/ServiceManager.cpp @@ -0,0 +1,171 @@ +////////////////////////////////////////////////////////////////////// +// Author :- Nish (c) https://www.codeproject.com/Articles/2312/CServiceManager +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ServiceManager.h" + +////////////////////////////////////////////////////////////////////// +// SERVICEINFO +////////////////////////////////////////////////////////////////////// + +SERVICEINFO::SERVICEINFO() +{ + bAutoStart = false; + lpBinaryPathName = NULL; + lpServiceName = NULL; + lpDisplayName = NULL; +} + +CServiceManager::CServiceManager() +{ + m_scm = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS); +} + +CServiceManager::~CServiceManager() +{ + CloseServiceHandle(m_scm); +} + +bool CServiceManager::Create(const SERVICEINFO& info) +{ + bool ok = false; + if (info.lpServiceName && + info.lpDisplayName && + info.lpBinaryPathName) + { + const SC_HANDLE hService = CreateService(m_scm, info.lpServiceName, info.lpDisplayName, + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, + info.bAutoStart ? SERVICE_AUTO_START : SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, + info.lpBinaryPathName, + 0, 0, 0, 0, 0); + if (hService) + { + ok = true; + CloseServiceHandle(hService); + } + } + return ok; +} + +bool CServiceManager::Delete(LPCTSTR serviceName) +{ + bool ok = false; + if (serviceName) + { + const SC_HANDLE hService = OpenService(m_scm, serviceName, SERVICE_ALL_ACCESS); + if (hService) + { + if (DeleteService(hService)) + { + ok = true; + } + CloseServiceHandle(hService); + } + } + return ok; + +} + +bool CServiceManager::Start(LPCTSTR serviceName) +{ + bool ok = false; + if (serviceName) + { + const SC_HANDLE hService = OpenService(m_scm, serviceName, SERVICE_ALL_ACCESS); + if (hService) + { + if (StartService(hService, 0, NULL)) + { + ok = true; + } + CloseServiceHandle(hService); + } + } + return ok; +} + +bool CServiceManager::Stop(LPCTSTR serviceName) +{ + bool ok = false; + if (serviceName) + { + const SC_HANDLE hService = OpenService(m_scm, serviceName, SERVICE_ALL_ACCESS); + if (hService) + { + SERVICE_STATUS m_SERVICE_STATUS; + if (ControlService(hService, SERVICE_CONTROL_STOP, &m_SERVICE_STATUS)) + { + ok = true; + } + CloseServiceHandle(hService); + } + } + return ok; + +} + +bool CServiceManager::Pause(LPCTSTR serviceName) +{ + bool ok = false; + if (serviceName) + { + const SC_HANDLE hService = OpenService(m_scm, serviceName, SERVICE_ALL_ACCESS); + if (hService) + { + SERVICE_STATUS m_SERVICE_STATUS; + + if (ControlService(hService, + SERVICE_CONTROL_PAUSE, + &m_SERVICE_STATUS)) + { + ok = true; + } + CloseServiceHandle(hService); + } + } + return ok; + +} + +bool CServiceManager::Continue(LPCTSTR serviceName) +{ + bool ok = false; + if (serviceName) + { + const SC_HANDLE hService = OpenService(m_scm, serviceName, SERVICE_ALL_ACCESS); + if (hService) + { + SERVICE_STATUS m_SERVICE_STATUS; + if (ControlService(hService, SERVICE_CONTROL_CONTINUE, &m_SERVICE_STATUS)) + { + ok = true; + } + CloseServiceHandle(hService); + } + } + return ok; +} + +bool CServiceManager::SetDescription(LPCTSTR serviceName, LPCTSTR description) +{ + bool ok = false; + if (serviceName) + { + const SC_HANDLE hService = OpenService(m_scm, serviceName, SERVICE_CHANGE_CONFIG); + if (hService) + { + SERVICE_DESCRIPTION desc; + desc.lpDescription = _tcsdup(description); + if (ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &desc)) + { + ok = true; + } + free(desc.lpDescription); + CloseServiceHandle(hService); + } + } + return ok; +} diff --git a/LogonHoursService/ServiceManager.h b/LogonHoursService/ServiceManager.h new file mode 100644 index 0000000..764a934 --- /dev/null +++ b/LogonHoursService/ServiceManager.h @@ -0,0 +1,43 @@ +#if !defined(ServiceManager_h_) +#define ServiceManager_h_ +////////////////////////////////////////////////////////////////////// +// Author :- Nish +////////////////////////////////////////////////////////////////////// + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +struct SERVICEINFO +{ +public: + bool bAutoStart; + LPCTSTR lpBinaryPathName; + LPCTSTR lpDisplayName; + LPCTSTR lpServiceName; + + SERVICEINFO(); +}; + +class CServiceManager +{ +public: + CServiceManager(); + ~CServiceManager(); + + bool Create(const SERVICEINFO& info); + bool Delete(LPCTSTR serviceName); + + bool Continue(LPCTSTR serviceName); + bool Pause(LPCTSTR serviceName); + bool Start(LPCTSTR serviceName); + bool Stop(LPCTSTR serviceName); + + bool SetDescription(LPCTSTR serviceName, LPCTSTR description); + +private: + SC_HANDLE m_scm; +}; + + +#endif diff --git a/LogonHoursService/WorkerThread.cpp b/LogonHoursService/WorkerThread.cpp new file mode 100644 index 0000000..c6bf1df --- /dev/null +++ b/LogonHoursService/WorkerThread.cpp @@ -0,0 +1,228 @@ +#include "stdafx.h" + +#include "config.h" +#include "Logger.h" +#include "WorkerThread.h" + +#pragma comment(lib, "Netapi32.lib") // NetUserGetInfo(), etc. +//#pragma comment(lib, "Secur32.lib") // LsaEnumerateLogonSessions(), etc. +#pragma comment(lib, "Wtsapi32.lib") // WTSEnumerateSessions(), etc. + +WorkerData g_WorkerData; + +enum EDayOfWeek +{ + Sun, Mon, Tue, Wed, Thu, Fri, Sat, +}; + +class LogonHours +{ +public: + LogonHours(PBYTE usri2_logon_hours) + { + Init(usri2_logon_hours); + } + void Init(PBYTE usri2_logon_hours) + { + m_all = true; + int day = Sun; + int hour = 0; // 0:00 + for (int p = 0; p < 21; ++p) + { + const BYTE chunk = usri2_logon_hours[p]; + BYTE mask = 0b00000001; + for (int bit = 0; bit < 8; ++bit) + { + m_data[day][hour] = (chunk & mask) != 0; + if (!m_data[day][hour]) + m_all = false; + ++hour; + mask <<= 1; + if (hour >= 24) + { + ++day; + hour = 0; + } + } + } + } + + bool All() const { return m_all; } + bool Allowed(EDayOfWeek day, WORD hour) const + { + if (Sun <= day && day <= Sat && 0 <= hour && hour <= 23) + return m_data[day][hour]; + return false; + } + // @return seconds left (for the given time) until the allowed logon period expires, or -1 if no restrictions are set + // @note 0 (zero) is returned if logon isn't allowed during given time + long SecondsLeft(EDayOfWeek day, WORD hour, WORD minute, WORD second) const + { + if (All()) + return -1; + if (!Allowed(day, hour)) + return 0; + long left = (60 - second - 1) + 60 * (60 - minute - 1); // the rest of current hour is allowed + // TODO other hours/days + return left; + } + +private: + bool m_all; // true if no logon restrictions + bool m_data[7][24]; +}; + +WorkerData::WorkerData() +{ + hStopEvent = NULL; +} + +DWORD WINAPI WorkerThread(LPVOID lpData) +{ + USES_CONVERSION; + + LOG_DEBUG(__func__) << "Entry"; + + WorkerData* const pData = reinterpret_cast(lpData); +#ifdef _DEBUG + enum { TIMEOUT = 5000 }; +#else + enum { TIMEOUT = 10000 }; +#endif + // Periodically check if the service has been requested to stop + while (pData->hStopEvent && WaitForSingleObject(pData->hStopEvent, TIMEOUT) != WAIT_OBJECT_0) + { + struct Session + { + DWORD id; + std::wstring user; + }; + std::vector sessions; + PWTS_SESSION_INFO pSessionInfo; + DWORD count; + LOG_DEBUG(__func__) << "Starting WTSEnumerateSessions"; + if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &count)) + { + for (DWORD i = 0; i < count; ++i) + { + const auto session_id = pSessionInfo[i].SessionId; + LPWSTR pBuffer; + DWORD bytes; + if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, session_id, WTSConnectState, &pBuffer, &bytes)) + continue; + const WTS_CONNECTSTATE_CLASS session_state = *reinterpret_cast(pBuffer); + WTSFreeMemory(pBuffer); + if (session_state != WTSActive) + continue; + if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, session_id, WTSUserName, &pBuffer, &bytes)) + { + if (wcslen(pBuffer)) + { + const std::wstring username(pBuffer); + LOG_DEBUG(__func__) << "WTSQuerySessionInformation: active session " << session_id + << ", user '" << T2A(username.c_str()) << '\''; + sessions.push_back({ session_id, username }); + } + WTSFreeMemory(pBuffer); + } + } + WTSFreeMemory(pSessionInfo); + } + LOG_DEBUG(__func__) << sessions.size() << " active session(s) detected"; + +#if 0 + ULONG SessionsCount; + PLUID SessionsList; + const NTSTATUS status = LsaEnumerateLogonSessions(&SessionsCount, &SessionsList); + if (status == 0) // SCESTATUS_SUCCESS // STATUS_SUCCESS | + { + std::set users; + for (ULONG i = 0; i < SessionsCount; ++i) + { + PSECURITY_LOGON_SESSION_DATA pSessionData; + if (LsaGetLogonSessionData(&SessionsList[i], &pSessionData) == 0) // STATUS_SUCCESS + { + if (Interactive == pSessionData->LogonType) + { + users.insert(pSessionData->UserName.Buffer); + } + LsaFreeReturnBuffer(pSessionData); + } + } + + for (const auto username: users) + { +#else + for (const auto session: sessions) + { + const auto& wusername = session.user; +#endif + + LPBYTE bufptr; + // Refer to https://docs.microsoft.com/en-us/windows/win32/api/lmaccess/nf-lmaccess-netusergetinfo + const NET_API_STATUS result = NetUserGetInfo(NULL, wusername.c_str(), 2, &bufptr); + if (result == NERR_Success) + { + const USER_INFO_2* const userinfo2 = reinterpret_cast(bufptr); + const LogonHours hours(userinfo2->usri2_logon_hours); + NetApiBufferFree(bufptr); + + const char* const username = T2A(session.user.c_str()); + if (!hours.All()) + { + LOG_DEBUG(__func__) << "checking logon hours for session " << session.id << " '" << username << "'"; + + SYSTEMTIME systime; GetSystemTime(&systime); // UTC/GMT + + const auto seconds_left = hours.SecondsLeft(EDayOfWeek(systime.wDayOfWeek), systime.wHour, systime.wMinute, systime.wSecond); + if (0 <= seconds_left && seconds_left <= 60) + { + LOG_INFO(__func__) << seconds_left << " seconds left for session " << session.id << " '" << username << "'"; + if (true) // always notify user? + { + LOG_DEBUG(__func__) << "WTSSendMessage for sesssion " << session.id; + TCHAR message[] = _T("Your session will end in 1 minute!"); + DWORD response; + if (!WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, session.id + , _T(SERVICE_FULL_NAME), strlen(SERVICE_FULL_NAME) * sizeof(TCHAR) // in bytes + , message, _tcslen(message) * sizeof(message[0]) // in bytes + , MB_OK | MB_ICONWARNING, 60, &response, FALSE)) + { + LOG_ERROR(__func__) << "WTSSendMessage failed to send message into session " << session.id; + } + Sleep(60 * 1000); + } + else + { + Sleep(seconds_left * 1000); + } + + LOG_DEBUG(__func__) << "WTSDisconnectSession for sesssion " << session.id; + if (WTSDisconnectSession(WTS_CURRENT_SERVER_HANDLE, session.id, FALSE)) + { + LOG_INFO(__func__) << "Session " << session.id << " '" << username << "' disconnected!"; + } + else + { + const auto err = GetLastError(); + LOG_ERROR(__func__) << "WTSDisconnectSession for sesssion " << session.id << " failed with error " << err; + } + } + } + } + else + { + + } +#if 0 + } + LsaFreeReturnBuffer(SessionsList); +#else + } +#endif + } + + LOG_DEBUG(__func__) << "Exit"; + + return ERROR_SUCCESS; +} diff --git a/LogonHoursService/WorkerThread.h b/LogonHoursService/WorkerThread.h new file mode 100644 index 0000000..16b7412 --- /dev/null +++ b/LogonHoursService/WorkerThread.h @@ -0,0 +1,12 @@ +#pragma once + +struct WorkerData +{ + HANDLE hStopEvent; + + WorkerData(); +}; + +extern WorkerData g_WorkerData; + +DWORD WINAPI WorkerThread(LPVOID lpData); diff --git a/LogonHoursService/config.h b/LogonHoursService/config.h new file mode 100644 index 0000000..7877812 --- /dev/null +++ b/LogonHoursService/config.h @@ -0,0 +1,10 @@ +#pragma once + +#define CMD_LINE_INSTALL _T("--install") +// The option to start as service +#define CMD_LINE_SERVICE _T("--service") +#define CMD_LINE_UNINSTALL _T("--uninstall") + +#define SERVICE_NAME "LogonHoursService" +#define SERVICE_FULL_NAME "Logon Hours Service" +#define SERVICE_DESC "Logon Hours Service for Classic Parental Control" diff --git a/LogonHoursService/main.cpp b/LogonHoursService/main.cpp new file mode 100644 index 0000000..d01fb0f --- /dev/null +++ b/LogonHoursService/main.cpp @@ -0,0 +1,228 @@ +#include "stdafx.h" + +#include "config.h" +#include "Logger.h" +#include "ServiceMain.h" +#include "ServiceManager.h" +#include "WorkerThread.h" + +#include "version.h" + +#pragma comment(lib, "ws2_32.lib") // Needed for log4cpp if PropertyConfigurator is used + +void Main() +{ + LOG_DEBUG(__func__) << "Entry"; + + // Create stop event to wait on later. + g_WorkerData.hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (g_WorkerData.hStopEvent == NULL) + { + LOG_ERROR(__func__) << "CreateEvent() for hStopEvent failed"; + } + else + { + // Start the thread that will perform the main task + const HANDLE hThread = CreateThread(NULL, 0, WorkerThread, &g_WorkerData, 0, NULL); + if (hThread) + { + LOG_DEBUG(__func__) << "Waiting for Worker Thread to finish"; + + // Wait until our worker thread exits + WaitForSingleObject(hThread, INFINITE); + + LOG_DEBUG(__func__) << "Worker Thread is over"; + + // Perform any cleanup tasks + LOG_DEBUG(__func__) << "Performing Cleanup Operations"; + } + else + LOG_ERROR(__func__) << "CreateThread failed!"; + + CloseHandle(g_WorkerData.hStopEvent); + } + + LOG_DEBUG(__func__) << "Exit"; +} + +#ifdef _CONSOLE +int _tmain(int argc, TCHAR* argv[]) +#else +int APIENTRY _tWinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) +#endif +{ + OutputDebugString(_T(SERVICE_NAME) _T(": Start\n")); + + log4cpp::Category& root = log4cpp::Category::getRoot(); + + //////////////////////////////////////// + // Configigure log4cpp + //////////////////////////////////////// + char config_path[_MAX_PATH] = { 0 }; + GetModuleFileNameA(NULL, config_path, _countof(config_path)); + strcat_s(config_path, ".log4cpp"); + try + { + log4cpp::PropertyConfigurator::configure(config_path); + } + catch (const log4cpp::ConfigureFailure& /*ex*/) + { + config_path[0] = 0; +/* +#ifdef _CONSOLE + std::cerr + << ex.what() + << " [log4cpp::ConfigureFailure catched] while reading " + << file_log4cpp_init + << std::endl; +#endif +*/ + + // Default configuration for log4cpp +#ifdef _DEBUG + root.setPriority(log4cpp::Priority::DEBUG); +#else + root.setPriority(log4cpp::Priority::INFO); +#endif + + } + + const log4cpp::AppenderSet& set = root.getAllAppenders(); + if (set.empty()) // if no appenders are specifiec in the config file ... + { + char log_path[MAX_PATH]; + ExpandEnvironmentStringsA("%TEMP%\\" SERVICE_NAME ".log", log_path, _countof(log_path)); +#if 1 + if (log4cpp::Appender* const appender = new log4cpp::RollingFileAppender("logfile", log_path)) +#else + if (log4cpp::Appender* const appender = new log4cpp::FileAppender("logfile", log_path)) +#endif + { + log4cpp::PatternLayout* const layout = new log4cpp::PatternLayout(); + // Refer to http://www.cplusplus.com/reference/ctime/strftime/ for format specification + layout->setConversionPattern("%d{%Y.%m.%d %H:%M:%S,%l} [%p] [%t] %c: %m%n"); + appender->setLayout(layout); + root.addAppender(appender); + } + if (log4cpp::Appender* const appender = new log4cpp::Win32DebugAppender("debugger")) + { + log4cpp::PatternLayout* const layout = new log4cpp::PatternLayout(); + // Refer to http://www.cplusplus.com/reference/ctime/strftime/ for format specification + layout->setConversionPattern("%d{%H:%M:%S,%l} [%p] [%t] %c: %m%n"); + appender->setLayout(layout); + root.addAppender(appender); + } + } + + LOG_INFO(__func__) << "========================================"; + LOG_INFO(__func__) << "Log initialized successfully"; + if (*config_path) + LOG_INFO(__func__) << "Loaded " << config_path; + +#ifndef _CONSOLE + LOG_INFO(__func__) << "Length of command line: " << _tcslen(lpCmdLine); +#endif + + LOG_INFO(__func__) << "Version: " << VERSION_STRING; + LOG_INFO(__func__) << "Build : " << __DATE__ << ' ' << __TIME__; +#if defined(_MSC_FULL_VER) + LOG_INFO(__func__) << "_MSC_FULL_VER: " + << _MSC_FULL_VER / 10000000 << '.' + << (_MSC_FULL_VER % 10000000) / 100000 << '.' + << _MSC_FULL_VER % 100000; +#elif defined(_MSC_VER) + LOG_INFO(__func__) << "_MSC_VER: " + << _MSC_VER / 100 << '.' + << _MSC_VER % 100; +#endif + + +#ifdef _CONSOLE + if (argc > 1) + { +#endif + // Let's handle the command line +#ifdef _CONSOLE + if (_tcsicmp(argv[1], CMD_LINE_INSTALL) == 0) +#else + if (_tcsstr(lpCmdLine, CMD_LINE_INSTALL)) +#endif + { + CServiceManager scm; + SERVICEINFO info; + wchar_t path[_MAX_PATH] = { 0 }; + GetModuleFileNameW(NULL, path, _countof(path)); + _tcscat_s(path, _T(" ")); + _tcscat_s(path, CMD_LINE_SERVICE); + info.lpBinaryPathName = path; + info.lpDisplayName = _T(SERVICE_FULL_NAME); + info.lpServiceName = _T(SERVICE_NAME); + if (scm.Create(info)) + { +#ifdef _CONSOLE + _tprintf(_T("Service %s installed successfully!"), _T(SERVICE_NAME)); +#endif +#ifdef SERVICE_DESC + if (_tcslen(_T(SERVICE_DESC))) + scm.SetDescription(_T(SERVICE_NAME), _T(SERVICE_DESC)); +#endif + } + else + { +#ifdef _CONSOLE + _tprintf(_T("ERROR: Cannot install service %s!"), _T(SERVICE_NAME)); +#endif + } + } +#ifdef _CONSOLE + else if (_tcsicmp(argv[1], CMD_LINE_SERVICE) == 0) +#else + else if (_tcsstr(lpCmdLine, CMD_LINE_SERVICE)) +#endif + { + static const SERVICE_TABLE_ENTRY ServiceTable[] = + { + { _T(SERVICE_NAME), ServiceMain }, + { NULL, NULL} + }; + + LOG_DEBUG(__func__) << "Calling StartServiceCtrlDispatcher..."; + if (StartServiceCtrlDispatcher(ServiceTable) == FALSE) + { + const DWORD dwLastErr = GetLastError(); + LOG_EMERG(__func__) << "StartServiceCtrlDispatcher returned error " << std::hex << dwLastErr; + return dwLastErr; + } + } +#ifdef _CONSOLE + else if (_tcsicmp(argv[1], CMD_LINE_UNINSTALL) == 0) +#else + else if (_tcsstr(lpCmdLine, CMD_LINE_UNINSTALL)) +#endif + { + CServiceManager scm; + if (scm.Delete(_T(SERVICE_NAME))) + { +#ifdef _CONSOLE + _tprintf(_T("Service %s uninstalled successfully!"), _T(SERVICE_NAME)); +#endif + } + else + { +#ifdef _CONSOLE + _tprintf(_T("ERROR: Cannot uninstall service %s!"), _T(SERVICE_NAME)); +#endif + } + } +#ifdef _CONSOLE + } +#endif + else + Main(); + + LOG_DEBUG(__func__) << "Exit"; + return 0; +} diff --git a/LogonHoursService/packages.config b/LogonHoursService/packages.config new file mode 100644 index 0000000..717db25 --- /dev/null +++ b/LogonHoursService/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/LogonHoursService/resource.h b/LogonHoursService/resource.h new file mode 100644 index 0000000..2484aaf --- /dev/null +++ b/LogonHoursService/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by LogonHoursService.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/LogonHoursService/stdafx.cpp b/LogonHoursService/stdafx.cpp new file mode 100644 index 0000000..fd4f341 --- /dev/null +++ b/LogonHoursService/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/LogonHoursService/stdafx.h b/LogonHoursService/stdafx.h new file mode 100644 index 0000000..f97836a --- /dev/null +++ b/LogonHoursService/stdafx.h @@ -0,0 +1,41 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include + +#include // NetUserGetInfo(), etc. +//#include // LsaEnumerateLogonSessions(), etc. (desktop only) +#include +#include +#include // WTSEnumerateSessions(), etc. + +//#include +#include + +#include // T2A() and other string convesions macros + +//#include +#include +#include +#include +#include +//#include +#include +#include +#include + +// reference additional headers your program requires here + +#ifdef _UNICODE +typedef std::wstring tstring; +#else +typedef std::string tstring; +#endif diff --git a/LogonHoursService/targetver.h b/LogonHoursService/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/LogonHoursService/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/LogonHoursService/version.h b/LogonHoursService/version.h new file mode 100644 index 0000000..296d0b9 --- /dev/null +++ b/LogonHoursService/version.h @@ -0,0 +1,16 @@ +#ifndef CPC_version_h_ +#define CPC_version_h_ + +#ifdef _DEBUG +# define VERSION_CONFIG " Debug" +#else +# define VERSION_CONFIG +#endif + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 0 +#define VERSION_REV 0 + +#define VERSION_STRING "1.0.0 Alpha" VERSION_CONFIG + +#endif diff --git a/_props/Cxx.props b/_props/Cxx.props new file mode 100644 index 0000000..b0b1095 --- /dev/null +++ b/_props/Cxx.props @@ -0,0 +1,31 @@ + + + + + + + Unicode + + + + EnableFastChecks + + Guard + ProgramDatabase + Disabled + MultiThreadedDebugDLL + + + + + + ProgramDatabase + MultiThreadedDLL + + + + + Level3 + + + diff --git a/_props/General_Directories.props b/_props/General_Directories.props new file mode 100644 index 0000000..ba92f3a --- /dev/null +++ b/_props/General_Directories.props @@ -0,0 +1,18 @@ + + + + $(SolutionDir)_out\$(Platform)\$(Configuration)\ + + + + $(SolutionDir).tmp\$(Platform)\$(Configuration)\$(ProjectName)\ + + + + $(SolutionDir).tmp\$(Platform)\$(Configuration)\$(RootNamespace)\ + + + + $(SolutionDir).tmp\$(Platform)\$(Configuration)\$(ProjectGuid)\ + + diff --git a/_props/Linker.props b/_props/Linker.props new file mode 100644 index 0000000..c6d7acb --- /dev/null +++ b/_props/Linker.props @@ -0,0 +1,15 @@ + + + + + Windows + MachineX86 + + + + + Windows + MachineX64 + + + diff --git a/_props/TARGET_EXE.props b/_props/TARGET_EXE.props new file mode 100644 index 0000000..1ce2aa4 --- /dev/null +++ b/_props/TARGET_EXE.props @@ -0,0 +1,7 @@ + + + + + + + diff --git a/_props/_Platform.props b/_props/_Platform.props new file mode 100644 index 0000000..c528b5e --- /dev/null +++ b/_props/_Platform.props @@ -0,0 +1,18 @@ + + + + + NoUpgrade + + + Unicode + v142 + + + false + + + true + + + diff --git a/_props/auto/LatestTargetPlatformVersion.props b/_props/auto/LatestTargetPlatformVersion.props new file mode 100644 index 0000000..f97fbd7 --- /dev/null +++ b/_props/auto/LatestTargetPlatformVersion.props @@ -0,0 +1,8 @@ + + + + + + $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) + + diff --git a/_props/user/_Platform.props.IN b/_props/user/_Platform.props.IN new file mode 100644 index 0000000..7cc2b64 --- /dev/null +++ b/_props/user/_Platform.props.IN @@ -0,0 +1,17 @@ + + + + + + + $(LatestTargetPlatformVersion) + + + + v140 + +