Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. *
  3. * This is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This software is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this software; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  16. * USA.
  17. */
  18. // -=- Service.cxx
  19. #include <rfb_win32/Service.h>
  20. #include <rfb_win32/MsgWindow.h>
  21. #include <rfb_win32/ModuleFileName.h>
  22. #include <rfb_win32/Registry.h>
  23. #include <rfb_win32/Handle.h>
  24. #include <logmessages/messages.h>
  25. #include <rdr/Exception.h>
  26. #include <rfb/LogWriter.h>
  27. using namespace rdr;
  28. using namespace rfb;
  29. using namespace win32;
  30. static LogWriter vlog("Service");
  31. // - Internal service implementation functions
  32. Service* service = 0;
  33. bool runAsService = false;
  34. VOID WINAPI serviceHandler(DWORD control) {
  35. switch (control) {
  36. case SERVICE_CONTROL_INTERROGATE:
  37. vlog.info("cmd: report status");
  38. service->setStatus();
  39. return;
  40. case SERVICE_CONTROL_PARAMCHANGE:
  41. vlog.info("cmd: param change");
  42. service->readParams();
  43. return;
  44. case SERVICE_CONTROL_SHUTDOWN:
  45. vlog.info("cmd: OS shutdown");
  46. service->osShuttingDown();
  47. return;
  48. case SERVICE_CONTROL_STOP:
  49. vlog.info("cmd: stop");
  50. service->setStatus(SERVICE_STOP_PENDING);
  51. service->stop();
  52. return;
  53. };
  54. vlog.debug("cmd: unknown %lu", control);
  55. }
  56. // -=- Service main procedure
  57. VOID WINAPI serviceProc(DWORD dwArgc, LPTSTR* lpszArgv) {
  58. vlog.debug("entering %s serviceProc", service->getName());
  59. vlog.info("registering handler...");
  60. service->status_handle = RegisterServiceCtrlHandler(service->getName(), serviceHandler);
  61. if (!service->status_handle) {
  62. DWORD err = GetLastError();
  63. vlog.error("failed to register handler: %lu", err);
  64. ExitProcess(err);
  65. }
  66. vlog.debug("registered handler (%p)", service->status_handle);
  67. service->setStatus(SERVICE_START_PENDING);
  68. vlog.debug("entering %s serviceMain", service->getName());
  69. service->status.dwWin32ExitCode = service->serviceMain(dwArgc, lpszArgv);
  70. vlog.debug("leaving %s serviceMain", service->getName());
  71. service->setStatus(SERVICE_STOPPED);
  72. }
  73. // -=- Service
  74. Service::Service(const TCHAR* name_) : name(name_) {
  75. vlog.debug("Service");
  76. status_handle = 0;
  77. status.dwControlsAccepted = SERVICE_CONTROL_INTERROGATE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
  78. status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
  79. status.dwWin32ExitCode = NO_ERROR;
  80. status.dwServiceSpecificExitCode = 0;
  81. status.dwCheckPoint = 0;
  82. status.dwWaitHint = 30000;
  83. status.dwCurrentState = SERVICE_STOPPED;
  84. }
  85. void
  86. Service::start() {
  87. SERVICE_TABLE_ENTRY entry[2];
  88. entry[0].lpServiceName = (TCHAR*)name;
  89. entry[0].lpServiceProc = serviceProc;
  90. entry[1].lpServiceName = NULL;
  91. entry[1].lpServiceProc = NULL;
  92. vlog.debug("entering dispatcher");
  93. if (!SetProcessShutdownParameters(0x100, 0))
  94. vlog.error("unable to set shutdown parameters: %lu", GetLastError());
  95. service = this;
  96. if (!StartServiceCtrlDispatcher(entry))
  97. throw SystemException("unable to start service", GetLastError());
  98. }
  99. void
  100. Service::setStatus() {
  101. setStatus(status.dwCurrentState);
  102. }
  103. void
  104. Service::setStatus(DWORD state) {
  105. if (status_handle == 0) {
  106. vlog.debug("warning - cannot setStatus");
  107. return;
  108. }
  109. status.dwCurrentState = state;
  110. status.dwCheckPoint++;
  111. if (!SetServiceStatus(status_handle, &status)) {
  112. status.dwCurrentState = SERVICE_STOPPED;
  113. status.dwWin32ExitCode = GetLastError();
  114. vlog.error("unable to set service status:%lu", status.dwWin32ExitCode);
  115. }
  116. vlog.debug("set status to %lu(%lu)", state, status.dwCheckPoint);
  117. }
  118. Service::~Service() {
  119. vlog.debug("~Service");
  120. service = 0;
  121. }
  122. // Find out whether this process is running as the WinVNC service
  123. bool thisIsService() {
  124. return service && (service->status.dwCurrentState != SERVICE_STOPPED);
  125. }
  126. // -=- Desktop handling code
  127. // Switch the current thread to the specified desktop
  128. static bool
  129. switchToDesktop(HDESK desktop) {
  130. HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());
  131. if (!SetThreadDesktop(desktop)) {
  132. vlog.debug("switchToDesktop failed:%lu", GetLastError());
  133. return false;
  134. }
  135. if (!CloseDesktop(old_desktop))
  136. vlog.debug("unable to close old desktop:%lu", GetLastError());
  137. return true;
  138. }
  139. // Determine whether the thread's current desktop is the input one
  140. static bool
  141. inputDesktopSelected() {
  142. HDESK current = GetThreadDesktop(GetCurrentThreadId());
  143. HDESK input = OpenInputDesktop(0, FALSE,
  144. DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
  145. DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
  146. DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
  147. DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
  148. if (!input) {
  149. vlog.debug("unable to OpenInputDesktop(1):%lu", GetLastError());
  150. return false;
  151. }
  152. DWORD size;
  153. char currentname[256];
  154. char inputname[256];
  155. if (!GetUserObjectInformation(current, UOI_NAME, currentname, 256, &size)) {
  156. vlog.debug("unable to GetUserObjectInformation(1):%lu", GetLastError());
  157. CloseDesktop(input);
  158. return false;
  159. }
  160. if (!GetUserObjectInformation(input, UOI_NAME, inputname, 256, &size)) {
  161. vlog.debug("unable to GetUserObjectInformation(2):%lu", GetLastError());
  162. CloseDesktop(input);
  163. return false;
  164. }
  165. if (!CloseDesktop(input))
  166. vlog.debug("unable to close input desktop:%lu", GetLastError());
  167. // *** vlog.debug("current=%s, input=%s", currentname, inputname);
  168. bool result = strcmp(currentname, inputname) == 0;
  169. return result;
  170. }
  171. // Switch the current thread into the input desktop
  172. static bool
  173. selectInputDesktop() {
  174. // - Open the input desktop
  175. HDESK desktop = OpenInputDesktop(0, FALSE,
  176. DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
  177. DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
  178. DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
  179. DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
  180. if (!desktop) {
  181. vlog.debug("unable to OpenInputDesktop(2):%lu", GetLastError());
  182. return false;
  183. }
  184. // - Switch into it
  185. if (!switchToDesktop(desktop)) {
  186. CloseDesktop(desktop);
  187. return false;
  188. }
  189. // ***
  190. DWORD size = 256;
  191. char currentname[256];
  192. if (GetUserObjectInformation(desktop, UOI_NAME, currentname, 256, &size)) {
  193. vlog.debug("switched to %s", currentname);
  194. }
  195. // ***
  196. vlog.debug("switched to input desktop");
  197. return true;
  198. }
  199. // -=- Access points to desktop-switching routines
  200. bool
  201. rfb::win32::desktopChangeRequired() {
  202. return !inputDesktopSelected();
  203. }
  204. bool
  205. rfb::win32::changeDesktop() {
  206. return selectInputDesktop();
  207. }
  208. // -=- Ctrl-Alt-Del emulation
  209. bool
  210. rfb::win32::emulateCtrlAltDel() {
  211. rfb::win32::Handle sessionEventCad =
  212. CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNCCad");
  213. SetEvent(sessionEventCad);
  214. return true;
  215. }
  216. // -=- Application Event Log target Logger class
  217. class Logger_EventLog : public Logger {
  218. public:
  219. Logger_EventLog(const TCHAR* srcname) : Logger("EventLog") {
  220. eventlog = RegisterEventSource(NULL, srcname);
  221. if (!eventlog)
  222. printf("Unable to open event log:%ld\n", GetLastError());
  223. }
  224. ~Logger_EventLog() {
  225. if (eventlog)
  226. DeregisterEventSource(eventlog);
  227. }
  228. virtual void write(int level, const char *logname, const char *message) {
  229. if (!eventlog) return;
  230. TStr log(logname), msg(message);
  231. const TCHAR* strings[] = {log, msg};
  232. WORD type = EVENTLOG_INFORMATION_TYPE;
  233. if (level == 0) type = EVENTLOG_ERROR_TYPE;
  234. if (!ReportEvent(eventlog, type, 0, VNC4LogMessage, NULL, 2, 0, strings, NULL)) {
  235. // *** It's not at all clear what is the correct behaviour if this fails...
  236. printf("ReportEvent failed:%ld\n", GetLastError());
  237. }
  238. }
  239. protected:
  240. HANDLE eventlog;
  241. };
  242. static Logger_EventLog* logger = 0;
  243. bool rfb::win32::initEventLogLogger(const TCHAR* srcname) {
  244. if (logger)
  245. return false;
  246. logger = new Logger_EventLog(srcname);
  247. logger->registerLogger();
  248. return true;
  249. }
  250. // -=- Registering and unregistering the service
  251. bool rfb::win32::registerService(const TCHAR* name,
  252. const TCHAR* display,
  253. const TCHAR* desc,
  254. int argc, char** argv) {
  255. // - Initialise the default service parameters
  256. const TCHAR* defaultcmdline;
  257. defaultcmdline = _T("-service");
  258. // - Get the full pathname of our executable
  259. ModuleFileName buffer;
  260. // - Calculate the command-line length
  261. int cmdline_len = _tcslen(buffer.buf) + 4;
  262. int i;
  263. for (i=0; i<argc; i++) {
  264. cmdline_len += strlen(argv[i]) + 3;
  265. }
  266. // - Add the supplied extra parameters to the command line
  267. TCharArray cmdline(cmdline_len+_tcslen(defaultcmdline));
  268. _stprintf(cmdline.buf, _T("\"%s\" %s"), buffer.buf, defaultcmdline);
  269. for (i=0; i<argc; i++) {
  270. _tcscat(cmdline.buf, _T(" \""));
  271. _tcscat(cmdline.buf, TStr(argv[i]));
  272. _tcscat(cmdline.buf, _T("\""));
  273. }
  274. // - Register the service
  275. // - Open the SCM
  276. ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
  277. if (!scm)
  278. throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
  279. // - Add the service
  280. ServiceHandle service = CreateService(scm,
  281. name, display, SC_MANAGER_ALL_ACCESS,
  282. SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
  283. SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
  284. cmdline.buf, NULL, NULL, NULL, NULL, NULL);
  285. if (!service)
  286. throw rdr::SystemException("unable to create service", GetLastError());
  287. // - Set a description
  288. SERVICE_DESCRIPTION sdesc = {(LPTSTR)desc};
  289. ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &sdesc);
  290. // - Register the event log source
  291. RegKey hk, hk2;
  292. hk2.createKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"));
  293. hk.createKey(hk2, name);
  294. for (i=_tcslen(buffer.buf); i>0; i--) {
  295. if (buffer.buf[i] == _T('\\')) {
  296. buffer.buf[i+1] = 0;
  297. break;
  298. }
  299. }
  300. const TCHAR* dllFilename = _T("logmessages.dll");
  301. TCharArray dllPath(_tcslen(buffer.buf) + _tcslen(dllFilename) + 1);
  302. _tcscpy(dllPath.buf, buffer.buf);
  303. _tcscat(dllPath.buf, dllFilename);
  304. hk.setExpandString(_T("EventMessageFile"), dllPath.buf);
  305. hk.setInt(_T("TypesSupported"), EVENTLOG_ERROR_TYPE | EVENTLOG_INFORMATION_TYPE);
  306. Sleep(500);
  307. return true;
  308. }
  309. bool rfb::win32::unregisterService(const TCHAR* name) {
  310. // - Open the SCM
  311. ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
  312. if (!scm)
  313. throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
  314. // - Create the service
  315. ServiceHandle service = OpenService(scm, name, SC_MANAGER_ALL_ACCESS);
  316. if (!service)
  317. throw rdr::SystemException("unable to locate the service", GetLastError());
  318. if (!DeleteService(service))
  319. throw rdr::SystemException("unable to remove the service", GetLastError());
  320. // - Register the event log source
  321. RegKey hk;
  322. hk.openKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"));
  323. hk.deleteKey(name);
  324. Sleep(500);
  325. return true;
  326. }
  327. // -=- Starting and stopping the service
  328. bool rfb::win32::startService(const TCHAR* name) {
  329. // - Open the SCM
  330. ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  331. if (!scm)
  332. throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
  333. // - Locate the service
  334. ServiceHandle service = OpenService(scm, name, SERVICE_START);
  335. if (!service)
  336. throw rdr::SystemException("unable to open the service", GetLastError());
  337. // - Start the service
  338. if (!StartService(service, 0, NULL))
  339. throw rdr::SystemException("unable to start the service", GetLastError());
  340. Sleep(500);
  341. return true;
  342. }
  343. bool rfb::win32::stopService(const TCHAR* name) {
  344. // - Open the SCM
  345. ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  346. if (!scm)
  347. throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
  348. // - Locate the service
  349. ServiceHandle service = OpenService(scm, name, SERVICE_STOP);
  350. if (!service)
  351. throw rdr::SystemException("unable to open the service", GetLastError());
  352. // - Start the service
  353. SERVICE_STATUS status;
  354. if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
  355. throw rdr::SystemException("unable to stop the service", GetLastError());
  356. Sleep(500);
  357. return true;
  358. }
  359. DWORD rfb::win32::getServiceState(const TCHAR* name) {
  360. // - Open the SCM
  361. ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  362. if (!scm)
  363. throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
  364. // - Locate the service
  365. ServiceHandle service = OpenService(scm, name, SERVICE_INTERROGATE);
  366. if (!service)
  367. throw rdr::SystemException("unable to open the service", GetLastError());
  368. // - Get the service status
  369. SERVICE_STATUS status;
  370. if (!ControlService(service, SERVICE_CONTROL_INTERROGATE, (SERVICE_STATUS*)&status))
  371. throw rdr::SystemException("unable to query the service", GetLastError());
  372. return status.dwCurrentState;
  373. }
  374. char* rfb::win32::serviceStateName(DWORD state) {
  375. switch (state) {
  376. case SERVICE_RUNNING: return strDup("Running");
  377. case SERVICE_STOPPED: return strDup("Stopped");
  378. case SERVICE_STOP_PENDING: return strDup("Stopping");
  379. };
  380. CharArray tmp(32);
  381. sprintf(tmp.buf, "Unknown (%lu)", state);
  382. return tmp.takeBuf();
  383. }
  384. bool rfb::win32::isServiceProcess() {
  385. return runAsService;
  386. }