Merge from mainline.
[bpt/emacs.git] / src / w32notify.c
CommitLineData
6f011d81
EZ
1/* Filesystem notifications support for GNU Emacs on the Microsoft Windows API.
2 Copyright (C) 2012 Free Software Foundation, Inc.
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
18
19#include <stddef.h>
20#include <errno.h>
21
22/* must include CRT headers *before* config.h */
23#include <config.h>
24
25#include <windows.h>
26
27#include "lisp.h"
28#include "w32term.h" /* for enter_crit/leave_crit and WM_EMACS_FILENOTIFY */
29#include "w32heap.h" /* for OS version data */
30#include "w32.h" /* for w32_strerror */
31#include "coding.h"
32#include "keyboard.h"
33#include "frame.h" /* needed by termhooks.h */
34#include "termhooks.h" /* for FILE_NOTIFY_EVENT */
35
36struct notification {
37 BYTE *buf; /* buffer for ReadDirectoryChangesW */
38 OVERLAPPED *io_info; /* the OVERLAPPED structure for async I/O */
39 BOOL subtree; /* whether to watch subdirectories */
40 DWORD filter; /* bit mask for events to watch */
41 char *watchee; /* the file we are interested in */
42 HANDLE dir; /* handle to the watched directory */
43 HANDLE thr; /* handle to the thread that watches */
44 int terminate; /* if non-zero, request for the thread to terminate */
45};
46
47/* FIXME: this needs to be changed to support more that one request at
48 a time. */
49static struct notification dirwatch;
50
51/* Used for communicating notifications to the main thread. */
52int notification_buffer_in_use;
53BYTE file_notifications[16384];
54DWORD notifications_size;
55HANDLE notifications_desc;
56
57static Lisp_Object Qfile_name, Qdirectory_name, Qattributes, Qsize;
58static Lisp_Object Qlast_write_time, Qlast_access_time, Qcreation_time;
59static Lisp_Object Qsecurity_desc, Qsubtree, watch_list;
60
61#if 0
62/* FIXME: Debugging code, should be removed eventually. */
63const wchar_t *
64format_file_action (DWORD action)
65{
66 static const wchar_t *action_str[] =
67 { L"???", L"Added", L"Removed", L"Modified", L"Renamed from", L"Renamed to" };
68
69 if (action >= sizeof(action_str)/sizeof(action_str[0]))
70 action = 0;
71 return action_str[action];
72}
73
74void
75parse_notifications (BYTE *info, DWORD info_size)
76{
77 BYTE *p = info;
78 FILE_NOTIFY_INFORMATION *fni = (PFILE_NOTIFY_INFORMATION)p;
79 const DWORD min_size
80 = offsetof (FILE_NOTIFY_INFORMATION, FileName) + sizeof(wchar_t);
81
82 if (!info_size)
83 {
84 printf ("No info in notifications!\n");
85 return;
86 }
87
88 while (info_size >= min_size)
89 {
90 wchar_t *fn = alloca (fni->FileNameLength + sizeof(wchar_t));
91 const wchar_t *action_str;
92
93 memcpy (fn, fni->FileName, fni->FileNameLength);
94 fn[fni->FileNameLength/sizeof(wchar_t)] = 0;
95 action_str = format_file_action (fni->Action);
96 wprintf (L"%s: %s\n", action_str, fn);
97 if (!fni->NextEntryOffset)
98 break;
99 p += fni->NextEntryOffset;
100 fni = (PFILE_NOTIFY_INFORMATION)p;
101 info_size -= fni->NextEntryOffset;
102 }
103}
104#endif /* debugging code */
105
106/* Signal to the main thread that we have file notifications for it to
107 process. */
108static void
109send_notifications (BYTE *info, DWORD info_size, HANDLE hdir, int *terminate)
110{
111 int done = 0;
112 FRAME_PTR f = SELECTED_FRAME ();
113
114 /* Too bad, but PostMessage will not work in non-GUI sessions.
115 FIXME. */
116 if (!FRAME_W32_P (f))
117 return;
118
119 /* A single buffer is used to communicate all notifications to the
120 main thread. Since both the main thread and several watcher
121 threads could be active at the same time, we use a critical area
122 and an "in-use" flag to synchronize them. A watcher thread can
123 only put its notifications in the buffer if it acquires the
124 critical area and finds the "in-use" flag reset. The main thread
125 resets the flag after it is done processing notifications.
126
127 FIXME: is there a better way of dealing with this? */
128 while (!done && !*terminate)
129 {
130 enter_crit ();
131 if (!notification_buffer_in_use)
132 {
133 if (info_size)
134 memcpy (file_notifications, info, info_size);
135 notifications_size = info_size;
136 notifications_desc = hdir;
137 /* If PostMessage fails, the message queue is full. If that
138 happens, the last thing they will worry about is file
139 notifications. So we effectively discard the
140 notification in that case. */
141 if (PostMessage (FRAME_W32_WINDOW (f), WM_EMACS_FILENOTIFY, 0, 0))
142 notification_buffer_in_use = 1;
143 done = 1;
144 DebPrint (("Announced notifications of %lu bytes\n", info_size));
145 }
146 leave_crit ();
147 if (!done)
148 Sleep (5);
149 }
150}
151
152/* An APC routine to cancel outstanding directory watch. Invoked by
153 the main thread via QueueUserAPC. This is needed because only the
154 thread that issued the ReadDirectoryChangesW call can call CancelIo
155 to cancel that. (CancelIoEx is only available since Vista, so we
156 cannot use it on XP.) */
157VOID CALLBACK
158watch_end (ULONG_PTR arg)
159{
160 HANDLE hdir = (HANDLE)arg;
161
162 if (hdir && hdir != INVALID_HANDLE_VALUE)
163 {
164 CancelIo (hdir);
165 CloseHandle (hdir);
166 }
167}
168
169/* A completion routine (a.k.a. APC function) for handling events read
170 by ReadDirectoryChangesW. Called by the OS when the thread which
171 issued the asynchronous ReadDirectoryChangesW call is in the
172 "alertable state", i.e. waiting inside SleepEx call. */
173VOID CALLBACK
174watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
175{
176 struct notification *dirwatch;
177
178 /* Who knows what happened? Perhaps the OVERLAPPED structure was
179 freed by someone already? In any case, we cannot do anything
180 with this request, so just punt and skip it. FIXME: should we
181 raise the 'terminate' flag in this case? */
182 if (!io_info)
183 return;
184
185 /* We have a pointer to our dirwatch structure conveniently stashed
186 away in the hEvent member of the OVERLAPPED struct. According to
187 MSDN documentation of ReadDirectoryChangesW: "The hEvent member
188 of the OVERLAPPED structure is not used by the system, so you can
189 use it yourself." */
190 dirwatch = (struct notification *)io_info->hEvent;
191 if (status == ERROR_OPERATION_ABORTED)
192 {
193 /* We've been called because the main thread told us to issue
194 CancelIo on the directory we watch, and watch_end did so.
195 The directory handle is already closed. We should clean up
196 and exit, signalling to the thread worker routine not to
197 issue another call to ReadDirectoryChangesW. */
198 xfree (dirwatch->buf);
199 dirwatch->buf = NULL;
200 xfree (dirwatch->io_info);
201 dirwatch->io_info = NULL;
202 xfree (dirwatch->watchee);
203 dirwatch->watchee = NULL;
204 dirwatch->terminate = 1;
205 }
206 else
207 {
208#if 0 /* debugging code */
209 parse_notifications (dirwatch->buf, bytes_ret);
210#endif
211 /* Tell the main thread we have notifications for it. */
212 send_notifications (dirwatch->buf, bytes_ret, dirwatch->dir,
213 &dirwatch->terminate);
214 }
215}
216
217/* Worker routine for the watch thread. */
218static DWORD WINAPI
219watch_worker (LPVOID arg)
220{
221 struct notification *dirwatch = (struct notification *)arg;
222
223 do {
224 BOOL status;
225 DWORD sleep_result;
226 DWORD bytes_ret = 0;
227
228 if (dirwatch->dir)
229 {
230 status = ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf, 16384,
231 dirwatch->subtree, dirwatch->filter,
232 &bytes_ret,
233 dirwatch->io_info, watch_completion);
234 if (!status)
235 {
236 DebPrint (("watch_worker(1): %lu\n", GetLastError ()));
237 xfree (dirwatch->buf);
238 dirwatch->buf = NULL;
239 xfree (dirwatch->io_info);
240 dirwatch->io_info = NULL;
241 CloseHandle (dirwatch->dir);
242 dirwatch->dir = NULL;
243 xfree (dirwatch->watchee);
244 dirwatch->watchee = NULL;
245 return 1;
246 }
247 }
248 /* Sleep indefinitely until awoken by the I/O completion, which
249 could be either a change notification or a cancellation of the
250 watch. */
251 sleep_result = SleepEx (INFINITE, TRUE);
252 if (dirwatch->terminate)
253 DebPrint (("watch_worker: exiting by request\n"));
254 } while (!dirwatch->terminate);
255
256 DebPrint (("watch_worker(2): %lu\n", GetLastError ()));
257 return 0;
258}
259
260/* Launch a thread to watch changes to FILE in a directory open on
261 handle HDIR. */
262static int
263start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags)
264{
265 dirwatch.buf = xmalloc (16384);
266 dirwatch.io_info = xzalloc (sizeof(OVERLAPPED));
267 /* Stash a pointer to dirwatch structure for use by the completion
268 routine. According to MSDN documentation of ReadDirectoryChangesW:
269 "The hEvent member of the OVERLAPPED structure is not used by the
270 system, so you can use it yourself." */
271 dirwatch.io_info->hEvent = &dirwatch;
272 dirwatch.subtree = subdirs;
273 dirwatch.filter = flags;
274 dirwatch.watchee = xstrdup (file);
275 dirwatch.terminate = 0;
276 dirwatch.dir = hdir;
277
278 /* See w32proc.c where it calls CreateThread for the story behind
279 the 2nd and 5th argument in the call to CreateThread. */
280 dirwatch.thr = CreateThread (NULL, 64 * 1024, watch_worker,
281 (void *)&dirwatch, 0x00010000, NULL);
282
283 if (!dirwatch.thr)
284 {
285 dirwatch.terminate = 1;
286 xfree (dirwatch.buf);
287 dirwatch.buf = NULL;
288 xfree (dirwatch.io_info);
289 dirwatch.io_info = NULL;
290 xfree (dirwatch.watchee);
291 dirwatch.watchee = NULL;
292 return -1;
293 }
294 return 0;
295}
296
297/* Called from the main thread to start watching FILE in PARENT_DIR,
298 subject to FLAGS. If SUBDIRS is TRUE, watch the subdirectories of
299 PARENT_DIR as well. Value is the handle on which the directory is
300 open. */
301static HANDLE *
302add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags)
303{
304 HANDLE hdir;
305
306 if (!file || !*file)
307 return NULL;
308
309 hdir = CreateFile (parent_dir,
310 FILE_LIST_DIRECTORY,
311 /* FILE_SHARE_DELETE doesn't preclude other
312 processes from deleting files inside
313 parent_dir. */
314 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
315 NULL, OPEN_EXISTING,
316 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
317 NULL);
318 if (hdir == INVALID_HANDLE_VALUE)
319 return NULL;
320
321 if (start_watching (file, hdir, subdirs, flags) == 0)
322 return hdir;
323
324 CloseHandle (hdir);
325 return NULL;
326}
327
328/* Stop watching a directory specified by its handle HDIR. */
329static int
330remove_watch (HANDLE hdir)
331{
332 if (hdir == dirwatch.dir)
333 {
334 int i;
335 BOOL status;
336 DWORD exit_code, err;
337
338 /* Only the thread that issued the outstanding I/O call can call
339 CancelIo on it. (CancelIoEx is available only since Vista.)
340 So we need to queue an APC for the worker thread telling it
341 to terminate. */
342 if (!QueueUserAPC (watch_end, dirwatch.thr, (ULONG_PTR)dirwatch.dir))
343 DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ()));
344 /* We also set the terminate flag, for when the thread is
345 waiting on the critical section that never gets acquired.
346 FIXME: is there a cleaner method? Using SleepEx there is a
347 no-no, as that will lead to recursive APC invocations and
348 stack overflow. */
349 dirwatch.terminate = 1;
350 /* Wait for the thread to exit. FIXME: is there a better method
351 that is not overly complex? */
352 for (i = 0; i < 50; i++)
353 {
354 if (!((status = GetExitCodeThread (dirwatch.thr, &exit_code))
355 && exit_code == STILL_ACTIVE))
356 break;
357 Sleep (10);
358 }
359 if ((status == FALSE && (err = GetLastError ()) == ERROR_INVALID_HANDLE)
360 || exit_code == STILL_ACTIVE)
361 {
362 if (!(status == FALSE && err == ERROR_INVALID_HANDLE))
363 TerminateThread (dirwatch.thr, 0);
364 }
365
366 /* Clean up. */
367 if (dirwatch.thr)
368 {
369 CloseHandle (dirwatch.thr);
370 dirwatch.thr = NULL;
371 }
372 return 0;
373 }
374 else if (!dirwatch.dir)
375 {
376 DebPrint (("Directory handle already closed!\n"));
377 return 0;
378 }
379 else
380 {
381 DebPrint (("Unknown directory handle!\n"));
382 return -1;
383 }
384}
385
386static DWORD
387filter_list_to_flags (Lisp_Object filter_list)
388{
389 DWORD flags = 0;
390
391 if (NILP (filter_list))
392 return flags;
393
394 if (!NILP (Fmember (Qfile_name, filter_list)))
395 flags |= FILE_NOTIFY_CHANGE_FILE_NAME;
396 if (!NILP (Fmember (Qdirectory_name, filter_list)))
397 flags |= FILE_NOTIFY_CHANGE_DIR_NAME;
398 if (!NILP (Fmember (Qattributes, filter_list)))
399 flags |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
400 if (!NILP (Fmember (Qsize, filter_list)))
401 flags |= FILE_NOTIFY_CHANGE_SIZE;
402 if (!NILP (Fmember (Qlast_write_time, filter_list)))
403 flags |= FILE_NOTIFY_CHANGE_LAST_WRITE;
404 if (!NILP (Fmember (Qlast_access_time, filter_list)))
405 flags |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
406 if (!NILP (Fmember (Qcreation_time, filter_list)))
407 flags |= FILE_NOTIFY_CHANGE_CREATION;
408 if (!NILP (Fmember (Qsecurity_desc, filter_list)))
409 flags |= FILE_NOTIFY_CHANGE_SECURITY;
410
411 return flags;
412}
413
414DEFUN ("w32notify-add-watch", Fw32notify_add_watch,
415 Sw32notify_add_watch, 3, 3, 0,
416 doc: /* Add a watch for filesystem events pertaining to FILE.
417
418This arranges for filesystem events pertaining to FILE to be reported
419to Emacs. Use `w32notify-rm-watch' to cancel the watch.
420
421Value is a descriptor for the added watch, or nil if the file
422cannot be watched.
423
424FILTER is a list of conditions for reporting an event. It can include
425the following symbols:
426
427 'file-name' -- report file creation, deletion, or renaming
428 'directory-name' -- report directory creation, deletion, or renaming
429 'attributes' -- report changes in attributes
430 'size' -- report changes in file-size
431 'last-write-time' -- report changes in last-write time
432 'last-access-time' -- report changes in last-access time
433 'creation-time' -- report changes in creation time
434 'security-desc' -- report changes in security descriptor
435
436If FILE is a directory, and FILTER includes 'subtree', then all the
437subdirectories will also be watched and changes in them reported.
438
439When any event happens that satisfies the conditions specified by
440FILTER, Emacs will call the CALLBACK function passing it a single
441argument EVENT, which is of the form
442
443 (DESCRIPTOR ACTION FILE)
444
445DESCRIPTOR is the same object as the one returned by this function.
446ACTION is the description of the event. It could be any one of the
447following:
448
449 'added' -- FILE was added
450 'removed' -- FILE was deleted
451 'modified' -- FILE's contents or its attributes were modified
452 'renamed-from' -- a file was renamed whose old name was FILE
453 'renamed-to' -- a file was renamed and its new name is FILE
454
455FILE is the name of the file whose event is being reported. */)
456 (Lisp_Object file, Lisp_Object filter, Lisp_Object callback)
457{
458 Lisp_Object encoded_file, watch_object, watch_descriptor;
459 char parent_dir[MAX_PATH], *basename;
460 size_t fn_len;
461 HANDLE hdir;
462 DWORD flags;
463 BOOL subdirs = FALSE;
464 Lisp_Object lisp_errstr;
465 char *errstr;
466
467 CHECK_LIST (filter);
468
469 /* The underlying features are available only since XP. */
470 if (os_subtype == OS_9X
471 || (w32_major_version == 5 && w32_major_version < 1))
472 {
473 errno = ENOSYS;
474 report_file_error ("Watching filesystem events is not supported",
475 Qnil);
476 }
477
478 /* We needa full absolute file name of FILE, and we need to remove
479 any trailing slashes from it, so that GetFullPathName below gets
480 the basename part correctly. */
481 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
482 encoded_file = ENCODE_FILE (file);
483
484 fn_len = GetFullPathName (SDATA (encoded_file), MAX_PATH, parent_dir,
485 &basename);
486 if (!fn_len)
487 {
488 errstr = w32_strerror (0);
489 errno = EINVAL;
490 if (!NILP (Vlocale_coding_system))
491 lisp_errstr
492 = code_convert_string_norecord (build_unibyte_string (errstr),
493 Vlocale_coding_system, 0);
494 else
495 lisp_errstr = build_string (errstr);
496 report_file_error ("GetFullPathName failed",
497 Fcons (lisp_errstr, Fcons (file, Qnil)));
498 }
499 /* We need the parent directory without the slash that follows it.
500 If BASENAME is NULL, the argument was the root directory on its
501 drive. */
502 if (basename)
503 basename[-1] = '\0';
504 else
505 subdirs = TRUE;
506
507 if (!NILP (Fmember (Qsubtree, filter)))
508 subdirs = TRUE;
509
510 flags = filter_list_to_flags (filter);
511
512 hdir = add_watch (parent_dir, basename, subdirs, flags);
513 if (!hdir)
514 {
515 DWORD err = GetLastError ();
516
517 errno = EINVAL;
518 if (err)
519 {
520 errstr = w32_strerror (err);
521 if (!NILP (Vlocale_coding_system))
522 lisp_errstr
523 = code_convert_string_norecord (build_unibyte_string (errstr),
524 Vlocale_coding_system, 0);
525 else
526 lisp_errstr = build_string (errstr);
527 report_file_error ("Cannot watch file",
528 Fcons (lisp_errstr, Fcons (file, Qnil)));
529 }
530 else
531 report_file_error ("Cannot watch file", Fcons (file, Qnil));
532 }
533 /* Store watch object in watch list. */
534 watch_descriptor = make_number (hdir);
535 watch_object = Fcons (watch_descriptor, callback);
536 watch_list = Fcons (watch_object, watch_list);
537
538 return watch_descriptor;
539}
540
541DEFUN ("w32notify-rm-watch", Fw32notify_rm_watch,
542 Sw32notify_rm_watch, 1, 1, 0,
543 doc: /* Remove an existing watch specified by its WATCH-DESCRIPTOR.
544
545WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'. */)
546 (Lisp_Object watch_descriptor)
547{
548 Lisp_Object watch_object;
549 HANDLE hdir = (HANDLE)XINT (watch_descriptor);
550
551 if (remove_watch (hdir) == -1)
552 report_file_error ("Could not remove watch", Fcons (watch_descriptor,
553 Qnil));
554
555 /* Remove watch descriptor from watch list. */
556 watch_object = Fassoc (watch_descriptor, watch_list);
557 if (!NILP (watch_object))
558 watch_list = Fdelete (watch_object, watch_list);
559
560 return Qnil;
561}
562
563Lisp_Object
564get_watch_object (Lisp_Object desc)
565{
566 return Fassoc (desc, watch_list);
567}
568
569void
570syms_of_w32notify (void)
571{
572 DEFSYM (Qfile_name, "file-name");
573 DEFSYM (Qdirectory_name, "directory-name");
574 DEFSYM (Qattributes, "attributes");
575 DEFSYM (Qsize, "size");
576 DEFSYM (Qlast_write_time, "last-write-time");
577 DEFSYM (Qlast_access_time, "last-access-time");
578 DEFSYM (Qcreation_time, "creation-time");
579 DEFSYM (Qsecurity_desc, "security-desc");
580 DEFSYM (Qsubtree, "subtree");
581
582 defsubr (&Sw32notify_add_watch);
583 defsubr (&Sw32notify_rm_watch);
584
585 staticpro (&watch_list);
586
587 Fprovide (intern_c_string ("w32notify"), Qnil);
588}