server: Use server_get_file_info for all info classes not implemented on client side.
[wine.git] / dlls / schedsvc / svc_main.c
bloba7af96ec65a7fd6ea4fec722707db2657eb5f64b
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 job_info;
51 OVERLAPPED ov;
52 LARGE_INTEGER period;
53 struct
55 FILE_NOTIFY_INFORMATION data;
56 WCHAR name_buffer[MAX_PATH];
57 } info;
59 /* the buffer must be DWORD aligned */
60 C_ASSERT(!(sizeof(info) & 3));
62 TRACE("Starting...\n");
64 load_at_tasks();
65 check_missed_task_time();
67 htimer = CreateWaitableTimerW(NULL, FALSE, NULL);
68 if (htimer == NULL)
70 ERR("CreateWaitableTimer failed\n");
71 return -1;
74 GetWindowsDirectoryW(path, MAX_PATH);
75 lstrcatW(path, tasksW);
77 /* Just in case it's an old Wine prefix with missing c:\windows\tasks */
78 CreateDirectoryW(path, NULL);
80 htasks = CreateFileW(path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
81 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
82 if (htasks == INVALID_HANDLE_VALUE)
84 ERR("Couldn't start monitoring %s for tasks, error %u\n", debugstr_w(path), GetLastError());
85 return -1;
88 hjob_queue = CreateJobObjectW(NULL, NULL);
89 if (!hjob_queue)
91 ERR("CreateJobObject failed\n");
92 return -1;
95 hport = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
96 if (!hport)
98 ERR("CreateIoCompletionPort failed\n");
99 return -1;
102 job_info.CompletionKey = hjob_queue;
103 job_info.CompletionPort = hport;
104 if (!SetInformationJobObject(hjob_queue, JobObjectAssociateCompletionPortInformation, &job_info, sizeof(job_info)))
106 ERR("SetInformationJobObject failed\n");
107 return -1;
110 memset(&ov, 0, sizeof(ov));
111 ov.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
113 memset(&info, 0, sizeof(info));
114 ReadDirectoryChangesW(htasks, &info, sizeof(info), FALSE,
115 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE,
116 NULL, &ov, NULL);
118 for (;;)
120 HANDLE events[4];
121 DWORD ret;
123 events[0] = done_event;
124 events[1] = htimer;
125 events[2] = hport;
126 events[3] = ov.hEvent;
128 ret = WaitForMultipleObjects(4, events, FALSE, INFINITE);
129 /* Done event */
130 if (ret == WAIT_OBJECT_0) break;
132 /* Next runtime timer */
133 if (ret == WAIT_OBJECT_0 + 1)
135 check_task_time();
136 continue;
139 /* Job queue */
140 if (ret == WAIT_OBJECT_0 + 2)
142 DWORD msg;
143 ULONG_PTR dummy, pid;
145 if (GetQueuedCompletionStatus(hport, &msg, &dummy, (OVERLAPPED **)&pid, 0))
147 if (msg == JOB_OBJECT_MSG_EXIT_PROCESS)
149 TRACE("got message: process %#lx has terminated\n", pid);
150 update_process_status(pid);
152 else
153 FIXME("got message %#x from the job\n", msg);
155 continue;
158 if (info.data.NextEntryOffset)
159 FIXME("got multiple entries\n");
161 /* Directory change notification */
162 info.data.FileName[info.data.FileNameLength/sizeof(WCHAR)] = 0;
164 switch (info.data.Action)
166 case FILE_ACTION_ADDED:
167 TRACE("FILE_ACTION_ADDED %s\n", debugstr_w(info.data.FileName));
169 GetWindowsDirectoryW(path, MAX_PATH);
170 lstrcatW(path, tasksW);
171 lstrcatW(path, info.data.FileName);
172 add_job(path);
173 break;
175 case FILE_ACTION_REMOVED:
176 TRACE("FILE_ACTION_REMOVED %s\n", debugstr_w(info.data.FileName));
177 GetWindowsDirectoryW(path, MAX_PATH);
178 lstrcatW(path, tasksW);
179 lstrcatW(path, info.data.FileName);
180 remove_job(path);
181 break;
183 case FILE_ACTION_MODIFIED:
184 TRACE("FILE_ACTION_MODIFIED %s\n", debugstr_w(info.data.FileName));
186 GetWindowsDirectoryW(path, MAX_PATH);
187 lstrcatW(path, tasksW);
188 lstrcatW(path, info.data.FileName);
189 remove_job(path);
190 add_job(path);
191 break;
193 default:
194 FIXME("%s: action %#x not handled\n", debugstr_w(info.data.FileName), info.data.Action);
195 break;
198 check_task_state();
200 if (get_next_runtime(&period))
202 if (!SetWaitableTimer(htimer, &period, 0, NULL, NULL, FALSE))
203 ERR("SetWaitableTimer failed\n");
206 memset(&info, 0, sizeof(info));
207 if (!ReadDirectoryChangesW(htasks, &info, sizeof(info), FALSE,
208 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE,
209 NULL, &ov, NULL)) break;
212 CancelWaitableTimer(htimer);
213 CloseHandle(htimer);
214 CloseHandle(ov.hEvent);
215 CloseHandle(hport);
216 CloseHandle(hjob_queue);
217 CloseHandle(htasks);
219 TRACE("Finished.\n");
221 return 0;
224 void schedsvc_auto_start(void)
226 static DWORD start_type;
227 SC_HANDLE scm, service;
228 QUERY_SERVICE_CONFIGW *cfg;
229 DWORD cfg_size;
231 if (start_type == SERVICE_AUTO_START) return;
233 TRACE("changing service start type to SERVICE_AUTO_START\n");
235 scm = OpenSCManagerW(NULL, NULL, 0);
236 if (!scm)
238 WARN("failed to open SCM (%u)\n", GetLastError());
239 return;
242 service = OpenServiceW(scm, scheduleW, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
243 if (service)
245 if (!QueryServiceConfigW(service, NULL, 0, &cfg_size) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
247 cfg = HeapAlloc(GetProcessHeap(), 0, cfg_size);
248 if (cfg)
250 if (QueryServiceConfigW(service, cfg, cfg_size, &cfg_size))
252 start_type = cfg->dwStartType;
253 if (start_type != SERVICE_AUTO_START)
255 if (ChangeServiceConfigW(service, SERVICE_NO_CHANGE, SERVICE_AUTO_START, SERVICE_NO_CHANGE,
256 NULL, NULL, NULL, NULL, NULL, NULL, NULL))
257 start_type = SERVICE_AUTO_START;
260 HeapFree(GetProcessHeap(), 0, cfg);
263 else
264 WARN("failed to query service config (%u)\n", GetLastError());
266 CloseServiceHandle(service);
268 else
269 WARN("failed to open service (%u)\n", GetLastError());
271 CloseServiceHandle(scm);
274 static void schedsvc_update_status(DWORD state)
276 SERVICE_STATUS status;
278 status.dwServiceType = SERVICE_WIN32;
279 status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
280 status.dwWin32ExitCode = 0;
281 status.dwServiceSpecificExitCode = 0;
282 status.dwCheckPoint = 0;
283 status.dwWaitHint = 0;
284 status.dwControlsAccepted = 0;
285 status.dwCurrentState = state;
287 SetServiceStatus(schedsvc_handle, &status);
290 static void WINAPI schedsvc_handler(DWORD control)
292 TRACE("%#x\n", control);
294 switch (control)
296 case SERVICE_CONTROL_STOP:
297 case SERVICE_CONTROL_SHUTDOWN:
298 schedsvc_update_status(SERVICE_STOP_PENDING);
299 SetEvent(done_event);
300 break;
302 default:
303 schedsvc_update_status(SERVICE_RUNNING);
304 break;
308 static RPC_BINDING_VECTOR *sched_bindings;
310 static RPC_STATUS RPC_init(void)
312 static WCHAR ncacn_npW[] = { 'n','c','a','c','n','_','n','p',0 };
313 static WCHAR endpoint_npW[] = { '\\','p','i','p','e','\\','a','t','s','v','c',0 };
314 static WCHAR ncalrpcW[] = { 'n','c','a','l','r','p','c',0 };
315 static WCHAR endpoint_lrpcW[] = { 'a','t','s','v','c',0 };
316 RPC_STATUS status;
318 status = RpcServerRegisterIf(ITaskSchedulerService_v1_0_s_ifspec, NULL, NULL);
319 if (status != RPC_S_OK)
321 ERR("RpcServerRegisterIf error %#x\n", status);
322 return status;
325 status = RpcServerRegisterIf(atsvc_v1_0_s_ifspec, NULL, NULL);
326 if (status != RPC_S_OK)
328 ERR("RpcServerRegisterIf error %#x\n", status);
329 RpcServerUnregisterIf(ITaskSchedulerService_v1_0_s_ifspec, NULL, FALSE);
330 return status;
333 status = RpcServerUseProtseqEpW(ncacn_npW, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, endpoint_npW, NULL);
334 if (status != RPC_S_OK)
336 ERR("RpcServerUseProtseqEp error %#x\n", status);
337 return status;
340 status = RpcServerUseProtseqEpW(ncalrpcW, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, endpoint_lrpcW, NULL);
341 if (status != RPC_S_OK)
343 ERR("RpcServerUseProtseqEp error %#x\n", status);
344 return status;
347 status = RpcServerInqBindings(&sched_bindings);
348 if (status != RPC_S_OK)
350 ERR("RpcServerInqBindings error %#x\n", status);
351 return status;
354 status = RpcEpRegisterW(ITaskSchedulerService_v1_0_s_ifspec, sched_bindings, NULL, NULL);
355 if (status != RPC_S_OK)
357 ERR("RpcEpRegister error %#x\n", status);
358 return status;
361 status = RpcEpRegisterW(atsvc_v1_0_s_ifspec, sched_bindings, NULL, NULL);
362 if (status != RPC_S_OK)
364 ERR("RpcEpRegister error %#x\n", status);
365 return status;
368 status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
369 if (status != RPC_S_OK)
371 ERR("RpcServerListen error %#x\n", status);
372 return status;
374 return RPC_S_OK;
377 static void RPC_finish(void)
379 RpcMgmtStopServerListening(NULL);
380 RpcEpUnregister(ITaskSchedulerService_v1_0_s_ifspec, sched_bindings, NULL);
381 RpcEpUnregister(atsvc_v1_0_s_ifspec, sched_bindings, NULL);
382 RpcBindingVectorFree(&sched_bindings);
383 RpcServerUnregisterIf(ITaskSchedulerService_v1_0_s_ifspec, NULL, FALSE);
384 RpcServerUnregisterIf(atsvc_v1_0_s_ifspec, NULL, FALSE);
387 void WINAPI ServiceMain(DWORD argc, LPWSTR *argv)
389 HANDLE thread;
390 DWORD tid;
392 TRACE("starting Task Scheduler Service\n");
394 schedsvc_handle = RegisterServiceCtrlHandlerW(scheduleW, schedsvc_handler);
395 if (!schedsvc_handle)
397 ERR("RegisterServiceCtrlHandler error %d\n", GetLastError());
398 return;
401 schedsvc_update_status(SERVICE_START_PENDING);
403 done_event = CreateEventW(NULL, TRUE, FALSE, NULL);
404 thread = CreateThread(NULL, 0, tasks_monitor_thread, NULL, 0, &tid);
406 if (thread && RPC_init() == RPC_S_OK)
408 schedsvc_update_status(SERVICE_RUNNING);
409 WaitForSingleObject(thread, INFINITE);
410 CloseHandle(thread);
411 RPC_finish();
414 schedsvc_update_status(SERVICE_STOPPED);
416 TRACE("exiting Task Scheduler Service\n");
419 void __RPC_FAR * __RPC_USER MIDL_user_allocate(SIZE_T len)
421 return heap_alloc(len);
424 void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
426 heap_free(ptr);