winex11.drv: Refcount the vulkan surface window.
[wine.git] / dlls / schedsvc / svc_main.c
blob8e23a049db69639ce0feb500073e2ca1c5dca051
1 /*
2 * Task Scheduler Service
4 * Copyright 2014 Dmitry Timoshkov
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winsvc.h"
26 #include "rpc.h"
27 #include "atsvc.h"
28 #include "schrpc.h"
29 #include "wine/debug.h"
31 #include "schedsvc_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(schedsvc);
35 static const WCHAR scheduleW[] = {'S','c','h','e','d','u','l','e',0};
36 static SERVICE_STATUS_HANDLE schedsvc_handle;
37 static HANDLE done_event, hjob_queue;
39 void add_process_to_queue(HANDLE process)
41 if (!AssignProcessToJobObject(hjob_queue, process))
42 ERR("AssignProcessToJobObject failed\n");
45 static DWORD WINAPI tasks_monitor_thread(void *arg)
47 static const WCHAR tasksW[] = { '\\','T','a','s','k','s','\\',0 };
48 WCHAR path[MAX_PATH];
49 HANDLE htasks, hport, htimer;
50 JOBOBJECT_ASSOCIATE_COMPLETION_PORT info;
51 OVERLAPPED ov;
52 LARGE_INTEGER period;
54 TRACE("Starting...\n");
56 load_at_tasks();
57 check_missed_task_time();
59 htimer = CreateWaitableTimerW(NULL, FALSE, NULL);
60 if (htimer == NULL)
62 ERR("CreateWaitableTimer failed\n");
63 return -1;
66 GetWindowsDirectoryW(path, MAX_PATH);
67 lstrcatW(path, tasksW);
69 htasks = CreateFileW(path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
70 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
71 if (htasks == INVALID_HANDLE_VALUE)
73 ERR("Couldn't start monitoring %s for tasks, error %u\n", debugstr_w(path), GetLastError());
74 /* Probably this is an old prefix with disabled updates */
75 if (GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_FILE_NOT_FOUND)
76 ERR("Please create the directory manually\n");
77 return -1;
80 hjob_queue = CreateJobObjectW(NULL, NULL);
81 if (!hjob_queue)
83 ERR("CreateJobObject failed\n");
84 return -1;
87 hport = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
88 if (!hport)
90 ERR("CreateIoCompletionPort failed\n");
91 return -1;
94 info.CompletionKey = hjob_queue;
95 info.CompletionPort = hport;
96 if (!SetInformationJobObject(hjob_queue, JobObjectAssociateCompletionPortInformation, &info, sizeof(info)))
98 ERR("SetInformationJobObject failed\n");
99 return -1;
102 memset(&ov, 0, sizeof(ov));
103 ov.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
105 for (;;)
107 struct
109 FILE_NOTIFY_INFORMATION data;
110 WCHAR name_buffer[MAX_PATH];
111 } info;
112 HANDLE events[4];
113 DWORD ret;
115 /* the buffer must be DWORD aligned */
116 C_ASSERT(!(sizeof(info) & 3));
118 memset(&info, 0, sizeof(info));
120 ret = ReadDirectoryChangesW(htasks, &info, sizeof(info), FALSE,
121 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE,
122 NULL, &ov, NULL);
123 if (!ret) break;
125 if (info.data.NextEntryOffset)
126 FIXME("got multiple entries\n");
128 events[0] = done_event;
129 events[1] = htimer;
130 events[2] = hport;
131 events[3] = ov.hEvent;
133 ret = WaitForMultipleObjects(4, events, FALSE, INFINITE);
134 /* Done event */
135 if (ret == WAIT_OBJECT_0) break;
137 /* Next runtime timer */
138 if (ret == WAIT_OBJECT_0 + 1)
140 check_task_time();
141 continue;
144 /* Job queue */
145 if (ret == WAIT_OBJECT_0 + 2)
147 DWORD msg;
148 ULONG_PTR dummy, pid;
150 if (GetQueuedCompletionStatus(hport, &msg, &dummy, (OVERLAPPED **)&pid, 0))
152 if (msg == JOB_OBJECT_MSG_EXIT_PROCESS)
154 TRACE("got message: process %#lx has terminated\n", pid);
155 update_process_status(pid);
157 else
158 FIXME("got message %#x from the job\n", msg);
160 continue;
163 /* Directory change notification */
164 info.data.FileName[info.data.FileNameLength/sizeof(WCHAR)] = 0;
166 switch (info.data.Action)
168 case FILE_ACTION_ADDED:
169 TRACE("FILE_ACTION_ADDED %s\n", debugstr_w(info.data.FileName));
171 GetWindowsDirectoryW(path, MAX_PATH);
172 lstrcatW(path, tasksW);
173 lstrcatW(path, info.data.FileName);
174 add_job(path);
175 break;
177 case FILE_ACTION_REMOVED:
178 TRACE("FILE_ACTION_REMOVED %s\n", debugstr_w(info.data.FileName));
179 GetWindowsDirectoryW(path, MAX_PATH);
180 lstrcatW(path, tasksW);
181 lstrcatW(path, info.data.FileName);
182 remove_job(path);
183 break;
185 case FILE_ACTION_MODIFIED:
186 TRACE("FILE_ACTION_MODIFIED %s\n", debugstr_w(info.data.FileName));
188 GetWindowsDirectoryW(path, MAX_PATH);
189 lstrcatW(path, tasksW);
190 lstrcatW(path, info.data.FileName);
191 remove_job(path);
192 add_job(path);
193 break;
195 default:
196 FIXME("%s: action %#x not handled\n", debugstr_w(info.data.FileName), info.data.Action);
197 break;
200 check_task_state();
202 if (get_next_runtime(&period))
204 if (!SetWaitableTimer(htimer, &period, 0, NULL, NULL, FALSE))
205 ERR("SetWaitableTimer failed\n");
209 CancelWaitableTimer(htimer);
210 CloseHandle(htimer);
211 CloseHandle(ov.hEvent);
212 CloseHandle(hport);
213 CloseHandle(hjob_queue);
214 CloseHandle(htasks);
216 TRACE("Finished.\n");
218 return 0;
221 void schedsvc_auto_start(void)
223 static DWORD start_type;
224 SC_HANDLE scm, service;
225 QUERY_SERVICE_CONFIGW *cfg;
226 DWORD cfg_size;
228 if (start_type == SERVICE_AUTO_START) return;
230 TRACE("changing service start type to SERVICE_AUTO_START\n");
232 scm = OpenSCManagerW(NULL, NULL, 0);
233 if (!scm)
235 WARN("failed to open SCM (%u)\n", GetLastError());
236 return;
239 service = OpenServiceW(scm, scheduleW, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
240 if (service)
242 if (!QueryServiceConfigW(service, NULL, 0, &cfg_size) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
244 cfg = HeapAlloc(GetProcessHeap(), 0, cfg_size);
245 if (cfg)
247 if (QueryServiceConfigW(service, cfg, cfg_size, &cfg_size))
249 start_type = cfg->dwStartType;
250 if (start_type != SERVICE_AUTO_START)
252 if (ChangeServiceConfigW(service, SERVICE_NO_CHANGE, SERVICE_AUTO_START, SERVICE_NO_CHANGE,
253 NULL, NULL, NULL, NULL, NULL, NULL, NULL))
254 start_type = SERVICE_AUTO_START;
257 HeapFree(GetProcessHeap(), 0, cfg);
260 else
261 WARN("failed to query service config (%u)\n", GetLastError());
263 CloseServiceHandle(service);
265 else
266 WARN("failed to open service (%u)\n", GetLastError());
268 CloseServiceHandle(scm);
271 static void schedsvc_update_status(DWORD state)
273 SERVICE_STATUS status;
275 status.dwServiceType = SERVICE_WIN32;
276 status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
277 status.dwWin32ExitCode = 0;
278 status.dwServiceSpecificExitCode = 0;
279 status.dwCheckPoint = 0;
280 status.dwWaitHint = 0;
281 status.dwControlsAccepted = 0;
282 status.dwCurrentState = state;
284 SetServiceStatus(schedsvc_handle, &status);
287 static void WINAPI schedsvc_handler(DWORD control)
289 TRACE("%#x\n", control);
291 switch (control)
293 case SERVICE_CONTROL_STOP:
294 case SERVICE_CONTROL_SHUTDOWN:
295 schedsvc_update_status(SERVICE_STOP_PENDING);
296 SetEvent(done_event);
297 break;
299 default:
300 schedsvc_update_status(SERVICE_RUNNING);
301 break;
305 static RPC_BINDING_VECTOR *sched_bindings;
307 static RPC_STATUS RPC_init(void)
309 static WCHAR ncacn_npW[] = { 'n','c','a','c','n','_','n','p',0 };
310 static WCHAR endpoint_npW[] = { '\\','p','i','p','e','\\','a','t','s','v','c',0 };
311 static WCHAR ncalrpcW[] = { 'n','c','a','l','r','p','c',0 };
312 static WCHAR endpoint_lrpcW[] = { 'a','t','s','v','c',0 };
313 RPC_STATUS status;
315 status = RpcServerRegisterIf(ITaskSchedulerService_v1_0_s_ifspec, NULL, NULL);
316 if (status != RPC_S_OK)
318 ERR("RpcServerRegisterIf error %#x\n", status);
319 return status;
322 status = RpcServerRegisterIf(atsvc_v1_0_s_ifspec, NULL, NULL);
323 if (status != RPC_S_OK)
325 ERR("RpcServerRegisterIf error %#x\n", status);
326 RpcServerUnregisterIf(ITaskSchedulerService_v1_0_s_ifspec, NULL, FALSE);
327 return status;
330 status = RpcServerUseProtseqEpW(ncacn_npW, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, endpoint_npW, NULL);
331 if (status != RPC_S_OK)
333 ERR("RpcServerUseProtseqEp error %#x\n", status);
334 return status;
337 status = RpcServerUseProtseqEpW(ncalrpcW, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, endpoint_lrpcW, NULL);
338 if (status != RPC_S_OK)
340 ERR("RpcServerUseProtseqEp error %#x\n", status);
341 return status;
344 status = RpcServerInqBindings(&sched_bindings);
345 if (status != RPC_S_OK)
347 ERR("RpcServerInqBindings error %#x\n", status);
348 return status;
351 status = RpcEpRegisterW(ITaskSchedulerService_v1_0_s_ifspec, sched_bindings, NULL, NULL);
352 if (status != RPC_S_OK)
354 ERR("RpcEpRegister error %#x\n", status);
355 return status;
358 status = RpcEpRegisterW(atsvc_v1_0_s_ifspec, sched_bindings, NULL, NULL);
359 if (status != RPC_S_OK)
361 ERR("RpcEpRegister error %#x\n", status);
362 return status;
365 status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
366 if (status != RPC_S_OK)
368 ERR("RpcServerListen error %#x\n", status);
369 return status;
371 return RPC_S_OK;
374 static void RPC_finish(void)
376 RpcMgmtStopServerListening(NULL);
377 RpcEpUnregister(ITaskSchedulerService_v1_0_s_ifspec, sched_bindings, NULL);
378 RpcEpUnregister(atsvc_v1_0_s_ifspec, sched_bindings, NULL);
379 RpcBindingVectorFree(&sched_bindings);
380 RpcServerUnregisterIf(ITaskSchedulerService_v1_0_s_ifspec, NULL, FALSE);
381 RpcServerUnregisterIf(atsvc_v1_0_s_ifspec, NULL, FALSE);
384 void WINAPI ServiceMain(DWORD argc, LPWSTR *argv)
386 HANDLE thread;
387 DWORD tid;
389 TRACE("starting Task Scheduler Service\n");
391 schedsvc_handle = RegisterServiceCtrlHandlerW(scheduleW, schedsvc_handler);
392 if (!schedsvc_handle)
394 ERR("RegisterServiceCtrlHandler error %d\n", GetLastError());
395 return;
398 schedsvc_update_status(SERVICE_START_PENDING);
400 done_event = CreateEventW(NULL, TRUE, FALSE, NULL);
401 thread = CreateThread(NULL, 0, tasks_monitor_thread, NULL, 0, &tid);
403 if (thread && RPC_init() == RPC_S_OK)
405 schedsvc_update_status(SERVICE_RUNNING);
406 WaitForSingleObject(thread, INFINITE);
407 CloseHandle(thread);
408 RPC_finish();
411 schedsvc_update_status(SERVICE_STOPPED);
413 TRACE("exiting Task Scheduler Service\n");
416 void __RPC_FAR * __RPC_USER MIDL_user_allocate(SIZE_T len)
418 return heap_alloc(len);
421 void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
423 heap_free(ptr);