Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "cloud_print/service/win/cloud_print_service.h" | 5 #include "cloud_print/service/win/cloud_print_service.h" |
| 6 | 6 |
| 7 #include <iomanip> | |
| 8 #include <iostream> | |
| 9 | |
| 10 #include "base/at_exit.h" | |
| 11 #include "base/command_line.h" | |
| 12 #include "base/file_path.h" | |
| 13 #include "base/path_service.h" | |
| 7 #include "base/win/scoped_handle.h" | 14 #include "base/win/scoped_handle.h" |
| 8 #include "cloud_print/service/win/resource.h" | 15 #include "cloud_print/service/win/resource.h" |
| 9 | 16 |
| 17 namespace { | |
| 18 | |
| 19 const char kInstallSwitch[] = "install"; | |
| 20 const char kUninstallSwitch[] = "uninstall"; | |
| 21 const char kStartSwitch[] = "start"; | |
| 22 const char kStopSwitch[] = "stop"; | |
| 23 | |
| 24 const char kUserDataDirSwitch[] = "user-data-dir"; | |
| 25 const char kQuietSwitch[] = "quiet"; | |
| 26 | |
| 10 // The traits class for Windows Service. | 27 // The traits class for Windows Service. |
| 11 class ServiceHandleTraits { | 28 class ServiceHandleTraits { |
| 12 public: | 29 public: |
| 13 typedef SC_HANDLE Handle; | 30 typedef SC_HANDLE Handle; |
| 14 | 31 |
| 15 // Closes the handle. | 32 // Closes the handle. |
| 16 static bool CloseHandle(Handle handle) { | 33 static bool CloseHandle(Handle handle) { |
| 17 return ::CloseServiceHandle(handle) != FALSE; | 34 return ::CloseServiceHandle(handle) != FALSE; |
| 18 } | 35 } |
| 19 | 36 |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 32 typedef base::win::GenericScopedHandle<ServiceHandleTraits> ServiceHandle; | 49 typedef base::win::GenericScopedHandle<ServiceHandleTraits> ServiceHandle; |
| 33 | 50 |
| 34 HRESULT HResultFromLastError() { | 51 HRESULT HResultFromLastError() { |
| 35 HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); | 52 HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); |
| 36 // Something already failed if function called. | 53 // Something already failed if function called. |
| 37 if (SUCCEEDED(hr)) | 54 if (SUCCEEDED(hr)) |
| 38 hr = E_FAIL; | 55 hr = E_FAIL; |
| 39 return hr; | 56 return hr; |
| 40 } | 57 } |
| 41 | 58 |
| 59 void InvalidUsage() { | |
| 60 FilePath service_path; | |
| 61 CHECK(PathService::Get(base::FILE_EXE, &service_path)); | |
| 62 | |
| 63 std::cerr << "Usage: "; | |
|
gene
2012/05/10 22:11:45
cout?
Vitaly Buka (NO REVIEWS)
2012/05/10 22:49:12
Done.
| |
| 64 std::cerr << service_path.BaseName().value(); | |
| 65 std::cerr << " ["; | |
| 66 std::cerr << "["; | |
| 67 std::cerr << "["; | |
| 68 std::cerr << " -" << kInstallSwitch; | |
| 69 std::cerr << " -" << kUserDataDirSwitch << "=DIRECTORY"; | |
| 70 std::cerr << " [ -" << kQuietSwitch << " ]"; | |
| 71 std::cerr << "]"; | |
| 72 std::cerr << "]"; | |
| 73 std::cerr << " | -" << kUninstallSwitch; | |
| 74 std::cerr << " | -" << kStartSwitch; | |
| 75 std::cerr << " | -" << kStopSwitch; | |
| 76 std::cerr << " ]\n"; | |
| 77 std::cerr << "Manages cloud print service.\n\n"; | |
| 78 | |
| 79 static const char* kSwitchHelp[] = { | |
| 80 kInstallSwitch, | |
| 81 "Installs cloud print as windows service.", | |
| 82 kUserDataDirSwitch, | |
| 83 "User data directory with \"Service State\" file.", | |
| 84 kQuietSwitch, | |
| 85 "Fails without questions if something wrong.", | |
| 86 kUninstallSwitch, | |
| 87 "Uninstalls windows service.", | |
| 88 kStartSwitch, | |
| 89 "Starts windows service. May be combined with installation.", | |
| 90 kStopSwitch, | |
| 91 "Stops windows service.", | |
| 92 }; | |
| 93 | |
| 94 for (size_t i = 0; i < arraysize(kSwitchHelp) / 2; ++i) { | |
|
gene
2012/05/10 22:11:45
change this to not use i*2+1
Vitaly Buka (NO REVIEWS)
2012/05/10 22:49:12
Done.
| |
| 95 std::cerr << std::setiosflags(std::ios::left); | |
| 96 std::cerr << " -" << std::setw(15) << kSwitchHelp[i * 2]; | |
| 97 std::cerr << kSwitchHelp[i * 2 + 1] << "\n"; | |
| 98 } | |
| 99 std::cerr << "\n"; | |
| 100 } | |
| 101 | |
| 102 } // namespace | |
| 103 | |
| 42 class CloudPrintServiceModule | 104 class CloudPrintServiceModule |
| 43 : public ATL::CAtlServiceModuleT<CloudPrintServiceModule, IDS_SERVICENAME> { | 105 : public ATL::CAtlServiceModuleT<CloudPrintServiceModule, IDS_SERVICENAME> { |
| 44 public: | 106 public: |
| 45 typedef ATL::CAtlServiceModuleT<CloudPrintServiceModule, | 107 typedef ATL::CAtlServiceModuleT<CloudPrintServiceModule, |
| 46 IDS_SERVICENAME> Base; | 108 IDS_SERVICENAME> Base; |
| 47 | 109 |
| 48 DECLARE_REGISTRY_APPID_RESOURCEID(IDR_CLOUDPRINTSERVICE, | 110 DECLARE_REGISTRY_APPID_RESOURCEID(IDR_CLOUDPRINTSERVICE, |
| 49 "{8013FB7C-2E3E-4992-B8BD-05C0C4AB0627}") | 111 "{8013FB7C-2E3E-4992-B8BD-05C0C4AB0627}") |
| 50 | 112 |
| 51 HRESULT InitializeSecurity() { | 113 HRESULT InitializeSecurity() { |
| 52 // TODO(gene): Check if we need to call CoInitializeSecurity and provide | 114 // TODO(gene): Check if we need to call CoInitializeSecurity and provide |
| 53 // the appropriate security settings for service. | 115 // the appropriate security settings for service. |
| 54 return S_OK; | 116 return S_OK; |
| 55 } | 117 } |
| 56 | 118 |
| 57 // Override to set autostart and start service. | 119 HRESULT Install(const FilePath& user_data_dir) { |
| 58 HRESULT RegisterAppId(bool bService = false) { | 120 if (!Uninstall()) |
|
gene
2012/05/10 22:11:45
We probably should not uninstall here, but rather
Vitaly Buka (NO REVIEWS)
2012/05/10 22:49:12
Done.
| |
| 59 HRESULT hr = Base::RegisterAppId(bService); | 121 return E_FAIL; |
| 122 | |
| 123 if (user_data_dir.empty()) { | |
|
gene
2012/05/10 22:11:45
you can probably check this condition in the Parse
Vitaly Buka (NO REVIEWS)
2012/05/10 22:49:12
Done.
| |
| 124 std::cerr << "Invalid value --user_data_dir=" << user_data_dir.value() << | |
| 125 "\n"; | |
| 126 return E_INVALIDARG; | |
| 127 } | |
| 128 | |
| 129 FilePath service_path; | |
| 130 CHECK(PathService::Get(base::FILE_EXE, &service_path)); | |
| 131 CommandLine command_line(service_path); | |
| 132 command_line.AppendSwitchPath(kUserDataDirSwitch, user_data_dir); | |
| 133 | |
| 134 ServiceHandle scm; | |
| 135 HRESULT hr = OpenServiceManager(&scm); | |
| 60 if (FAILED(hr)) | 136 if (FAILED(hr)) |
| 61 return hr; | 137 return hr; |
| 62 | 138 |
| 63 ServiceHandle service; | 139 ServiceHandle service( |
| 64 hr = OpenService(SERVICE_CHANGE_CONFIG, &service); | 140 ::CreateService( |
| 65 if (FAILED(hr)) | 141 scm, m_szServiceName, m_szServiceName, SERVICE_ALL_ACCESS, |
| 66 return hr; | 142 SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, |
| 143 command_line.GetCommandLineString().c_str(), NULL, NULL, NULL, | |
| 144 L"NT AUTHORITY\\LocalService", NULL)); | |
| 67 | 145 |
| 68 if (!::ChangeServiceConfig(service, SERVICE_WIN32_OWN_PROCESS, | 146 if (!service.IsValid()) |
| 69 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, NULL, NULL, NULL, NULL, | |
| 70 L"NT AUTHORITY\\LocalService", NULL, NULL)) { | |
| 71 return HResultFromLastError(); | 147 return HResultFromLastError(); |
| 72 } | |
| 73 | 148 |
| 74 return S_OK; | 149 return S_OK; |
| 75 } | 150 } |
| 76 | 151 |
| 77 // Override to handle service uninstall case manually. | |
| 78 bool ParseCommandLine(LPCTSTR lpCmdLine, HRESULT* pnRetCode) { | 152 bool ParseCommandLine(LPCTSTR lpCmdLine, HRESULT* pnRetCode) { |
| 79 if (!Base::ParseCommandLine(lpCmdLine, pnRetCode)) | 153 CHECK(pnRetCode); |
| 80 return false; | 154 CommandLine command_line(CommandLine::NO_PROGRAM); |
| 81 | 155 command_line.ParseFromString(lpCmdLine); |
| 82 const wchar_t tokens[] = L"-/"; | 156 *pnRetCode = ParseCommandLine(command_line); |
| 83 *pnRetCode = S_OK; | 157 if (*pnRetCode == S_OK) |
| 84 | 158 return true; // Run as service. |
| 85 for (const wchar_t* cur = lpCmdLine; cur = FindOneOf(cur, tokens);) { | 159 if (FAILED(*pnRetCode)) { |
| 86 if (WordCmpI(cur, L"UninstallService") == 0) { | 160 LOG(ERROR) << "Operation failed. 0x" << std::setw(8) << |
| 87 if (!Uninstall()) | 161 std::setbase(16) << *pnRetCode; |
| 88 *pnRetCode = E_FAIL; | |
| 89 return false; | |
| 90 } else if (WordCmpI(cur, L"Start") == 0) { | |
| 91 *pnRetCode = StartService(); | |
| 92 return false; | |
| 93 } else if (WordCmpI(cur, L"Stop") == 0) { | |
| 94 *pnRetCode = StopService(); | |
| 95 return false; | |
| 96 } | |
| 97 } | 162 } |
| 98 return true; | 163 return false; // Exit process. |
| 99 } | 164 } |
| 100 | 165 |
| 101 HRESULT PreMessageLoop(int nShowCmd) { | 166 HRESULT PreMessageLoop(int nShowCmd) { |
| 102 HRESULT hr = Base::PreMessageLoop(nShowCmd); | 167 HRESULT hr = Base::PreMessageLoop(nShowCmd); |
| 103 if (FAILED(hr)) | 168 if (FAILED(hr)) |
| 104 return hr; | 169 return hr; |
| 105 | 170 |
| 106 hr = StartConnector(); | 171 hr = StartConnector(); |
| 107 if (FAILED(hr)) | 172 if (FAILED(hr)) |
| 108 return hr; | 173 return hr; |
| 109 | 174 |
| 110 LogEvent(_T("Service started/resumed")); | 175 LogEvent(_T("Service started/resumed")); |
| 111 SetServiceStatus(SERVICE_RUNNING); | 176 SetServiceStatus(SERVICE_RUNNING); |
| 112 return hr; | 177 return hr; |
| 113 } | 178 } |
| 114 | 179 |
| 115 HRESULT PostMessageLoop() { | 180 HRESULT PostMessageLoop() { |
| 116 StopConnector(); | 181 StopConnector(); |
| 117 return Base::PostMessageLoop(); | 182 return Base::PostMessageLoop(); |
| 118 } | 183 } |
| 119 | 184 |
| 120 private: | 185 private: |
| 186 // Returns S_OK if it's called as service. | |
| 187 // Returns S_FALSE on success or usage error. | |
| 188 // Returns error code for any other error. | |
| 189 HRESULT ParseCommandLine(const CommandLine& command_line) { | |
| 190 HRESULT hr = E_INVALIDARG; | |
| 191 if (command_line.HasSwitch(kStopSwitch)) { | |
| 192 hr = StopService(); | |
|
gene
2012/05/10 22:11:45
I think we shall return here no matter what StopSe
Vitaly Buka (NO REVIEWS)
2012/05/10 22:49:12
Done.
| |
| 193 if (FAILED(hr)) | |
| 194 return hr; | |
| 195 } | |
| 196 | |
| 197 if (command_line.HasSwitch(kUninstallSwitch)) { | |
| 198 hr = Uninstall() ? S_OK : E_FAIL; | |
| 199 if (FAILED(hr)) | |
|
gene
2012/05/10 22:11:45
Same as above here.
Vitaly Buka (NO REVIEWS)
2012/05/10 22:49:12
Done.
| |
| 200 return hr; | |
| 201 } | |
| 202 | |
| 203 if (command_line.HasSwitch(kInstallSwitch)) { | |
| 204 if (!command_line.HasSwitch(kUserDataDirSwitch)) { | |
| 205 InvalidUsage(); | |
| 206 return S_FALSE; | |
| 207 } | |
| 208 | |
| 209 FilePath data_dir = command_line.GetSwitchValuePath(kUserDataDirSwitch); | |
| 210 hr = Install(data_dir); | |
| 211 if (FAILED(hr)) | |
| 212 return hr; | |
| 213 } | |
| 214 | |
| 215 if (command_line.HasSwitch(kStartSwitch)) { | |
| 216 hr = StartService(); | |
| 217 if (FAILED(hr)) | |
| 218 return hr; | |
| 219 } | |
| 220 | |
| 221 // No main switch matched, so it was called by SCM or just usage error. | |
| 222 if (hr == E_INVALIDARG) { | |
| 223 if (command_line.HasSwitch(kUserDataDirSwitch)) | |
| 224 return S_OK; | |
| 225 InvalidUsage(); | |
| 226 } | |
| 227 | |
| 228 return S_FALSE; | |
| 229 } | |
| 230 | |
| 231 HRESULT OpenServiceManager(ServiceHandle* service_manager) { | |
| 232 if (!service_manager) | |
| 233 return E_POINTER; | |
| 234 | |
| 235 service_manager->Set(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)); | |
| 236 if (!service_manager->IsValid()) | |
| 237 return HResultFromLastError(); | |
| 238 | |
| 239 return S_OK; | |
| 240 } | |
| 241 | |
| 121 HRESULT OpenService(DWORD access, ServiceHandle* service) { | 242 HRESULT OpenService(DWORD access, ServiceHandle* service) { |
| 122 if (!service) | 243 if (!service) |
| 123 return E_POINTER; | 244 return E_POINTER; |
| 124 | 245 |
| 125 ServiceHandle scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)); | 246 ServiceHandle scm; |
| 126 if (!scm.IsValid()) | 247 HRESULT hr = OpenServiceManager(&scm); |
| 127 return HResultFromLastError(); | 248 if (FAILED(hr)) |
| 249 return hr; | |
| 128 | 250 |
| 129 service->Set(::OpenService(scm, m_szServiceName, access)); | 251 service->Set(::OpenService(scm, m_szServiceName, access)); |
| 130 | 252 |
| 131 if (!service->IsValid()) | 253 if (!service->IsValid()) |
| 132 return HResultFromLastError(); | 254 return HResultFromLastError(); |
| 133 | 255 |
| 134 return S_OK; | 256 return S_OK; |
| 135 } | 257 } |
| 136 | 258 |
| 137 HRESULT StartService() { | 259 HRESULT StartService() { |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 158 HRESULT StartConnector() { | 280 HRESULT StartConnector() { |
| 159 return S_OK; | 281 return S_OK; |
| 160 } | 282 } |
| 161 | 283 |
| 162 void StopConnector() { | 284 void StopConnector() { |
| 163 } | 285 } |
| 164 }; | 286 }; |
| 165 | 287 |
| 166 CloudPrintServiceModule _AtlModule; | 288 CloudPrintServiceModule _AtlModule; |
| 167 | 289 |
| 168 int WINAPI WinMain(__in HINSTANCE hInstance, | 290 int main() { |
| 169 __in HINSTANCE hPrevInstance, | 291 base::AtExitManager at_exit; |
| 170 __in LPSTR lpCmdLine, | 292 return _AtlModule.WinMain(0); |
| 171 __in int nCmdShow) { | |
| 172 return _AtlModule.WinMain(nCmdShow); | |
| 173 } | 293 } |
| 174 | |
| OLD | NEW |