├── .github └── CODEOWNERS ├── .gitignore ├── .gitmodules ├── .pipelines ├── iis.servicemonitor.build.dev.yml └── iis.servicemonitor.build.official.yml ├── Directory.Build.props ├── LICENSE ├── NuGet.config ├── README.md ├── SECURITY.md ├── ServiceMonitor.sln ├── azure-pipelines.yml ├── build.cmd ├── build └── Build.Settings.targets ├── root.props ├── sign.props └── src └── ServiceMonitor ├── IISConfigUtil.cpp ├── IISConfigUtil.h ├── Main.cpp ├── ServiceMonitor.cpp ├── ServiceMonitor.h ├── ServiceMonitor.rc ├── ServiceMonitor.vcxproj ├── packages.config ├── stdafx.cpp ├── stdafx.h ├── targetver.h └── version.h /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @Microsoft/iis-contributor 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "IIS.Common"] 2 | path = IIS.Common 3 | url = https://github.com/Microsoft/IIS.Common.git 4 | -------------------------------------------------------------------------------- /.pipelines/iis.servicemonitor.build.dev.yml: -------------------------------------------------------------------------------- 1 | name: $(Rev:rr) 2 | 3 | pr: 4 | - master 5 | 6 | trigger: 7 | batch: true 8 | branches: 9 | include: 10 | - master 11 | 12 | resources: 13 | repositories: 14 | - repository: MicrosoftIISCommon 15 | type: github 16 | name: Microsoft/IIS.Common 17 | endpoint: GitHub-IIS-Token 18 | 19 | jobs: 20 | - template: .azure\templates\build.yml@MicrosoftIISCommon 21 | parameters: 22 | agentPoolName: 'VSEngSS-MicroBuild2022-1ES' 23 | agentPoolDemandTeam: '' 24 | solution: '**\ServiceMonitor.sln' 25 | productMajor: 2 26 | productMinor: 0 27 | buildMinor: $(Build.BuildNumber) 28 | signType: 'test' 29 | indexSourcesAndPublishSymbols: 'true' 30 | publishArtifactInstallers: 'false' 31 | whiteListPathForAuthenticodeSign: '$(Build.SourcesDirectory)\IIS-Common\.azure\templates\no_authenticode.txt' 32 | 33 | -------------------------------------------------------------------------------- /.pipelines/iis.servicemonitor.build.official.yml: -------------------------------------------------------------------------------- 1 | name: $(Rev:rr) 2 | 3 | pr: none 4 | trigger: none 5 | 6 | resources: 7 | repositories: 8 | - repository: MicrosoftIISCommon 9 | type: github 10 | name: Microsoft/IIS.Common 11 | endpoint: GitHub-IIS-Token 12 | 13 | jobs: 14 | - template: .azure\templates\build.yml@MicrosoftIISCommon 15 | parameters: 16 | agentPoolName: 'VSEngSS-MicroBuild2022-1ES' 17 | agentPoolDemandTeam: '' 18 | solution: '**\ServiceMonitor.sln' 19 | productMajor: 1 20 | productMinor: 0 21 | buildMinor: $(Build.BuildNumber) 22 | signType: 'real' 23 | indexSourcesAndPublishSymbols: 'true' 24 | publishArtifactInstallers: 'false' 25 | whiteListPathForAuthenticodeSign: '$(Build.SourcesDirectory)\IIS-Common\.azure\templates\no_authenticode.txt' 26 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Microsoft IIS Service Monitor 2 | 3 | **ServiceMonitor** is a Windows executable designed to be used as the entrypoint 4 | process when running IIS inside a Windows Server container. 5 | 6 | ServiceMonitor monitors the status of the `w3svc` service and will exit when the 7 | service state changes from `SERVICE_RUNNING` to either one of `SERVICE_STOPPED`, 8 | `SERVICE_STOP_PENDING`, `SERVICE_PAUSED` or `SERVICE_PAUSE_PENDING`. 9 | 10 | Additionally, ServiceMonitor will promote environment variables from process 11 | environment it's own process environment block to the DefaultAppPool. We achieve 12 | this by naively copying all variables in our process environment block except 13 | for those Environment variable / value pairs present in this list below. 14 | 15 | ### Environment variable exclusion list 16 | 17 | | Environment Variable | Value | 18 | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 19 | | TMP | "C:\\Users\\ContainerAdministrator\\AppData\\Local\\Temp" | 20 | | TEMP | "C:\\Users\\ContainerAdministrator\\AppData\\Local\\Temp" | 21 | | USERNAME | "ContainerAdministrator" | 22 | | USERPROFILE | "C:\\Users\\ContainerAdministrator" | 23 | | APPDATA | "C:\\Users\\ContainerAdministrator\\AppData\\Roaming" | 24 | | LOCALAPPDATA | "C:\\Users\\ContainerAdministrator\\AppData\\Local" | 25 | | PROGRAMDATA | "C:\\ProgramData" | 26 | | PSMODULEPATH | "%ProgramFiles%\\WindowsPowerShell\\Modules;C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules" | 27 | | PUBLIC | "C:\\Users\\Public" | 28 | | USERDOMAIN | "User Manager" | 29 | | ALLUSERSPROFILE | "C:\\ProgramData" | 30 | | PATHEXT | ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC" | 31 | | PATH | * | 32 | | COMPUTERNAME | * | 33 | | COMSPEC | * | 34 | | OS | * | 35 | | PROCESSOR_IDENTIFIER | * | 36 | | PROCESSOR_LEVEL | * | 37 | | PROCESSOR_REVISION | * | 38 | | PROGRAMFILES | * | 39 | | PROGRAMFILES(X86) | * | 40 | | PROGRAMW6432 | * | 41 | | SYSTEMDRIVE | * | 42 | | WINDIR | * | 43 | | NUMBER_OF_PROCESSORS | * | 44 | | PROCESSOR_ARCHITECTURE | * | 45 | | SYSTEMROOT | * | 46 | | COMMONPROGRAMFILES | * | 47 | | COMMONPROGRAMFILES(X86) | * | 48 | | COMMONPROGRAMW6432 | * | 49 | | DRIVERDATA | * | 50 | 51 | ## Build 52 | 53 | ``` 54 | .\build.cmd 55 | ``` 56 | 57 | ## Usage 58 | 59 | ``` 60 | .\ServiceMonitor.exe w3svc 61 | ``` 62 | 63 | ServiceMonitor is currently distributed as part of the [IIS](https://hub.docker.com/_/microsoft-windows-servercore-iis), 64 | [ASP.NET](https://hub.docker.com/_/microsoft-dotnet-framework-aspnet), and [WCF](https://hub.docker.com/_/microsoft-dotnet-framework-wcf) images on Docker Hub. We recommend layering your project on top of those official images as running 65 | ServiceMonitor directly in your Dockerfile. 66 | 67 | ## Contributing 68 | 69 | This project welcomes contributions and suggestions. Most contributions require 70 | you to agree to a Contributor License Agreement (CLA) declaring that you have 71 | the right to, and actually do, grant us the rights to use your contribution. For 72 | details, visit https://cla.microsoft.com. 73 | 74 | When you submit a pull request, a CLA-bot will automatically determine whether 75 | you need to provide a CLA and decorate the PR appropriately (e.g., label, 76 | comment). Simply follow the instructions provided by the bot. You will only need 77 | to do this once across all repos using our CLA. 78 | 79 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 80 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 81 | or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any 82 | additional questions or comments. 83 | 84 | 85 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /ServiceMonitor.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26717.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{1F39DB32-3422-4FF2-A8A4-A4BCCB0FD36E}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ServiceMonitor", "src\ServiceMonitor\ServiceMonitor.vcxproj", "{EFAE236B-EFB4-413C-8EEE-09F211558AC1}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {EFAE236B-EFB4-413C-8EEE-09F211558AC1}.Debug|x64.ActiveCfg = Debug|x64 19 | {EFAE236B-EFB4-413C-8EEE-09F211558AC1}.Debug|x64.Build.0 = Debug|x64 20 | {EFAE236B-EFB4-413C-8EEE-09F211558AC1}.Debug|x86.ActiveCfg = Debug|Win32 21 | {EFAE236B-EFB4-413C-8EEE-09F211558AC1}.Debug|x86.Build.0 = Debug|Win32 22 | {EFAE236B-EFB4-413C-8EEE-09F211558AC1}.Release|x64.ActiveCfg = Release|x64 23 | {EFAE236B-EFB4-413C-8EEE-09F211558AC1}.Release|x64.Build.0 = Release|x64 24 | {EFAE236B-EFB4-413C-8EEE-09F211558AC1}.Release|x86.ActiveCfg = Release|Win32 25 | {EFAE236B-EFB4-413C-8EEE-09F211558AC1}.Release|x86.Build.0 = Release|Win32 26 | EndGlobalSection 27 | GlobalSection(SolutionProperties) = preSolution 28 | HideSolutionNode = FALSE 29 | EndGlobalSection 30 | GlobalSection(NestedProjects) = preSolution 31 | {EFAE236B-EFB4-413C-8EEE-09F211558AC1} = {1F39DB32-3422-4FF2-A8A4-A4BCCB0FD36E} 32 | EndGlobalSection 33 | GlobalSection(ExtensibilityGlobals) = postSolution 34 | SolutionGuid = {DEE383BB-40C7-47B7-84D9-91DDD29A8297} 35 | EndGlobalSection 36 | EndGlobal 37 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | resources: 2 | repositories: 3 | - repository: Microsoft/IIS.Common 4 | type: github 5 | name: Microsoft/IIS.Common 6 | endpoint: GitHub-IIS-PAT 7 | 8 | jobs: 9 | - template: azure-pipelines.yml@Microsoft/IIS.Common # Template reference 10 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | msbuild ServiceMonitor.sln /p:platform=x64 /p:configuration=Release -------------------------------------------------------------------------------- /build/Build.Settings.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | x64 4 | x86 5 | 6 | -------------------------------------------------------------------------------- /root.props: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | $(MSBuildThisFileDirectory) 9 | $(RepositoryRoot)src 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sign.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 0.2.0 5 | 6 | 7 | 8 | 9 | $(SigningIdentity) 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ServiceMonitor/IISConfigUtil.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #include"stdafx.h" 5 | #include 6 | using namespace std; 7 | 8 | #define APPCMD_MAX_SIZE 30000 9 | #define KV(a,b) pair(a,b) 10 | #define KV_WSTR(a,b) pair(a,b) 11 | #define POPULATE(map) do \ 12 | { \ 13 | map.insert(KV(L"TMP", L"C:\\Users\\ContainerAdministrator\\AppData\\Local\\Temp")); \ 14 | map.insert(KV(L"TEMP", L"C:\\Users\\ContainerAdministrator\\AppData\\Local\\Temp")); \ 15 | map.insert(KV(L"USERNAME", L"ContainerAdministrator")); \ 16 | map.insert(KV(L"USERPROFILE", L"C:\\Users\\ContainerAdministrator")); \ 17 | map.insert(KV(L"APPDATA", L"C:\\Users\\ContainerAdministrator\\AppData\\Roaming")); \ 18 | map.insert(KV(L"LOCALAPPDATA", L"C:\\Users\\ContainerAdministrator\\AppData\\Local")); \ 19 | map.insert(KV(L"PROGRAMDATA", L"C:\\ProgramData")); \ 20 | map.insert(KV(L"PSMODULEPATH", L"%ProgramFiles%\\WindowsPowerShell\\Modules;C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules")); \ 21 | map.insert(KV(L"PUBLIC", L"C:\\Users\\Public")); \ 22 | map.insert(KV(L"USERDOMAIN", L"User Manager")); \ 23 | map.insert(KV(L"ALLUSERSPROFILE", L"C:\\ProgramData")); \ 24 | map.insert(KV(L"PATHEXT", L".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC")); \ 25 | map.insert(KV(L"PATH", NULL)); \ 26 | map.insert(KV(L"COMPUTERNAME", NULL)); \ 27 | map.insert(KV(L"COMSPEC", NULL)); \ 28 | map.insert(KV(L"OS", NULL)); \ 29 | map.insert(KV(L"PROCESSOR_IDENTIFIER", NULL)); \ 30 | map.insert(KV(L"PROCESSOR_LEVEL", NULL)); \ 31 | map.insert(KV(L"PROCESSOR_REVISION", NULL)); \ 32 | map.insert(KV(L"PROGRAMFILES", NULL)); \ 33 | map.insert(KV(L"PROGRAMFILES(X86)", NULL)); \ 34 | map.insert(KV(L"PROGRAMW6432", NULL)); \ 35 | map.insert(KV(L"SYSTEMDRIVE", NULL)); \ 36 | map.insert(KV(L"WINDIR", NULL)); \ 37 | map.insert(KV(L"NUMBER_OF_PROCESSORS", NULL)); \ 38 | map.insert(KV(L"PROCESSOR_ARCHITECTURE", NULL)); \ 39 | map.insert(KV(L"SYSTEMROOT", NULL)); \ 40 | map.insert(KV(L"COMMONPROGRAMFILES", NULL)); \ 41 | map.insert(KV(L"COMMONPROGRAMFILES(X86)", NULL)); \ 42 | map.insert(KV(L"COMMONPROGRAMW6432", NULL)); \ 43 | map.insert(KV(L"DRIVERDATA", NULL)); \ 44 | } while(0) 45 | 46 | IISConfigUtil::IISConfigUtil():m_pstrSysDirPath(NULL) 47 | { 48 | } 49 | 50 | IISConfigUtil::~IISConfigUtil() 51 | { 52 | if (m_pstrSysDirPath != NULL) 53 | { 54 | delete m_pstrSysDirPath; 55 | m_pstrSysDirPath = NULL; 56 | } 57 | } 58 | 59 | BOOL IISConfigUtil::FilterEnv(const unordered_map& filter, LPCTSTR strEnvName, LPCTSTR strEnvValue) 60 | { 61 | LPTSTR strFilterValue; 62 | _ASSERT(strEnvName != NULL); 63 | _ASSERT(strEnvValue != NULL); 64 | 65 | auto value = filter.find(strEnvName); 66 | 67 | // 68 | // add this environment variable if the name does not match the block list 69 | // 70 | if (value == filter.end()) 71 | { 72 | return FALSE; 73 | } 74 | 75 | strFilterValue = value->second; 76 | 77 | // 78 | // filter out this environment variable if 79 | // 1. value match is not required (strFilterValue is NULL) 80 | // 2. require value match and value matches 81 | // 82 | if ((strFilterValue == NULL ) || (lstrcmpi(strEnvValue, strFilterValue) == 0)) 83 | { 84 | return TRUE; 85 | } 86 | 87 | return FALSE; 88 | } 89 | 90 | HRESULT IISConfigUtil::Initialize() 91 | { 92 | HRESULT hr = S_OK; 93 | TCHAR* pBuffer = NULL; 94 | DWORD dwBufSize = 0; 95 | 96 | // 97 | // resolve system drive 98 | // 99 | dwBufSize = GetSystemDirectory(NULL, 0); 100 | if (dwBufSize == 0) 101 | { 102 | // 103 | // failed to get System Directory info 104 | // 105 | hr = HRESULT_FROM_WIN32(GetLastError()); 106 | goto Finished; 107 | } 108 | 109 | pBuffer = (TCHAR*)malloc(dwBufSize * sizeof(TCHAR)); 110 | if (pBuffer == NULL) 111 | { 112 | hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); 113 | goto Finished; 114 | } 115 | 116 | if (GetSystemDirectory(pBuffer, dwBufSize) == 0) 117 | { 118 | hr = HRESULT_FROM_WIN32(GetLastError()); 119 | goto Finished; 120 | } 121 | 122 | m_pstrSysDirPath = pBuffer; 123 | pBuffer = NULL; 124 | 125 | Finished: 126 | if (pBuffer != NULL) 127 | { 128 | free(pBuffer); 129 | pBuffer = NULL; 130 | } 131 | return hr; 132 | } 133 | 134 | void IISConfigUtil::Replace(std::wstring& str, std::wstring oldValue, std::wstring newValue) 135 | { 136 | size_t pos = 0; 137 | size_t oldValueLen = oldValue.length(); 138 | size_t newValueLen = newValue.length(); 139 | 140 | while ((pos = str.find(oldValue, pos)) != std::wstring::npos) { 141 | str.replace(pos, oldValueLen, newValue); 142 | pos += newValueLen; 143 | } 144 | } 145 | 146 | HRESULT IISConfigUtil::BuildAppCmdCommand(const vector>& vecSet, vector>::iterator& envVecIter, WCHAR* pstrAppPoolName, wstring& pStrCmd, APPCMD_CMD_TYPE appCmdType) 147 | { 148 | HRESULT hr = S_OK; 149 | _ASSERT(pstrAppPoolName != NULL); 150 | 151 | pStrCmd.append(m_pstrSysDirPath); 152 | pStrCmd.append(L"\\inetsrv\\appcmd.exe set config -section:system.applicationHost/applicationPools "); 153 | 154 | for (; envVecIter != vecSet.end(); envVecIter++) 155 | { 156 | wstring strEnvName = envVecIter->first; 157 | wstring strEnvValue = envVecIter->second; 158 | 159 | // 160 | // Handle values that have single and double quotes 161 | // 162 | Replace(strEnvName, L"'", L"''"); 163 | Replace(strEnvName, L"\"", L"\"\"\""); 164 | Replace(strEnvValue, L"'", L"''"); 165 | Replace(strEnvValue, L"\"", L"\"\"\""); 166 | 167 | if ((pStrCmd.length() + strEnvName.length() + strEnvValue.length()) > APPCMD_MAX_SIZE) 168 | { 169 | // 170 | // caller need to call again 171 | // 172 | 173 | hr = ERROR_MORE_DATA; 174 | break; 175 | } 176 | 177 | if (appCmdType == APPCMD_ADD) 178 | { 179 | pStrCmd.append(L"/+\"[name='"); 180 | } 181 | else 182 | { 183 | pStrCmd.append(L"/-\"[name='"); 184 | } 185 | pStrCmd.append(pstrAppPoolName); 186 | pStrCmd.append(L"'].environmentVariables.[name='"); 187 | pStrCmd.append(strEnvName); 188 | if (appCmdType == APPCMD_ADD) 189 | { 190 | pStrCmd.append(L"',value='"); 191 | pStrCmd.append(strEnvValue); 192 | } 193 | pStrCmd.append(L"']\" "); 194 | } 195 | 196 | pStrCmd.append(L" /commit:apphost"); 197 | 198 | return hr; 199 | } 200 | 201 | 202 | HRESULT IISConfigUtil::RunCommand(wstring& pstrCmd, BOOL fIgnoreError) 203 | { 204 | HRESULT hr = S_OK; 205 | STARTUPINFO si = { sizeof(STARTUPINFO) }; 206 | DWORD dwStatus = 0; 207 | PROCESS_INFORMATION pi; 208 | 209 | ZeroMemory(&si, sizeof(STARTUPINFO)); 210 | si.cb = sizeof(STARTUPINFO); 211 | si.dwFlags |= STARTF_USESTDHANDLES; 212 | 213 | if (!CreateProcess(NULL, 214 | (LPWSTR)pstrCmd.c_str(), 215 | NULL, 216 | NULL, 217 | false, 218 | 0, 219 | NULL, 220 | NULL, 221 | &si, 222 | &pi)) 223 | { 224 | hr = HRESULT_FROM_WIN32(GetLastError()); 225 | goto Finished; 226 | } 227 | 228 | // 229 | // wait for at most 5 seconds to allow APPCMD finish 230 | // 231 | WaitForSingleObject(pi.hProcess, 5000); 232 | if ((!GetExitCodeProcess(pi.hProcess, &dwStatus) || dwStatus != 0) && (!fIgnoreError)) 233 | { 234 | // 235 | // appcmd command failed 236 | // 237 | _tprintf(L"\nAPPCMD failed with error code %d\n", dwStatus); 238 | hr = E_FAIL; 239 | } 240 | 241 | CloseHandle(pi.hProcess); 242 | CloseHandle(pi.hThread); 243 | 244 | Finished: 245 | 246 | return hr; 247 | } 248 | 249 | HRESULT IISConfigUtil::UpdateEnvironmentVarsToConfig(WCHAR* pstrAppPoolName) 250 | { 251 | HRESULT hr = S_OK; 252 | LPTCH lpvEnv = NULL; 253 | LPTSTR lpszVariable = NULL; 254 | wstring pstrAddCmd; 255 | wstring pstrRmCmd; 256 | BOOL fMoreData = TRUE; 257 | 258 | unordered_map filter; 259 | vector> envVec; 260 | vector>::iterator envVecIter; 261 | 262 | POPULATE(filter) ; 263 | 264 | lpvEnv = GetEnvironmentStrings(); 265 | if (lpvEnv == NULL) 266 | { 267 | _tprintf(L"Failed to call GetEnvironmentStrings! \n"); 268 | hr = E_FAIL; 269 | goto Finished; 270 | } 271 | 272 | lpszVariable = (LPTSTR)lpvEnv; 273 | while (*lpszVariable) 274 | { 275 | LPTSTR pEqualChar = wcschr(lpszVariable, L'='); 276 | if (pEqualChar != lpszVariable) 277 | { 278 | DWORD dwStatus = 0; 279 | LPTSTR pstrValue = pEqualChar + 1; 280 | LPTSTR pstrName = lpszVariable; 281 | 282 | 283 | pEqualChar[0] = L'\0'; 284 | wstring strNameCheck(pstrName); 285 | if (FilterEnv(filter, CharUpper((LPWSTR)strNameCheck.c_str()), pstrValue)) 286 | { 287 | pEqualChar[0] = L'='; 288 | lpszVariable += lstrlen(lpszVariable) + 1; 289 | 290 | continue; 291 | } 292 | 293 | envVec.emplace_back(wstring(pstrName), wstring(pstrValue)); 294 | 295 | pEqualChar[0] = L'='; 296 | 297 | } 298 | // 299 | // move to next environment variable 300 | // 301 | lpszVariable += lstrlen(lpszVariable) + 1; 302 | } 303 | 304 | envVecIter = envVec.begin(); 305 | while (fMoreData) 306 | { 307 | 308 | pstrRmCmd.clear(); 309 | fMoreData = FALSE; 310 | hr = BuildAppCmdCommand(envVec, envVecIter, pstrAppPoolName, pstrRmCmd, APPCMD_RM); 311 | 312 | if (hr != ERROR_MORE_DATA && FAILED(hr)) 313 | { 314 | goto Finished; 315 | } 316 | 317 | if (hr == ERROR_MORE_DATA) 318 | { 319 | hr = S_OK; 320 | fMoreData = TRUE; 321 | } 322 | 323 | // 324 | //allow appcmd to fail if it is trying to remove environment variable 325 | // 326 | RunCommand(pstrRmCmd, TRUE); 327 | } 328 | 329 | fMoreData = TRUE; 330 | envVecIter = envVec.begin(); 331 | while (fMoreData) 332 | { 333 | pstrAddCmd.clear(); 334 | fMoreData = FALSE; 335 | hr = BuildAppCmdCommand(envVec, envVecIter, pstrAppPoolName, pstrAddCmd, APPCMD_ADD); 336 | 337 | if (hr != ERROR_MORE_DATA && FAILED(hr)) 338 | { 339 | goto Finished; 340 | } 341 | 342 | if (hr == ERROR_MORE_DATA) 343 | { 344 | hr = S_OK; 345 | fMoreData = TRUE; 346 | } 347 | 348 | // 349 | //appcmd must succeed when add new environment variables 350 | // 351 | hr = RunCommand(pstrAddCmd, FALSE); 352 | 353 | if (FAILED(hr)) 354 | { 355 | goto Finished; 356 | } 357 | } 358 | 359 | Finished: 360 | if (lpvEnv != NULL) 361 | { 362 | FreeEnvironmentStrings(lpvEnv); 363 | lpvEnv = NULL; 364 | } 365 | 366 | return hr; 367 | } -------------------------------------------------------------------------------- /src/ServiceMonitor/IISConfigUtil.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | enum APPCMD_CMD_TYPE 10 | { 11 | APPCMD_ADD = 0, 12 | APPCMD_RM = 1 13 | }; 14 | 15 | class IISConfigUtil 16 | { 17 | public: 18 | 19 | IISConfigUtil(); 20 | ~IISConfigUtil(); 21 | HRESULT Initialize(); 22 | HRESULT UpdateEnvironmentVarsToConfig(WCHAR* pstrAppPoolName); 23 | 24 | private: 25 | HRESULT RunCommand(std::wstring& pstrCmd, BOOL fIgnoreError); 26 | void Replace(std::wstring& str, std::wstring oldValue, std::wstring newValue); 27 | HRESULT BuildAppCmdCommand(const std::vector>& vecSet, std::vector>::iterator& envVecIter, WCHAR* pstrAppPoolName, std::wstring& pStrCmd, APPCMD_CMD_TYPE appcmdType); 28 | BOOL FilterEnv(const std::unordered_map& filter, LPCTSTR strEnvName, LPCTSTR strEnvValue); 29 | TCHAR* m_pstrSysDirPath; 30 | }; 31 | 32 | -------------------------------------------------------------------------------- /src/ServiceMonitor/Main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #include"stdafx.h" 5 | 6 | HANDLE g_hStopEvent = INVALID_HANDLE_VALUE; 7 | 8 | BOOL WINAPI CtrlHandle(DWORD dwCtrlType) 9 | { 10 | switch (dwCtrlType) 11 | { 12 | case CTRL_C_EVENT: 13 | case CTRL_CLOSE_EVENT: 14 | case CTRL_BREAK_EVENT: 15 | case CTRL_LOGOFF_EVENT: 16 | case CTRL_SHUTDOWN_EVENT: 17 | _tprintf(L"\nCTRL signal received. The process will now terminate.\n"); 18 | SetEvent(g_hStopEvent); 19 | g_hStopEvent = INVALID_HANDLE_VALUE; 20 | break; 21 | 22 | default: 23 | break; 24 | } 25 | 26 | return TRUE; 27 | } 28 | 29 | int __cdecl _tmain(int argc, _TCHAR* argv[]) 30 | { 31 | HRESULT hr = S_OK; 32 | Service_Monitor sm = Service_Monitor(); 33 | 34 | if (argc <= 1) 35 | { 36 | _tprintf(L"\nUSAGE: %s [windows service name]", argv[0]); 37 | _tprintf(L"\n %s w3svc [application pool]", argv[0]); 38 | _tprintf(L"\n\nOptions:"); 39 | _tprintf(L"\n windows service name Name of the Windows service to monitor"); 40 | _tprintf(L"\n application pool Name of the application pool to monitor; defaults to DefaultAppPool\n"); 41 | goto Finished; 42 | } 43 | 44 | // iis only allow monitor on w3svc 45 | if (_wcsicmp(argv[1], L"w3svc") == 0) 46 | { 47 | hr = sm.StopServiceByName(L"w3svc"); 48 | if (FAILED(hr)) 49 | { 50 | hr = HRESULT_FROM_WIN32(GetLastError()); 51 | goto Finished; 52 | } 53 | 54 | // 55 | // iis scenario, update the environment variable 56 | // we hardcode this behavior for now. We can add an input switch later if needed 57 | // 58 | WCHAR* pstrAppPoolName = L"DefaultAppPool"; 59 | if (argc > 2) 60 | { 61 | pstrAppPoolName = argv[2]; 62 | } 63 | IISConfigUtil configHelper = IISConfigUtil(); 64 | if( FAILED(hr = configHelper.Initialize()) || 65 | FAILED(hr = configHelper.UpdateEnvironmentVarsToConfig(pstrAppPoolName))) 66 | { 67 | _tprintf(L"\nFailed to update IIS configuration\n"); 68 | goto Finished; 69 | } 70 | } 71 | 72 | g_hStopEvent = CreateEvent( 73 | NULL, // default security attributes 74 | TRUE, // manual-reset event 75 | FALSE, // initial state is nonsignaled 76 | argv[1] // object name 77 | ); 78 | 79 | if (g_hStopEvent == NULL) 80 | { 81 | hr = HRESULT_FROM_WIN32(GetLastError()); 82 | goto Finished; 83 | } 84 | 85 | if (!SetConsoleCtrlHandler(CtrlHandle, TRUE)) 86 | { 87 | hr = HRESULT_FROM_WIN32(GetLastError()); 88 | _tprintf(L"\nERROR: Failed to set control handle with error [%x]\n", hr); 89 | goto Finished; 90 | } 91 | 92 | if (g_hStopEvent != INVALID_HANDLE_VALUE) 93 | { 94 | hr = sm.StartServiceByName(argv[1]); 95 | } 96 | 97 | if (SUCCEEDED(hr)) 98 | { 99 | if (g_hStopEvent != INVALID_HANDLE_VALUE) 100 | { 101 | // 102 | // will stop monitoring once the stop event is set 103 | // 104 | hr = sm.MonitoringService(argv[1], 105 | SERVICE_NOTIFY_STOPPED | SERVICE_NOTIFY_STOP_PENDING | SERVICE_NOTIFY_PAUSED | SERVICE_NOTIFY_PAUSE_PENDING, 106 | g_hStopEvent); 107 | } 108 | } 109 | // 110 | // don't capture the stop error 111 | // 112 | sm.StopServiceByName(argv[1]); 113 | 114 | Finished: 115 | if (g_hStopEvent != INVALID_HANDLE_VALUE) 116 | { 117 | CloseHandle(g_hStopEvent); 118 | g_hStopEvent = INVALID_HANDLE_VALUE; 119 | } 120 | return hr; 121 | } 122 | -------------------------------------------------------------------------------- /src/ServiceMonitor/ServiceMonitor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | // ServiceMonitor.cpp : Defines the entry point for the console application. 5 | // 6 | #include"stdafx.h" 7 | 8 | VOID CALLBACK Service_Monitor::NotifyCallBack(PVOID parameter) 9 | { 10 | PSERVICE_NOTIFY pSNotify = (PSERVICE_NOTIFY)parameter; 11 | HANDLE hEvent = (HANDLE) pSNotify->pContext; 12 | _tprintf(L"\nService Change Status Notify Callback is called for service '%s' with status '%d'", 13 | pSNotify->pszServiceNames, pSNotify->ServiceStatus.dwCurrentState); 14 | if (hEvent != NULL) 15 | { 16 | SetEvent(hEvent); 17 | } 18 | } 19 | 20 | HRESULT Service_Monitor::EnsureInitialized() 21 | { 22 | HRESULT hr = S_OK; 23 | if (!_fInitialized) 24 | { 25 | AcquireSRWLockExclusive(&_srwLock); 26 | if (!_fInitialized) 27 | { 28 | _hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); 29 | if (_hSCManager == NULL) 30 | { 31 | hr = HRESULT_FROM_WIN32(GetLastError()); 32 | _tprintf(L"\nERROR:Could NOT open server control manager [%x]\n", hr); 33 | } 34 | else 35 | { 36 | _fInitialized = TRUE; 37 | } 38 | } 39 | ReleaseSRWLockExclusive(&_srwLock); 40 | } 41 | return hr; 42 | } 43 | 44 | HRESULT Service_Monitor::StartServiceByName(LPCTSTR pServiceName, DWORD dwTimeOutSeconds) 45 | { 46 | HRESULT hr = S_OK; 47 | SC_HANDLE hService = NULL; 48 | DWORD dwSleepTime = 0; 49 | DWORD dwRemainTime = dwTimeOutSeconds; 50 | 51 | hr = GetServiceHandle(pServiceName, &hService); 52 | 53 | if (SUCCEEDED(hr)) 54 | { 55 | if (StartService(hService, 0, NULL) == FALSE) 56 | { 57 | DWORD dwError = GetLastError(); 58 | if (dwError == ERROR_SERVICE_ALREADY_RUNNING) 59 | { 60 | dwError = ERROR_SUCCESS; 61 | } 62 | else 63 | { 64 | hr = HRESULT_FROM_WIN32(dwError); 65 | } 66 | } 67 | // 68 | // Query service status to make sure service is in running state 69 | // 70 | while(dwRemainTime >0) 71 | { 72 | DWORD dwBytes = 0; 73 | SERVICE_STATUS_PROCESS sStatus; 74 | 75 | if (!QueryServiceStatusEx(hService, 76 | SC_STATUS_PROCESS_INFO, 77 | (LPBYTE)&sStatus, 78 | sizeof(SERVICE_STATUS_PROCESS), 79 | &dwBytes)) 80 | { 81 | hr = HRESULT_FROM_WIN32(GetLastError()); 82 | goto Finished; 83 | } 84 | 85 | if (sStatus.dwCurrentState == SERVICE_RUNNING) 86 | { 87 | goto Finished; 88 | } 89 | else if(sStatus.dwCurrentState == SERVICE_START_PENDING) 90 | { 91 | dwSleepTime = rand() % 10 + 1; 92 | dwSleepTime = dwSleepTime < dwRemainTime ? dwSleepTime : dwRemainTime; 93 | dwRemainTime -= dwSleepTime; 94 | Sleep(dwSleepTime * 1000); 95 | } 96 | else 97 | { 98 | // 99 | // Service fails to start 100 | // 101 | hr = E_FAIL; 102 | goto Finished; 103 | } 104 | } 105 | // 106 | // Cannot start service within given time period 107 | // 108 | hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); 109 | } 110 | 111 | Finished: 112 | if(SUCCEEDED(hr)) 113 | { 114 | _tprintf(L"\n Service '%s' started \n", pServiceName); 115 | } 116 | else 117 | { 118 | _tprintf(L"\nERROR: Failed to start or query status of service '%s' error [%x]\n", pServiceName, hr); 119 | } 120 | 121 | if (hService != NULL) 122 | { 123 | CloseServiceHandle(hService); 124 | } 125 | return hr; 126 | } 127 | 128 | HRESULT Service_Monitor::StopServiceByName(LPCTSTR pServiceName, DWORD dwTimeOutSeconds) 129 | { 130 | HRESULT hr = S_OK; 131 | SC_HANDLE hService = NULL; 132 | DWORD dwBytes = 0; 133 | DWORD dwSleepTime = 0; 134 | DWORD dwRemainTime = dwTimeOutSeconds; 135 | SERVICE_STATUS_PROCESS sStatus; 136 | 137 | hr = GetServiceHandle(pServiceName, &hService); 138 | if (SUCCEEDED(hr)) 139 | { 140 | while (dwRemainTime >0) 141 | { 142 | DWORD dwBytes = 0; 143 | if (!QueryServiceStatusEx(hService, 144 | SC_STATUS_PROCESS_INFO, 145 | (LPBYTE)&sStatus, 146 | sizeof(SERVICE_STATUS_PROCESS), 147 | &dwBytes)) 148 | { 149 | hr = HRESULT_FROM_WIN32(GetLastError()); 150 | goto Finished; 151 | } 152 | if (sStatus.dwCurrentState == SERVICE_STOPPED) 153 | { 154 | goto Finished; 155 | } 156 | else if (sStatus.dwCurrentState == SERVICE_STOP_PENDING || sStatus.dwCurrentState == SERVICE_START_PENDING || sStatus.dwCurrentState == SERVICE_PAUSE_PENDING || sStatus.dwCurrentState == SERVICE_CONTINUE_PENDING) 157 | { 158 | dwSleepTime = rand() % 10 + 1; 159 | dwSleepTime = dwSleepTime < dwRemainTime ? dwSleepTime : dwRemainTime; 160 | dwRemainTime -= dwSleepTime; 161 | Sleep(dwSleepTime * 1000); 162 | } 163 | else if (sStatus.dwCurrentState == SERVICE_RUNNING || sStatus.dwCurrentState == SERVICE_PAUSED) 164 | { 165 | if (!ControlService(hService, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&sStatus)) 166 | { 167 | hr = HRESULT_FROM_WIN32(GetLastError()); 168 | goto Finished; 169 | } 170 | _tprintf(L"\nStopping service '%s'\n", pServiceName); 171 | } 172 | else 173 | { 174 | // 175 | // Service fails to stop 176 | // 177 | hr = E_FAIL; 178 | goto Finished; 179 | } 180 | } 181 | // 182 | // cannot stop service within given time period 183 | // 184 | hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); 185 | 186 | } 187 | 188 | Finished: 189 | if (SUCCEEDED(hr)) 190 | { 191 | _tprintf(L"\n Service '%s' has been stopped \n", pServiceName); 192 | } 193 | else 194 | { 195 | _tprintf(L"\nERROR: Failed to stop or query status of service '%s' error [%x]\n", pServiceName, hr); 196 | } 197 | 198 | if (hService != NULL) 199 | { 200 | CloseServiceHandle(hService); 201 | } 202 | return hr; 203 | } 204 | 205 | HRESULT Service_Monitor::MonitoringService(LPCTSTR pServiceName, DWORD dwStatus, HANDLE hStopEvent) 206 | { 207 | HRESULT hr = S_OK; 208 | DWORD dwError = ERROR_SUCCESS; 209 | DWORD dwWaitResult; 210 | SC_HANDLE hService; 211 | SERVICE_NOTIFY sNotify; 212 | 213 | hr = GetServiceHandle(pServiceName, &hService); 214 | if(FAILED(hr)) 215 | { 216 | goto Finished; 217 | } 218 | 219 | sNotify.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE; 220 | sNotify.pfnNotifyCallback = (PFN_SC_NOTIFY_CALLBACK)NotifyCallBack; 221 | sNotify.pszServiceNames = (LPWSTR) pServiceName; 222 | sNotify.pContext = hStopEvent; 223 | 224 | dwError = NotifyServiceStatusChange(hService, dwStatus, &sNotify); 225 | if (dwError != ERROR_SUCCESS) 226 | { 227 | hr = HRESULT_FROM_WIN32(dwError); 228 | _tprintf(L"\nERROR: fail to register status change callback [%x]\n", hr); 229 | goto Finished; 230 | } 231 | 232 | dwWaitResult = WaitForSingleObjectEx(hStopEvent, INFINITE, TRUE); 233 | switch (dwWaitResult) 234 | { 235 | // Event object was signaled 236 | case WAIT_OBJECT_0: 237 | case WAIT_IO_COMPLETION: 238 | break; 239 | 240 | // An error occurred 241 | default: 242 | hr = HRESULT_FROM_WIN32(GetLastError()); 243 | _tprintf(L"\nERROR: Monitoring service '%s' wait error [%x]\n", pServiceName, hr); 244 | } 245 | 246 | Finished: 247 | if (hService != NULL) 248 | { 249 | CloseServiceHandle(hService); 250 | } 251 | return hr; 252 | } 253 | 254 | HRESULT Service_Monitor::GetServiceHandle(LPCTSTR pServiceName, SC_HANDLE* pHandle) 255 | { 256 | HRESULT hr = S_OK; 257 | 258 | if (pServiceName == NULL) 259 | { 260 | _tprintf(L"\nERROR: Null parameter for GetServiceHandle()\n"); 261 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); 262 | goto Finished; 263 | } 264 | 265 | hr = EnsureInitialized(); 266 | if (SUCCEEDED(hr)) 267 | { 268 | *pHandle = OpenService(_hSCManager, pServiceName, SERVICE_START | SERVICE_STOP | SERVICE_QUERY_STATUS); 269 | if (*pHandle == NULL) 270 | { 271 | hr = HRESULT_FROM_WIN32(GetLastError()); 272 | goto Finished; 273 | } 274 | } 275 | 276 | Finished: 277 | return hr; 278 | } 279 | -------------------------------------------------------------------------------- /src/ServiceMonitor/ServiceMonitor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | class Service_Monitor 9 | { 10 | 11 | public: 12 | Service_Monitor() :_hSCManager(NULL) 13 | { 14 | InitializeSRWLock(&_srwLock); 15 | } 16 | 17 | ~Service_Monitor() 18 | { 19 | if (_hSCManager != NULL) 20 | { 21 | CloseServiceHandle(_hSCManager); 22 | } 23 | } 24 | 25 | HRESULT EnsureInitialized(); 26 | HRESULT StartServiceByName(LPCTSTR pServiceName, DWORD dwTimeOutSeconds = 20); 27 | HRESULT StopServiceByName(LPCTSTR pServiceName, DWORD dwTimeOutSeconds = 20); 28 | HRESULT MonitoringService(LPCTSTR pServiceName, DWORD dwStatus, HANDLE hStopEvent); 29 | static VOID CALLBACK NotifyCallBack(PVOID parameter); 30 | 31 | private: 32 | HRESULT GetServiceHandle(LPCTSTR pServiceName, SC_HANDLE* pHandle); 33 | SC_HANDLE _hSCManager; 34 | BOOL _fInitialized; 35 | SRWLOCK _srwLock; 36 | }; 37 | -------------------------------------------------------------------------------- /src/ServiceMonitor/ServiceMonitor.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/IIS.ServiceMonitor/1c4153056fd8ad8e61317af115153180dc10c2cc/src/ServiceMonitor/ServiceMonitor.rc -------------------------------------------------------------------------------- /src/ServiceMonitor/ServiceMonitor.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | Win32 8 | 9 | 10 | Release 11 | Win32 12 | 13 | 14 | Debug 15 | x64 16 | 17 | 18 | Release 19 | x64 20 | 21 | 22 | 23 | {EFAE236B-EFB4-413C-8EEE-09F211558AC1} 24 | Win32Proj 25 | ServiceMonitor 26 | 10.0 27 | 28 | 29 | 30 | $(RepositoryRoot)bin\$(Configuration)\$(PlatformShortname)\ 31 | $(RepositoryRoot)obj\$(PlatformShortname)\$(Configuration)\$(MSBuildThisFileName)\ 32 | 33 | 34 | Application 35 | true 36 | v143 37 | Unicode 38 | 39 | 40 | Application 41 | false 42 | v143 43 | true 44 | Unicode 45 | 46 | 47 | Application 48 | true 49 | v143 50 | Unicode 51 | 52 | 53 | Application 54 | false 55 | v143 56 | true 57 | Unicode 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | true 79 | 80 | 81 | true 82 | 83 | 84 | false 85 | 86 | 87 | false 88 | 89 | 90 | 91 | Use 92 | Level3 93 | Disabled 94 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 95 | true 96 | MultiThreaded 97 | 98 | 99 | Console 100 | true 101 | 102 | 103 | 104 | 105 | Use 106 | Level3 107 | Disabled 108 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 109 | true 110 | MultiThreaded 111 | 112 | 113 | Console 114 | true 115 | 116 | 117 | 118 | 119 | Level3 120 | Use 121 | MaxSpeed 122 | true 123 | true 124 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 125 | true 126 | MultiThreaded 127 | 128 | 129 | Console 130 | true 131 | true 132 | true 133 | 134 | 135 | 136 | 137 | Level3 138 | Use 139 | MaxSpeed 140 | true 141 | true 142 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 143 | true 144 | MultiThreaded 145 | 146 | 147 | Console 148 | true 149 | true 150 | true 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | Create 165 | Create 166 | Create 167 | Create 168 | 169 | 170 | 171 | 172 | SM_BUILDMINORVERSION=$(BUILD_MINOR) 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | $(SigningIdentity) 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 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}. 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /src/ServiceMonitor/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/ServiceMonitor/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | // stdafx.cpp : source file that includes just the standard includes 5 | // ServiceMonitor.pch will be the pre-compiled header 6 | // stdafx.obj will contain the pre-compiled type information 7 | 8 | #include "stdafx.h" 9 | 10 | // TODO: reference any additional headers you need in STDAFX.H 11 | // and not in this file 12 | -------------------------------------------------------------------------------- /src/ServiceMonitor/stdafx.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | // stdafx.h : include file for standard system include files, 5 | // or project specific include files that are used frequently, but 6 | // are changed infrequently 7 | // 8 | 9 | #pragma once 10 | 11 | #include "targetver.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include "ServiceMonitor.h" 17 | #include "IISConfigUtil.h" 18 | #include 19 | #include 20 | 21 | 22 | 23 | // TODO: reference additional headers your program requires here 24 | -------------------------------------------------------------------------------- /src/ServiceMonitor/targetver.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #pragma once 5 | 6 | // Including SDKDDKVer.h defines the highest available Windows platform. 7 | 8 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 9 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 10 | 11 | #include 12 | -------------------------------------------------------------------------------- /src/ServiceMonitor/version.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/IIS.ServiceMonitor/1c4153056fd8ad8e61317af115153180dc10c2cc/src/ServiceMonitor/version.h --------------------------------------------------------------------------------