├── .gitignore ├── LICENSE ├── Makefile.am ├── README ├── autogen.sh ├── compat.h ├── compat ├── atfuncs.c ├── fdopendir.c ├── fstatat.c ├── openat.c ├── pthread_barrier.c ├── queue.h └── tree.h ├── configure.ac ├── controller.c ├── dep-list.c ├── dep-list.h ├── inotify-watch.c ├── inotify-watch.h ├── libinotify.sym ├── m4 └── ax_pthread.m4 ├── patches ├── freebsd11-NOTE_EXTEND-onrename.patch ├── freebsd11-NOTE_OPEN-NOTE_CLOSE-NOTE_READ.patch ├── freebsd11-NOTE_REVOKE.patch └── freebsd11-O_SYMLINK.patch ├── sys └── inotify.h ├── test.c ├── tests ├── bugs_test.cc ├── bugs_test.hh ├── core │ ├── action.cc │ ├── action.hh │ ├── consumer.cc │ ├── consumer.hh │ ├── core.hh │ ├── event.cc │ ├── event.hh │ ├── inotify_client.cc │ ├── inotify_client.hh │ ├── journal.cc │ ├── journal.hh │ ├── log.cc │ ├── log.hh │ ├── platform.hh │ ├── request.cc │ ├── request.hh │ ├── response.cc │ ├── response.hh │ ├── test.cc │ └── test.hh ├── fail_test.cc ├── fail_test.hh ├── notifications_dir_test.cc ├── notifications_dir_test.hh ├── notifications_test.cc ├── notifications_test.hh ├── open_close_test.cc ├── open_close_test.hh ├── start_stop_dir_test.cc ├── start_stop_dir_test.hh ├── start_stop_test.cc ├── start_stop_test.hh ├── symlink_test.cc ├── symlink_test.hh ├── tests.cc ├── update_flags_dir_test.cc ├── update_flags_dir_test.hh ├── update_flags_test.cc └── update_flags_test.hh ├── utils.c ├── utils.h ├── watch-set.c ├── watch-set.h ├── watch.c ├── watch.h ├── worker-thread.c ├── worker-thread.h ├── worker.c └── worker.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | */*/*.o 3 | 4 | *.lo 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Dmitry Matveev 2 | The software is redistributed under the terms of MIT License. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | # The library 3 | #----------------------------------------------------------- 4 | 5 | ACLOCAL_AMFLAGS = -I m4 6 | 7 | if BUILD_LIBRARY 8 | lib_LTLIBRARIES = libinotify.la 9 | 10 | nobase_include_HEADERS = sys/inotify.h 11 | 12 | libinotify_la_SOURCES = \ 13 | utils.c \ 14 | dep-list.c \ 15 | inotify-watch.c \ 16 | watch-set.c \ 17 | watch.c \ 18 | worker-thread.c \ 19 | worker.c \ 20 | controller.c 21 | 22 | if !HAVE_PTHREAD_BARRIER 23 | libinotify_la_SOURCES += compat/pthread_barrier.c 24 | endif 25 | 26 | if !HAVE_ATFUNCS 27 | libinotify_la_SOURCES += compat/atfuncs.c 28 | endif 29 | 30 | if !HAVE_OPENAT 31 | libinotify_la_SOURCES += compat/openat.c 32 | endif 33 | 34 | if !HAVE_FDOPENDIR 35 | libinotify_la_SOURCES += compat/fdopendir.c 36 | endif 37 | 38 | if !HAVE_FSTATAT 39 | libinotify_la_SOURCES += compat/fstatat.c 40 | endif 41 | 42 | libinotify_la_CFLAGS = -I. -DNDEBUG @PTHREAD_CFLAGS@ 43 | libinotify_la_LDFLAGS = @PTHREAD_LIBS@ -export-symbols libinotify.sym 44 | endif 45 | 46 | ############################################################ 47 | # Test suite 48 | #----------------------------------------------------------- 49 | 50 | EXTRA_PROGRAMS = check_libinotify 51 | 52 | test: check_libinotify 53 | @echo Running test suite... 54 | @./check_libinotify 55 | 56 | .PHONY: test 57 | 58 | check_libinotify_SOURCES = \ 59 | tests/core/log.cc \ 60 | tests/core/event.cc \ 61 | tests/core/action.cc \ 62 | tests/core/request.cc \ 63 | tests/core/response.cc \ 64 | tests/core/inotify_client.cc \ 65 | tests/core/consumer.cc \ 66 | tests/core/journal.cc \ 67 | tests/core/test.cc \ 68 | tests/start_stop_test.cc \ 69 | tests/start_stop_dir_test.cc \ 70 | tests/fail_test.cc \ 71 | tests/notifications_test.cc \ 72 | tests/notifications_dir_test.cc \ 73 | tests/update_flags_test.cc \ 74 | tests/update_flags_dir_test.cc \ 75 | tests/open_close_test.cc \ 76 | tests/symlink_test.cc \ 77 | tests/bugs_test.cc \ 78 | tests/tests.cc 79 | 80 | check_libinotify_CXXFLAGS = @PTHREAD_CFLAGS@ 81 | check_libinotify_LDFLAGS = @PTHREAD_LIBS@ 82 | 83 | if LINUX 84 | check_libinotify_CXXFLAGS += -std=c++0x 85 | endif 86 | 87 | if !HAVE_PTHREAD_BARRIER 88 | check_libinotify_SOURCES += compat/pthread_barrier.c 89 | endif 90 | 91 | if BUILD_LIBRARY 92 | check_libinotify_LDADD = libinotify.la 93 | endif 94 | 95 | noinst_programs = check_libinotify 96 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ********************************************************************* 2 | ** WARNING: This is not the "official" repository anymore. ** 3 | ** Please obtain use this repository instead: ** 4 | ** ** 5 | ** https://github.com/libinotify-kqueue/libinotify-kqueue ** 6 | ********************************************************************* 7 | 8 | 9 | libinotify-kqueue 10 | ================= 11 | 12 | Copyright (c) 2011-2014 Dmitry Matveev 13 | 14 | The purpose of this library is to provide inotify API on the 15 | *BSD family of operating systems. The library uses kqueue(2) 16 | to monitor the file system activity. 17 | 18 | 19 | 20 | Requirements 21 | ------------ 22 | 23 | - gcc, g++, automake, autoconf, libtool; 24 | - NetBSD, OpenBSD, FreeBSD (all tested), Mac OS X (reported 25 | to work). 26 | 27 | 28 | 29 | Downloading 30 | ----------- 31 | 32 | This project does not have a special home page yet. The source 33 | code and the issue tracker are hosted on Github: 34 | 35 | https://github.com/dmatveev/libinotify-kqueue 36 | 37 | 38 | 39 | Building 40 | -------- 41 | 42 | Building from a git snaphost is as simple as: 43 | 44 | $ autoreconf -fvi 45 | $ ./configure 46 | $ make 47 | 48 | 49 | 50 | Testing 51 | ------- 52 | 53 | After you build the library, you can run tests to ensure that 54 | everything works in your system: 55 | 56 | $ make test 57 | 58 | There are 60+ tests, and since some events cann't be implemented 59 | using only userspace kqueue(2), this test will always fail: 60 | 61 | > In test "Open/close notifications": 62 | > failed: receive IN_OPEN on cat 63 | > failed: receive IN_CLOSE_NOWRITE on cat 64 | > failed: receive IN_OPEN on ls 65 | > failed: receive IN_CLOSE_NOWRITE on ls 66 | > failed: receive IN_OPEN on modify 67 | > failed: receive IN_CLOSE_WRITE on modify 68 | 69 | Sometimes these cheks will also fail: 70 | 71 | > In test "Directory notifications": 72 | > failed: receive IN_DELETE_SELF on removing a directory 73 | > failed: receive IN_IGNORED on removing a directory 74 | 75 | It is a known problem and it is going to be fixed. If you will get 76 | any other results, please feel free to report it at: 77 | 78 | https://github.com/dmatveev/libinotify-kqueue/issues 79 | 80 | 81 | 82 | Using 83 | ----- 84 | 85 | To use the library, all you need to do is to link your application 86 | with it (adding -linotify to LDFLAGS works in almost all cases). 87 | Of course, you will need to have include (-I) and linker (-L) 88 | directories to be properly set in your project. 89 | 90 | Since the heart of the library is kqueue(2), the library has to 91 | keep many files open to provide proper monitoring. It is especially 92 | important when you monitor a single but large directory - EVERY file 93 | in this directory will be opened by a library. You can run out of 94 | file descriptors, so do not forget request more with setrlimit(2) 95 | before starting monitoring. 96 | 97 | Note that fcntl(2) calls are not supported on descriptors returned 98 | by the library's inotify_init(). 99 | 100 | 101 | 102 | Status 103 | ------ 104 | 105 | The library is feature-complete, i.e. it implements and provides 106 | everything what can be implemented/provided using kqueue(2). The 107 | support of the remaining events (listed in the "Testing" section) 108 | will be added as soon as it will be possible to do with kqueue(2) 109 | (or using other tools in the userspace) in all major supported 110 | operating systems. 111 | 112 | The other point is the performance, and the directory difference 113 | algorithm used in libinotify-kqueue can be optimized. It is the 114 | primary scope of the future work on the library. 115 | 116 | 117 | 118 | Thanks 119 | ------ 120 | 121 | Thanks to Julio Merino for mentoring my work during the initial 122 | library development on Google Summer Of Code 2011. 123 | 124 | Thanks to Antoine Jacoutot for support, reports, and the work 125 | on the related glib-kqueue project. 126 | 127 | Thanks to bugreporters: Baptiste Daroussin, Stanislav Sedov, 128 | Dmitry Okunev, luoqi-git. 129 | 130 | 131 | 132 | License 133 | ------- 134 | 135 | libinotify-kqueue is redistributed under the terms of MIT License. 136 | See file LICENSE for details. 137 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf -v --install || exit 1 4 | -------------------------------------------------------------------------------- /compat.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011-2014 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __COMPAT_H__ 24 | #define __COMPAT_H__ 25 | 26 | #include "config.h" 27 | 28 | #include 29 | 30 | #ifdef HAVE_SYS_QUEUE_H 31 | #include 32 | #else 33 | #include "compat/queue.h" 34 | #endif 35 | 36 | #ifdef HAVE_SYS_TREE_H 37 | #include /* RB tree macroses */ 38 | #else 39 | #include "compat/tree.h" 40 | #endif 41 | 42 | #ifdef HAVE_STDINT_H 43 | #include 44 | #else 45 | #include 46 | #endif 47 | 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | #ifndef DTTOIF 54 | #define DTTOIF(dirtype) ((dirtype) << 12) 55 | #endif 56 | 57 | #ifndef SIZE_MAX 58 | #define SIZE_MAX SIZE_T_MAX 59 | #endif 60 | 61 | #ifndef HAVE_PTHREAD_BARRIER 62 | typedef struct { 63 | int count; /* the number of threads to wait on a barrier */ 64 | volatile int entered; /* the number of threads entered on a barrier */ 65 | volatile int sleeping; /* the number of threads still sleeping */ 66 | 67 | pthread_mutex_t mtx; /* barrier's internal mutex.. */ 68 | pthread_cond_t cnd; /* ..and a condition variable */ 69 | } pthread_barrier_t; 70 | 71 | /* barrier attributes are not supported */ 72 | typedef void pthread_barrierattr_t; 73 | 74 | void pthread_barrier_init (pthread_barrier_t *impl, 75 | const pthread_barrierattr_t *attr, 76 | unsigned count); 77 | void pthread_barrier_wait (pthread_barrier_t *impl); 78 | void pthread_barrier_destroy (pthread_barrier_t *impl); 79 | #endif 80 | 81 | #ifndef AT_FDCWD 82 | #define AT_FDCWD -100 83 | #endif 84 | #ifndef AT_SYMLINK_NOFOLLOW 85 | #define AT_SYMLINK_NOFOLLOW 0x200 /* Do not follow symbolic links */ 86 | #endif 87 | 88 | #ifndef HAVE_ATFUNCS 89 | char *fd_getpath_cached (int fd); 90 | char *fd_concat (int fd, const char *file); 91 | #endif 92 | #ifndef HAVE_OPENAT 93 | int openat (int fd, const char *path, int flags, ...); 94 | #endif 95 | #ifndef HAVE_FDOPENDIR 96 | DIR *fdopendir (int fd); 97 | #endif 98 | #ifndef HAVE_FSTATAT 99 | int fstatat (int fd, const char *path, struct stat *buf, int flag); 100 | #endif 101 | 102 | #endif /* __COMPAT_H__ */ 103 | -------------------------------------------------------------------------------- /compat/fdopendir.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2014 Vladimir Kondratiev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include /* opendir */ 25 | 26 | #include "compat.h" 27 | 28 | DIR * 29 | fdopendir (int fd) 30 | { 31 | DIR *dir; 32 | char *dirpath = fd_getpath_cached (fd); 33 | if (dirpath == NULL) { 34 | return NULL; 35 | } 36 | 37 | dir = opendir (dirpath); 38 | /* close fd just now as it will not be closed on closedir */ 39 | if (dir != NULL) { 40 | close (fd); 41 | } 42 | 43 | return dir; 44 | } 45 | -------------------------------------------------------------------------------- /compat/fstatat.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2014 Vladimir Kondratiev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include /* stat */ 25 | 26 | #include /* errno */ 27 | #include /* free */ 28 | 29 | #include "compat.h" 30 | 31 | int 32 | fstatat (int fd, const char *path, struct stat *buf, int flag) 33 | { 34 | char *fullpath; 35 | int retval, save_errno; 36 | 37 | fullpath = fd_concat (fd, path); 38 | if (fullpath == NULL) { 39 | return -1; 40 | } 41 | 42 | if (flag & AT_SYMLINK_NOFOLLOW) { 43 | retval = lstat (fullpath, buf); 44 | } else { 45 | retval = stat (fullpath, buf); 46 | } 47 | 48 | save_errno = errno; 49 | free (fullpath); 50 | errno = save_errno; 51 | 52 | return retval; 53 | } 54 | -------------------------------------------------------------------------------- /compat/openat.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2014 Vladimir Kondratiev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include /* mode_t */ 25 | 26 | #include /* errno */ 27 | #include /* open */ 28 | #include /* va_start */ 29 | #include /* free */ 30 | 31 | #include "compat.h" 32 | 33 | int 34 | openat (int fd, const char *path, int flags, ...) 35 | { 36 | char *fullpath; 37 | int newfd, save_errno; 38 | mode_t mode; 39 | va_list ap; 40 | 41 | if (flags & O_CREAT) { 42 | va_start(ap, flags); 43 | mode = va_arg(ap, int); 44 | va_end(ap); 45 | } else { 46 | mode = 0; 47 | } 48 | 49 | fullpath = fd_concat (fd, path); 50 | if (fullpath == NULL) { 51 | return -1; 52 | } 53 | 54 | newfd = open (fullpath, flags, mode); 55 | 56 | save_errno = errno; 57 | free (fullpath); 58 | errno = save_errno; 59 | 60 | return newfd; 61 | } 62 | -------------------------------------------------------------------------------- /compat/pthread_barrier.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011-2014 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include 25 | #include /* memset */ 26 | 27 | #include "compat.h" 28 | 29 | /** 30 | * Initialize a barrier 31 | * 32 | * @param[in] impl A pointer to barrier 33 | * @param[in] attr A barrier attributes (not implemented) 34 | * @param[in] count The number of threads to wait on the barrier 35 | **/ 36 | void 37 | pthread_barrier_init (pthread_barrier_t *impl, 38 | const pthread_barrierattr_t *attr, 39 | unsigned count) 40 | { 41 | assert (impl != NULL); 42 | 43 | memset (impl, 0, sizeof (pthread_barrier_t)); 44 | impl->count = count; 45 | 46 | pthread_mutex_init (&impl->mtx, NULL); 47 | pthread_cond_init (&impl->cnd, NULL); 48 | } 49 | 50 | 51 | /** 52 | * Wait on a barrier. 53 | * 54 | * If this thread is not the last expected one, it will be blocked 55 | * until all the expected threads will check in on the barrier. 56 | * Otherwise the barrier will be marked as passed and all blocked 57 | * threads will be unlocked. 58 | * 59 | * This barrier implementation is based on: 60 | * http://siber.cankaya.edu.tr/ozdogan/GraduateParallelComputing.old/ceng505/node94.html 61 | * 62 | * @param[in] impl A pointer to barrier 63 | **/ 64 | void 65 | pthread_barrier_wait (pthread_barrier_t *impl) 66 | { 67 | while (impl->entered == 0 && impl->sleeping != 0); 68 | 69 | pthread_mutex_lock (&impl->mtx); 70 | impl->entered++; 71 | if (impl->entered == impl->count) { 72 | impl->entered = 0; 73 | pthread_cond_broadcast (&impl->cnd); 74 | } else { 75 | ++impl->sleeping; 76 | while (pthread_cond_wait (&impl->cnd, &impl->mtx) != 0 77 | && impl->entered != 0); 78 | --impl->sleeping; 79 | } 80 | pthread_mutex_unlock (&impl->mtx); 81 | } 82 | 83 | /** 84 | * Destroy the barrier and all associated resources. 85 | * 86 | * @param[in] impl A pointer to barrier 87 | **/ 88 | void 89 | pthread_barrier_destroy (pthread_barrier_t *impl) 90 | { 91 | assert (impl != NULL); 92 | 93 | pthread_cond_destroy (&impl->cnd); 94 | pthread_mutex_destroy (&impl->mtx); 95 | 96 | impl->count = 0; 97 | impl->entered = 0; 98 | impl->sleeping = 0; 99 | } 100 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([package], [version]) 2 | AM_INIT_AUTOMAKE([foreign subdir-objects]) 3 | AC_CONFIG_SRCDIR([configure.ac]) 4 | AC_CONFIG_HEADERS([config.h]) 5 | AC_CONFIG_MACRO_DIR([m4]) 6 | AC_PROG_LIBTOOL 7 | AC_PROG_CXX 8 | AC_CONFIG_FILES([Makefile]) 9 | 10 | case "$host_os" in 11 | *linux*) 12 | OS=Linux 13 | ;; 14 | *freebsd*) 15 | OS=FreeBSD 16 | ;; 17 | esac 18 | 19 | AM_CONDITIONAL(LINUX, [test "$OS" = "Linux"]) 20 | AM_CONDITIONAL(FREEBSD, [test "$OS" = "FreeBSD"]) 21 | 22 | 23 | AC_ARG_ENABLE([perrors], 24 | AS_HELP_STRING([--enable-perrors], [enable error messages in the library]), 25 | AC_DEFINE([ENABLE_PERRORS],[1],[Enable error messages]) 26 | ) 27 | 28 | 29 | AX_PTHREAD([], AC_MSG_ERROR(No pthread library found in your system!)) 30 | 31 | 32 | kqueue_support=no 33 | AC_CHECK_HEADERS([sys/event.h], 34 | [ 35 | AC_CHECK_FUNCS(kqueue,,AC_MSG_ERROR(No kqueue detected in your system!)) 36 | AC_CHECK_FUNCS(kevent,,AC_MSG_ERROR(No kevent detected in your system!)) 37 | kqueue_support=yes 38 | ], 39 | [ 40 | if [test "$OS" = "Linux"]; then 41 | echo "Host system in GNU/Linux, enabling target \"test\" only" 42 | kqueue_support=no 43 | else 44 | AC_MSG_ERROR(No sys/kqueue.h found in your system!) 45 | fi 46 | ]) 47 | AM_CONDITIONAL(BUILD_LIBRARY, [test "$kqueue_support" = "yes"]) 48 | 49 | 50 | AC_MSG_CHECKING(for SLIST_FOREACH_SAFE in sys/queue.h) 51 | AC_COMPILE_IFELSE( 52 | [ 53 | AC_LANG_PROGRAM( 54 | [ 55 | @%:@include 56 | ], 57 | [ 58 | @%:@ifndef SLIST_FOREACH_SAFE 59 | @%:@error 60 | @%:@endif 61 | return 0; 62 | ]) 63 | ], 64 | have_sys_queue_h=yes, 65 | have_sys_queue_h=no 66 | ) 67 | AC_MSG_RESULT($have_sys_queue_h) 68 | 69 | 70 | AC_MSG_CHECKING(for SLIST_REMOVE_AFTER in sys/queue.h) 71 | AC_COMPILE_IFELSE( 72 | [ 73 | AC_LANG_PROGRAM( 74 | [ 75 | @%:@include 76 | ], 77 | [ 78 | @%:@ifndef SLIST_REMOVE_AFTER 79 | @%:@error 80 | @%:@endif 81 | return 0; 82 | ]) 83 | ], 84 | AC_MSG_RESULT(yes), 85 | [ 86 | AC_MSG_RESULT(no) 87 | have_sys_queue_h=no 88 | ], 89 | ) 90 | if test "$have_sys_queue_h" = "yes"; then 91 | AC_DEFINE([HAVE_SYS_QUEUE_H],[1],[Define to 1 if the system has useable sys/queue.h]) 92 | fi 93 | 94 | 95 | AC_MSG_CHECKING(for RB_FOREACH_SAFE in sys/tree.h) 96 | AC_COMPILE_IFELSE( 97 | [ 98 | AC_LANG_PROGRAM( 99 | [ 100 | @%:@include 101 | ], 102 | [ 103 | @%:@ifndef RB_FOREACH_SAFE 104 | @%:@error 105 | @%:@endif 106 | return 0; 107 | ]) 108 | ], 109 | have_sys_tree_h=yes, 110 | have_sys_tree_h=no 111 | ) 112 | AC_MSG_RESULT($have_sys_tree_h) 113 | if test "$have_sys_tree_h" = "yes"; then 114 | AC_DEFINE([HAVE_SYS_TREE_H],[1],[Define to 1 if the system has useable sys/tree.h]) 115 | fi 116 | 117 | 118 | AC_MSG_CHECKING(for pthread_barrier) 119 | AC_COMPILE_IFELSE( 120 | [ 121 | AC_LANG_PROGRAM( 122 | [ 123 | @%:@include 124 | ], 125 | [ 126 | pthread_barrier_t barrier; 127 | pthread_barrier_init(&barrier, NULL, 1); 128 | pthread_barrier_wait(&barrier); 129 | pthread_barrier_destroy(&barrier); 130 | return 0; 131 | ]) 132 | ], 133 | [ 134 | AC_DEFINE([HAVE_PTHREAD_BARRIER],[1],[Define to 1 if the system has pthread_barrier]) 135 | have_pthread_barrier=yes 136 | ], 137 | have_pthread_barrier=no 138 | ) 139 | AC_MSG_RESULT($have_pthread_barrier) 140 | AM_CONDITIONAL(HAVE_PTHREAD_BARRIER, [test "$have_pthread_barrier" = "yes"]) 141 | 142 | AC_ARG_ENABLE([unsafe_fchdir], 143 | AS_HELP_STRING([--enable-unsafe-fchdir], [allow unsafe use of fchdir to track of watched directory path changes on its renaming]), 144 | [ 145 | AC_DEFINE([ENABLE_UNSAFE_FCHDIR],[1],[Allow to use of fchdir to track of watched directory path changes]) 146 | enable_unsafe_fchdir=yes 147 | ], 148 | enable_unsafe_fchdir=no 149 | ) 150 | 151 | atfuncs_support=yes 152 | AC_CHECK_FUNCS(openat fdopendir fstatat,,atfuncs_support=no) 153 | if test "$atfuncs_support" = "yes"; then 154 | AC_DEFINE([HAVE_ATFUNCS],[1],[Define to 1 if relative pathname functions detected]) 155 | fi 156 | AM_CONDITIONAL(HAVE_ATFUNCS, [test "$atfuncs_support" = "yes"]) 157 | AM_CONDITIONAL(HAVE_OPENAT, [test "$ac_cv_func_openat" = "yes"]) 158 | AM_CONDITIONAL(HAVE_FDOPENDIR, [test "$ac_cv_func_fdopendir" = "yes"]) 159 | AM_CONDITIONAL(HAVE_FSTATAT, [test "$ac_cv_func_fstatat" = "yes"]) 160 | 161 | AC_MSG_CHECKING(for F_GETPATH in fcntl.h) 162 | AC_COMPILE_IFELSE( 163 | [AC_LANG_PROGRAM([@%:@include ], [int a = F_GETPATH;])], 164 | f_getpath_support=yes, 165 | f_getpath_support=no 166 | ) 167 | AC_MSG_RESULT($f_getpath_support) 168 | 169 | if test "$atfuncs_support" = "no" -a \ 170 | "$f_getpath_support" = "no" -a \ 171 | "$enable_unsafe_fchdir" = "no"; then 172 | AC_MSG_WARN([Neither POSIX.1-2008 relative pathname functions nor F_GETPATH fcntl have been found. Watched directory renamings are untracked. You can specify --enable-unsafe-fchdir to avoid this]) 173 | fi 174 | 175 | 176 | AC_MSG_CHECKING(for O_SYMLINK in fcntl.h) 177 | AC_COMPILE_IFELSE( 178 | [AC_LANG_PROGRAM([@%:@include ], [int a = O_SYMLINK;])], 179 | have_o_symlink=yes, 180 | have_o_symlink=no 181 | ) 182 | AC_MSG_RESULT($have_o_symlink) 183 | 184 | if test "$have_o_symlink" = "no"; then 185 | AC_MSG_WARN([O_SYMLINK open(2) flag is not supported on your system. Watching for symbolic links is unavailable]) 186 | fi 187 | 188 | 189 | AC_MSG_CHECKING(for O_EVTONLY in fcntl.h) 190 | AC_COMPILE_IFELSE( 191 | [AC_LANG_PROGRAM([@%:@include ], [int a = O_EVTONLY;])], 192 | have_o_evtonly=yes, 193 | have_o_evtonly=no 194 | ) 195 | AC_MSG_RESULT($have_o_evtonly) 196 | 197 | if test "$have_o_evtonly" = "yes"; then 198 | AC_DEFINE([HAVE_O_EVTONLY],[1],[Define to 1 if O_EVTONLY defined in fcntl.h]) 199 | else 200 | AC_MSG_WARN([O_EVTONLY open(2) flag is not supported on your system. Unmounting of fs where watch located is unavailable]) 201 | fi 202 | 203 | 204 | AC_MSG_CHECKING(for NOTE_OPEN in sys/event.h) 205 | AC_COMPILE_IFELSE( 206 | [ 207 | AC_LANG_PROGRAM( 208 | [ 209 | @%:@include 210 | @%:@include 211 | ], 212 | [ 213 | int a = NOTE_OPEN; 214 | ]) 215 | ], 216 | have_note_open=yes, 217 | have_note_open=no 218 | ) 219 | AC_MSG_RESULT($have_note_open) 220 | 221 | 222 | AC_MSG_CHECKING(for NOTE_CLOSE in sys/event.h) 223 | AC_COMPILE_IFELSE( 224 | [ 225 | AC_LANG_PROGRAM( 226 | [ 227 | @%:@include 228 | @%:@include 229 | ], 230 | [ 231 | int a = NOTE_CLOSE; 232 | ]) 233 | ], 234 | have_note_close=yes, 235 | have_note_close=no 236 | ) 237 | AC_MSG_RESULT($have_note_close) 238 | 239 | 240 | AC_MSG_CHECKING(for NOTE_READ in sys/event.h) 241 | AC_COMPILE_IFELSE( 242 | [ 243 | AC_LANG_PROGRAM( 244 | [ 245 | @%:@include 246 | @%:@include 247 | ], 248 | [ 249 | int a = NOTE_READ; 250 | ]) 251 | ], 252 | have_note_read=yes, 253 | have_note_read=no 254 | ) 255 | AC_MSG_RESULT($have_note_read) 256 | 257 | 258 | AC_MSG_CHECKING(for NODE_EXTEND on renaming of file in subdirectory) 259 | rm -rf iktestdir 260 | mkdir iktestdir 261 | touch iktestfile 262 | AC_RUN_IFELSE( 263 | [ 264 | AC_LANG_PROGRAM( 265 | [ 266 | @%:@include 267 | @%:@include 268 | @%:@include 269 | @%:@include 270 | @%:@include 271 | ], 272 | [ 273 | int fd, kq; 274 | struct kevent ev; 275 | static const struct timespec tout = { 1, 0 }; 276 | 277 | if ((fd = open("iktestdir", O_RDONLY)) == -1) return 1; 278 | if ((kq = kqueue()) == -1) return 1; 279 | EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, 280 | NOTE_WRITE | NOTE_EXTEND, 0, 0); 281 | if (kevent(kq, &ev, 1, NULL, 0, &tout) == -1) return 1; 282 | if (rename ("iktestfile", "iktestdir/iktestfile") == -1) return 1; 283 | if (kevent(kq, NULL, 0, &ev, 1, &tout) != 1) return 1; 284 | return (ev.fflags & NOTE_EXTEND) ? 0 : 1; 285 | ]) 286 | ], 287 | [ 288 | AC_DEFINE([HAVE_NOTE_EXTEND_ON_SUBFILE_RENAME],[1],[Define to 1 if rename set NODE_EXTEND flag]) 289 | AC_MSG_RESULT(yes) 290 | ], 291 | AC_MSG_RESULT(no) 292 | ) 293 | rm -rf iktestdir 294 | 295 | 296 | AC_CHECK_MEMBER([struct dirent.d_type], 297 | [ 298 | AC_DEFINE([DIRENT_HAVE_D_TYPE],[1],[Define to 1 if dirent have d_type field]) 299 | ], 300 | [], 301 | [ 302 | [@%:@include ] 303 | ]) 304 | 305 | 306 | # There are two ways we can reread directory opened already. Namely: 307 | # 1. Open directory by realtive path "." to file descriptor than read & close 308 | # 2. Rewind directory than read it one more time and leave opened 309 | # Former one is more reliable but latter is prefered when system has NOTE_OPEN 310 | # or NOTE_CLOSE events and we dont want that kqueue catches open and closedirs 311 | # issued by directory diffing routines. 312 | # 313 | # Define this if we can reuse filedes opened once before or leave undefined if 314 | # we have to execute open/close pair on every directory read. 315 | 316 | if test "$ac_cv_func_fdopendir" = "yes" -a "$have_o_evtonly" = "no" -a \ 317 | \( "$have_note_open" = "yes" -o "$have_note_close" = "yes" \) ; then 318 | AC_DEFINE([DIRECTORY_LISTING_REWINDS], [1], [Define to 1 if reusing of watched dir filedes is necessary]) 319 | fi 320 | 321 | 322 | AC_OUTPUT 323 | -------------------------------------------------------------------------------- /controller.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011-2014 Dmitry Matveev 3 | Copyright (c) 2014-2015 Vladimir Kondratiev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | *******************************************************************************/ 23 | 24 | #include /* NULL */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | #include "sys/inotify.h" 38 | 39 | #include "utils.h" 40 | #include "worker.h" 41 | 42 | 43 | #define WORKER_SZ 100 44 | static worker* volatile workers[WORKER_SZ] = {NULL}; 45 | static pthread_mutex_t workers_mutex = PTHREAD_MUTEX_INITIALIZER; 46 | 47 | /** 48 | * Create a new inotify instance. 49 | * 50 | * This function will create a new inotify instance (actually, a worker 51 | * with its own thread). To destroy the instance, just close its file 52 | * descriptor. 53 | * 54 | * @return -1 on failure, a file descriptor on success. 55 | **/ 56 | INO_EXPORT int 57 | inotify_init (void) __THROW 58 | { 59 | return inotify_init1 (0); 60 | } 61 | 62 | /** 63 | * Create a new inotify instance. 64 | * 65 | * This function will create a new inotify instance (actually, a worker 66 | * with its own thread). To destroy the instance, just close its file 67 | * descriptor. 68 | * 69 | * @param[in] flags A combination of inotify_init1 flags 70 | * @return -1 on failure, a file descriptor on success. 71 | **/ 72 | INO_EXPORT int 73 | inotify_init1 (int flags) __THROW 74 | { 75 | int lfd = -1; 76 | 77 | #ifdef O_CLOEXEC 78 | if (flags & ~(IN_CLOEXEC|O_CLOEXEC|IN_NONBLOCK|O_NONBLOCK)) { 79 | #else 80 | if (flags & ~(IN_CLOEXEC|IN_NONBLOCK|O_NONBLOCK)) { 81 | #endif 82 | errno = EINVAL; 83 | return -1; 84 | } 85 | 86 | pthread_mutex_lock (&workers_mutex); 87 | 88 | int i; 89 | for (i = 0; i < WORKER_SZ; i++) { 90 | if (workers[i] == NULL) { 91 | worker *wrk = worker_create (flags); 92 | if (wrk != NULL) { 93 | workers[i] = wrk; 94 | lfd = wrk->io[INOTIFY_FD]; 95 | 96 | /* We can face into situation when there are two workers with 97 | * the same inotify FDs. It usually occurs when a worker fd has 98 | * been closed but the worker has not been removed from a list 99 | * yet. The fd is free, and when we create a new worker, we can 100 | * receive the same fd. So check for duplicates and remove them 101 | * now. */ 102 | int j; 103 | for (j = 0; j < WORKER_SZ; j++) { 104 | worker *jw = workers[j]; 105 | if (jw != NULL && jw->io[INOTIFY_FD] == lfd && jw != wrk) { 106 | workers[j] = NULL; 107 | perror_msg ("Collision found: fd %d", lfd); 108 | } 109 | } 110 | } 111 | 112 | pthread_mutex_unlock (&workers_mutex); 113 | return lfd; 114 | } 115 | } 116 | 117 | pthread_mutex_unlock (&workers_mutex); 118 | errno = EMFILE; 119 | return -1; 120 | } 121 | 122 | 123 | /** 124 | * Add or modify a watch. 125 | * 126 | * If the watch with a such filename is already exist, its mask will 127 | * be updated. A new watch will be created otherwise. 128 | * 129 | * @param[in] fd A file descriptor of an inotify instance. 130 | * @param[in] name A path to a file to watch. 131 | * @param[in] mask A combination of inotify flags. 132 | * @return id of a watch, -1 on failure. 133 | **/ 134 | INO_EXPORT int 135 | inotify_add_watch (int fd, 136 | const char *name, 137 | uint32_t mask) __THROW 138 | { 139 | struct stat st; 140 | 141 | if (!is_opened (fd)) { 142 | return -1; /* errno = EBADF */ 143 | } 144 | 145 | /* 146 | * this lstat() call guards worker from incorrectly specified path. 147 | * E.g, it prevents catching of SIGSEGV when pathname points outside 148 | * of the process's accessible address space 149 | */ 150 | if (lstat (name, &st) == -1) { 151 | perror_msg("failed to lstat watch %s", 152 | errno != EFAULT ? name : ""); 153 | return -1; 154 | } 155 | 156 | if (mask == 0) { 157 | perror_msg ("Failed to open watch %s. Bad event mask %x", name, mask); 158 | errno = EINVAL; 159 | return -1; 160 | } 161 | 162 | pthread_mutex_lock (&workers_mutex); 163 | 164 | /* look up for an appropriate worker */ 165 | int i; 166 | for (i = 0; i < WORKER_SZ; i++) { 167 | worker *wrk = workers[i]; 168 | if (wrk != NULL 169 | && wrk->io[INOTIFY_FD] == fd 170 | && wrk->closed == 0 171 | && is_opened (wrk->io[INOTIFY_FD])) { 172 | pthread_mutex_lock (&wrk->mutex); 173 | 174 | /* Closed flag could be set before we lock on a mutex */ 175 | if (wrk->closed) { 176 | pthread_mutex_unlock (&wrk->mutex); 177 | worker_free (wrk); 178 | workers[i] = NULL; 179 | pthread_mutex_unlock (&workers_mutex); 180 | errno = EBADF; 181 | return -1; 182 | } 183 | 184 | worker_cmd_add (&wrk->cmd, name, mask); 185 | safe_write (wrk->io[INOTIFY_FD], "*", 1); 186 | 187 | worker_cmd_wait (&wrk->cmd); 188 | int retval = wrk->cmd.retval; 189 | int error = wrk->cmd.error; 190 | 191 | pthread_mutex_unlock (&wrk->mutex); 192 | 193 | /* TODO: ???? */ 194 | if (wrk->closed) { 195 | worker_free (wrk); 196 | workers[i] = NULL; 197 | } 198 | 199 | pthread_mutex_unlock (&workers_mutex); 200 | if (retval == -1) { 201 | errno = error; 202 | } 203 | return retval; 204 | } 205 | } 206 | 207 | pthread_mutex_unlock (&workers_mutex); 208 | errno = EINVAL; 209 | return -1; 210 | } 211 | 212 | /** 213 | * Remove a watch. 214 | * 215 | * Removes a watch and releases all the associated resources. 216 | * Notifications from the watch should not be received by a client 217 | * anymore. 218 | * 219 | * @param[in] fd Inotify instance file descriptor. 220 | * @param[in] wd Watch id. 221 | * @return 0 on success, -1 on failure. 222 | **/ 223 | INO_EXPORT int 224 | inotify_rm_watch (int fd, 225 | int wd) __THROW 226 | { 227 | if (!is_opened (fd)) { 228 | return -1; /* errno = EBADF */ 229 | } 230 | 231 | pthread_mutex_lock (&workers_mutex); 232 | 233 | int i; 234 | for (i = 0; i < WORKER_SZ; i++) { 235 | worker *wrk = workers[i]; 236 | if (wrk != NULL 237 | && wrk->io[INOTIFY_FD] == fd 238 | && wrk->closed == 0 239 | && is_opened (wrk->io[INOTIFY_FD])) { 240 | pthread_mutex_lock (&wrk->mutex); 241 | 242 | if (wrk->closed) { 243 | pthread_mutex_unlock (&wrk->mutex); 244 | worker_free (wrk); 245 | workers[i] = NULL; 246 | pthread_mutex_unlock (&workers_mutex); 247 | errno = EBADF; 248 | return -1; 249 | } 250 | 251 | worker_cmd_remove (&wrk->cmd, wd); 252 | safe_write (wrk->io[INOTIFY_FD], "*", 1); 253 | 254 | worker_cmd_wait (&wrk->cmd); 255 | int retval = wrk->cmd.retval; 256 | int error = wrk->cmd.error; 257 | 258 | pthread_mutex_unlock (&wrk->mutex); 259 | 260 | /* TODO: ???? */ 261 | if (wrk->closed) { 262 | worker_free (wrk); 263 | workers[i] = NULL; 264 | } 265 | 266 | pthread_mutex_unlock (&workers_mutex); 267 | if (retval == -1) { 268 | errno = error; 269 | } 270 | return retval; 271 | } 272 | } 273 | 274 | pthread_mutex_unlock (&workers_mutex); 275 | errno = EINVAL; 276 | return -1; 277 | } 278 | 279 | /** 280 | * Erase a worker from a list of workers. 281 | * 282 | * This function does not lock the global array of workers (I assume that 283 | * marking its items as volatile should be enough). Also this function is 284 | * intended to be called from the worker threads only. 285 | * 286 | * @param[in] wrk A pointer to a worker 287 | **/ 288 | void 289 | worker_erase (worker *wrk) 290 | { 291 | assert (wrk != NULL); 292 | 293 | int i; 294 | for (i = 0; i < WORKER_SZ; i++) { 295 | if (workers[i] == wrk) { 296 | workers[i] = NULL; 297 | break; 298 | } 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /dep-list.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011-2014 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __DEP_LIST_H__ 24 | #define __DEP_LIST_H__ 25 | 26 | #include "compat.h" 27 | 28 | #include /* ino_t */ 29 | #include /* mode_t */ 30 | 31 | #define S_IFUNK 0000000 /* mode_t extension. File type is unknown */ 32 | #define S_ISUNK(m) (((m) & S_IFMT) == S_IFUNK) 33 | 34 | typedef struct dep_item { 35 | ino_t inode; 36 | mode_t type; 37 | char path[]; 38 | } dep_item; 39 | 40 | typedef struct dep_node { 41 | SLIST_ENTRY(dep_node) next; 42 | dep_item *item; 43 | } dep_node; 44 | 45 | typedef struct dep_list { 46 | SLIST_HEAD(, dep_node) head; 47 | } dep_list; 48 | 49 | typedef void (* no_entry_cb) (void *udata); 50 | typedef void (* single_entry_cb) (void *udata, dep_item *di); 51 | typedef void (* dual_entry_cb) (void *udata, 52 | dep_item *from_di, 53 | dep_item *to_di); 54 | typedef void (* list_cb) (void *udata, const dep_list *list); 55 | 56 | 57 | typedef struct traverse_cbs { 58 | dual_entry_cb unchanged; 59 | single_entry_cb added; 60 | single_entry_cb removed; 61 | single_entry_cb replaced; 62 | dual_entry_cb overwritten; 63 | dual_entry_cb moved; 64 | list_cb many_added; 65 | list_cb many_removed; 66 | no_entry_cb names_updated; 67 | } traverse_cbs; 68 | 69 | dep_item* di_create (const char *path, ino_t inode, mode_t type); 70 | void di_free (dep_item *di); 71 | 72 | dep_list* dl_create (); 73 | dep_node* dl_insert (dep_list *dl, dep_item *di); 74 | void dl_remove_after (dep_list *dl, dep_node *dn); 75 | void dl_print (const dep_list *dl); 76 | dep_list* dl_shallow_copy (const dep_list *dl); 77 | void dl_shallow_free (dep_list *dl); 78 | void dl_free (dep_list *dl); 79 | dep_list* dl_listing (int fd); 80 | 81 | int 82 | dl_calculate (dep_list *before, 83 | dep_list *after, 84 | const traverse_cbs *cbs, 85 | void *udata); 86 | 87 | 88 | #endif /* __DEP_LIST_H__ */ 89 | -------------------------------------------------------------------------------- /inotify-watch.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2014 Vladimir Kondratiev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include "compat.h" 24 | 25 | #include 26 | #include /* fstat */ 27 | 28 | #include /* assert */ 29 | #include /* errno */ 30 | #include /* AT_FDCWD */ 31 | #include /* calloc, free */ 32 | #include /* strcmp */ 33 | #include /* close */ 34 | 35 | #include "sys/inotify.h" 36 | 37 | #include "inotify-watch.h" 38 | #include "utils.h" 39 | #include "watch-set.h" 40 | #include "watch.h" 41 | 42 | /** 43 | * Preform minimal initialization required for opening watch descriptor 44 | * 45 | * @param[in] path Path to watch. 46 | * @param[in] flags A combination of inotify event flags. 47 | * @return A inotify watch descriptor on success, -1 otherwise. 48 | **/ 49 | int 50 | iwatch_open (const char *path, uint32_t flags) 51 | { 52 | int fd = watch_open (AT_FDCWD, path, flags); 53 | if (fd == -1) { 54 | perror_msg ("Failed to open inotify watch %s", path); 55 | } 56 | 57 | return fd; 58 | } 59 | 60 | /** 61 | * Initialize inotify watch. 62 | * 63 | * This function creates and initializes additional watches for a directory. 64 | * 65 | * @param[in] wrk A pointer to #worker. 66 | * @param[in] fd A file descriptor of a watched entry. 67 | * @param[in] flags A combination of inotify event flags. 68 | * @return A pointer to a created #i_watch on success NULL otherwise 69 | **/ 70 | i_watch * 71 | iwatch_init (worker *wrk, int fd, uint32_t flags) 72 | { 73 | assert (wrk != NULL); 74 | assert (fd != -1); 75 | 76 | struct stat st; 77 | if (fstat (fd, &st) == -1) { 78 | perror_msg ("fstat failed on %d", fd); 79 | return NULL; 80 | } 81 | 82 | i_watch *iw = calloc (1, sizeof (i_watch)); 83 | if (iw == NULL) { 84 | perror_msg ("Failed to allocate inotify watch"); 85 | return NULL; 86 | } 87 | 88 | iw->deps = NULL; 89 | iw->wrk = wrk; 90 | iw->wd = fd; 91 | iw->flags = flags; 92 | iw->inode = st.st_ino; 93 | iw->dev = st.st_dev; 94 | iw->is_closed = 0; 95 | 96 | watch_set_init (&iw->watches); 97 | 98 | if (S_ISDIR (st.st_mode)) { 99 | iw->deps = dl_listing (fd); 100 | if (iw->deps == NULL) { 101 | perror_msg ("Directory listing of %d failed", fd); 102 | iwatch_free (iw); 103 | return NULL; 104 | } 105 | } 106 | 107 | watch *parent = watch_init (iw, WATCH_USER, fd, &st); 108 | if (parent == NULL) { 109 | iwatch_free (iw); 110 | return NULL; 111 | } 112 | 113 | watch_set_insert (&iw->watches, parent); 114 | 115 | if (S_ISDIR (st.st_mode)) { 116 | 117 | dep_node *iter; 118 | SLIST_FOREACH (iter, &iw->deps->head, next) { 119 | iwatch_add_subwatch (iw, iter->item); 120 | } 121 | } 122 | return iw; 123 | } 124 | 125 | /** 126 | * Free an inotify watch. 127 | * 128 | * @param[in] iw A pointer to #i_watch to remove. 129 | **/ 130 | void 131 | iwatch_free (i_watch *iw) 132 | { 133 | assert (iw != NULL); 134 | 135 | watch_set_free (&iw->watches); 136 | if (iw->deps != NULL) { 137 | dl_free (iw->deps); 138 | } 139 | free (iw); 140 | } 141 | 142 | /** 143 | * Start watching a file or a directory. 144 | * 145 | * @param[in] iw A pointer to #i_watch. 146 | * @param[in] di A dependency item with relative path to watch. 147 | * @return A pointer to a created watch. 148 | **/ 149 | watch* 150 | iwatch_add_subwatch (i_watch *iw, dep_item *di) 151 | { 152 | assert (iw != NULL); 153 | assert (iw->deps != NULL); 154 | assert (di != NULL); 155 | 156 | if (iw->is_closed) { 157 | return NULL; 158 | } 159 | 160 | watch *w = watch_set_find (&iw->watches, di->inode); 161 | if (w != NULL) { 162 | di->type = w->flags & S_IFMT; 163 | goto hold; 164 | } 165 | 166 | /* Don`t open a watches with empty kqueue filter flags */ 167 | watch_flags_t wf = (di->type & S_IFMT) | WF_ISSUBWATCH; 168 | if (!S_ISUNK (di->type) && inotify_to_kqueue (iw->flags, wf) == 0) { 169 | return NULL; 170 | } 171 | 172 | int fd = watch_open (iw->wd, di->path, IN_DONT_FOLLOW); 173 | if (fd == -1) { 174 | perror_msg ("Failed to open file %s", di->path); 175 | goto lstat; 176 | } 177 | 178 | struct stat st; 179 | if (fstat (fd, &st) == -1) { 180 | perror_msg ("Failed to stat subwatch %s", di->path); 181 | close (fd); 182 | goto lstat; 183 | } 184 | 185 | di->type = st.st_mode & S_IFMT; 186 | 187 | /* Correct inode number if opened file is not a listed one */ 188 | if (di->inode != st.st_ino) { 189 | if (iw->dev != st.st_dev) { 190 | /* It`s a mountpoint. Keep underlying directory inode number */ 191 | st.st_ino = di->inode; 192 | } else { 193 | /* Race detected. Use new inode number and try to find watch again */ 194 | perror_msg ("%s has been replaced after directory listing", di->path); 195 | di->inode = st.st_ino; 196 | w = watch_set_find (&iw->watches, di->inode); 197 | if (w != NULL) { 198 | close (fd); 199 | goto hold; 200 | } 201 | } 202 | } 203 | 204 | w = watch_init (iw, WATCH_DEPENDENCY, fd, &st); 205 | if (w == NULL) { 206 | close (fd); 207 | return NULL; 208 | } 209 | 210 | watch_set_insert (&iw->watches, w); 211 | 212 | hold: 213 | ++w->refcount; 214 | return w; 215 | 216 | lstat: 217 | if (S_ISUNK (di->type)) { 218 | if (fstatat (iw->wd, di->path, &st, AT_SYMLINK_NOFOLLOW) != -1) { 219 | di->type = st.st_mode & S_IFMT; 220 | } else { 221 | perror_msg ("Failed to lstat subwatch %s", di->path); 222 | } 223 | } 224 | return NULL; 225 | } 226 | 227 | /** 228 | * Remove a watch from worker by its path. 229 | * 230 | * @param[in] iw A pointer to the #i_watch. 231 | * @param[in] di A dependency list item to remove watch. 232 | **/ 233 | void 234 | iwatch_del_subwatch (i_watch *iw, const dep_item *di) 235 | { 236 | assert (iw != NULL); 237 | assert (di != NULL); 238 | 239 | watch *w = watch_set_find (&iw->watches, di->inode); 240 | if (w != NULL) { 241 | assert (w->refcount > 0); 242 | --w->refcount; 243 | 244 | if (w->refcount == 0) { 245 | watch_set_delete (&iw->watches, w); 246 | } 247 | } 248 | } 249 | 250 | /** 251 | * Update inotify watch flags. 252 | * 253 | * When called for a directory watch, update also the flags of all the 254 | * dependent (child) watches. 255 | * 256 | * @param[in] iw A pointer to #i_watch. 257 | * @param[in] flags A combination of the inotify watch flags. 258 | **/ 259 | void 260 | iwatch_update_flags (i_watch *iw, uint32_t flags) 261 | { 262 | assert (iw != NULL); 263 | 264 | /* merge flags if IN_MASK_ADD flag is set */ 265 | if (flags & IN_MASK_ADD) { 266 | flags |= iw->flags; 267 | } 268 | 269 | iw->flags = flags; 270 | 271 | watch *w, *tmpw; 272 | /* update kwatches or close those we dont need to watch */ 273 | RB_FOREACH_SAFE (w, watch_set, &iw->watches, tmpw) { 274 | uint32_t fflags = inotify_to_kqueue (flags, w->flags); 275 | if (fflags == 0) { 276 | watch_set_delete (&iw->watches, w); 277 | } else { 278 | watch_register_event (w, fflags); 279 | } 280 | } 281 | 282 | if (iw->deps != NULL) { 283 | /* create list of unwatched subfiles */ 284 | dep_list *dl = dl_shallow_copy (iw->deps); 285 | dep_node *iter, *tmpdn, *prev = NULL; 286 | SLIST_FOREACH_SAFE (iter, &iw->deps->head, next, tmpdn) { 287 | if (watch_set_find (&iw->watches, iter->item->inode)) { 288 | dl_remove_after (dl, prev); 289 | } else { 290 | prev = iter; 291 | } 292 | } 293 | 294 | /* And finally try to watch that list */ 295 | SLIST_FOREACH (iter, &dl->head, next) { 296 | iwatch_add_subwatch (iw, iter->item); 297 | } 298 | dl_shallow_free (dl); 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /inotify-watch.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2014 Vladimir Kondratiev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __INOTIFY_WATCH_H__ 24 | #define __INOTIFY_WATCH_H__ 25 | 26 | #include "compat.h" 27 | 28 | typedef struct i_watch i_watch; 29 | 30 | #include "dep-list.h" 31 | #include "watch-set.h" 32 | #include "watch.h" 33 | #include "worker.h" 34 | 35 | struct i_watch { 36 | int wd; /* watch descriptor */ 37 | worker *wrk; /* pointer to a parent worker structure */ 38 | int is_closed; /* inotify watch is stopped but not freed yet */ 39 | uint32_t flags; /* flags in the inotify format */ 40 | ino_t inode; /* inode number of watched inode */ 41 | dev_t dev; /* device number of watched inode */ 42 | dep_list *deps; /* dependence list of inotify watch */ 43 | watch_set watches; /* kqueue watches of inotify watch */ 44 | SLIST_ENTRY(i_watch) next; /* pointer to the next inotify watch in list */ 45 | }; 46 | 47 | int iwatch_open (const char *path, uint32_t flags); 48 | i_watch *iwatch_init (worker *wrk, int fd, uint32_t flags); 49 | void iwatch_free (i_watch *iw); 50 | 51 | void iwatch_update_flags (i_watch *iw, uint32_t flags); 52 | 53 | watch* iwatch_add_subwatch (i_watch *iw, dep_item *di); 54 | void iwatch_del_subwatch (i_watch *iw, const dep_item *di); 55 | 56 | #endif /* __INOTIFY_WATCH_H__ */ 57 | -------------------------------------------------------------------------------- /libinotify.sym: -------------------------------------------------------------------------------- 1 | inotify_init 2 | inotify_init1 3 | inotify_add_watch 4 | inotify_rm_watch 5 | -------------------------------------------------------------------------------- /patches/freebsd11-NOTE_EXTEND-onrename.patch: -------------------------------------------------------------------------------- 1 | Index: sys/kern/vfs_subr.c 2 | =================================================================== 3 | --- sys/kern/vfs_subr.c (revision 272513) 4 | +++ sys/kern/vfs_subr.c (working copy) 5 | @@ -4231,8 +4231,8 @@ 6 | struct vop_rename_args *a = ap; 7 | 8 | if (!rc) { 9 | - VFS_KNOTE_UNLOCKED(a->a_fdvp, NOTE_WRITE); 10 | - VFS_KNOTE_UNLOCKED(a->a_tdvp, NOTE_WRITE); 11 | + VFS_KNOTE_UNLOCKED(a->a_fdvp, NOTE_WRITE | NOTE_EXTEND); 12 | + VFS_KNOTE_UNLOCKED(a->a_tdvp, NOTE_WRITE | NOTE_EXTEND); 13 | VFS_KNOTE_UNLOCKED(a->a_fvp, NOTE_RENAME); 14 | if (a->a_tvp) 15 | VFS_KNOTE_UNLOCKED(a->a_tvp, NOTE_DELETE); 16 | -------------------------------------------------------------------------------- /patches/freebsd11-NOTE_OPEN-NOTE_CLOSE-NOTE_READ.patch: -------------------------------------------------------------------------------- 1 | Index: lib/libc/sys/kqueue.2 2 | =================================================================== 3 | --- lib/libc/sys/kqueue.2 (revision 272513) 4 | +++ lib/libc/sys/kqueue.2 (working copy) 5 | @@ -365,6 +365,12 @@ 6 | Access to the file was revoked via 7 | .Xr revoke 2 8 | or the underlying file system was unmounted. 9 | +.It NOTE_OPEN 10 | +The file referenced by the descriptor was opened. 11 | +.It NOTE_CLOSE 12 | +The file referenced by the descriptor was closed. 13 | +.It NOTE_READ 14 | +A read occurred on the file referenced by the descriptor. 15 | .El 16 | .Pp 17 | On return, 18 | Index: sys/kern/vfs_subr.c 19 | =================================================================== 20 | --- sys/kern/vfs_subr.c (revision 272513) 21 | +++ sys/kern/vfs_subr.c (working copy) 22 | @@ -4284,6 +4284,33 @@ 23 | VFS_KNOTE_LOCKED(a->a_dvp, NOTE_WRITE); 24 | } 25 | 26 | +void 27 | +vop_open_post(void *ap, int rc) 28 | +{ 29 | + struct vop_open_args *a = ap; 30 | + 31 | + if (!rc) 32 | + VFS_KNOTE_LOCKED(a->a_vp, NOTE_OPEN); 33 | +} 34 | + 35 | +void 36 | +vop_close_post(void *ap, int rc) 37 | +{ 38 | + struct vop_close_args *a = ap; 39 | + 40 | + if (!rc) 41 | + VFS_KNOTE_LOCKED(a->a_vp, NOTE_CLOSE); 42 | +} 43 | + 44 | +void 45 | +vop_read_post(void *ap, int rc) 46 | +{ 47 | + struct vop_read_args *a = ap; 48 | + 49 | + if (!rc) 50 | + VFS_KNOTE_LOCKED(a->a_vp, NOTE_READ); 51 | +} 52 | + 53 | static struct knlist fs_knlist; 54 | 55 | static void 56 | Index: sys/kern/vnode_if.src 57 | =================================================================== 58 | --- sys/kern/vnode_if.src (revision 272513) 59 | +++ sys/kern/vnode_if.src (working copy) 60 | @@ -121,6 +121,7 @@ 61 | 62 | 63 | %% open vp L L L 64 | +%! open post vop_open_post 65 | 66 | vop_open { 67 | IN struct vnode *vp; 68 | @@ -132,6 +133,7 @@ 69 | 70 | 71 | %% close vp L L L 72 | +%! close post vop_close_post 73 | 74 | vop_close { 75 | IN struct vnode *vp; 76 | @@ -186,6 +188,7 @@ 77 | }; 78 | 79 | %% read vp L L L 80 | +%! read post vop_read_post 81 | 82 | vop_read { 83 | IN struct vnode *vp; 84 | Index: sys/sys/event.h 85 | =================================================================== 86 | --- sys/sys/event.h (revision 272513) 87 | +++ sys/sys/event.h (working copy) 88 | @@ -118,6 +118,9 @@ 89 | #define NOTE_LINK 0x0010 /* link count changed */ 90 | #define NOTE_RENAME 0x0020 /* vnode was renamed */ 91 | #define NOTE_REVOKE 0x0040 /* vnode access was revoked */ 92 | +#define NOTE_OPEN 0x0080 /* vnode was opened */ 93 | +#define NOTE_CLOSE 0x0100 /* vnode was closed */ 94 | +#define NOTE_READ 0x0200 /* data content was read */ 95 | 96 | /* 97 | * data/hint flags for EVFILT_PROC and EVFILT_PROCDESC, shared with userspace 98 | Index: sys/sys/vnode.h 99 | =================================================================== 100 | --- sys/sys/vnode.h (revision 272513) 101 | +++ sys/sys/vnode.h (working copy) 102 | @@ -759,6 +759,7 @@ 103 | int vop_panic(struct vop_generic_args *ap); 104 | 105 | /* These are called from within the actual VOPS. */ 106 | +void vop_close_post(void *a, int rc); 107 | void vop_create_post(void *a, int rc); 108 | void vop_deleteextattr_post(void *a, int rc); 109 | void vop_link_post(void *a, int rc); 110 | @@ -768,6 +769,8 @@ 111 | void vop_lookup_pre(void *a); 112 | void vop_mkdir_post(void *a, int rc); 113 | void vop_mknod_post(void *a, int rc); 114 | +void vop_open_post(void *a, int rc); 115 | +void vop_read_post(void *a, int rc); 116 | void vop_remove_post(void *a, int rc); 117 | void vop_rename_post(void *a, int rc); 118 | void vop_rename_pre(void *a); 119 | -------------------------------------------------------------------------------- /patches/freebsd11-NOTE_REVOKE.patch: -------------------------------------------------------------------------------- 1 | Index: sys/kern/vfs_subr.c 2 | =================================================================== 3 | --- sys/kern/vfs_subr.c (revision 273960) 4 | +++ sys/kern/vfs_subr.c (working copy) 5 | @@ -2826,8 +2826,8 @@ 6 | delmntque(vp); 7 | cache_purge(vp); 8 | /* 9 | - * Done with purge, reset to the standard lock and invalidate 10 | - * the vnode. 11 | + * Done with purge, notify sleepers of the grim news, 12 | + * reset to the standard lock and invalidate the vnode. 13 | */ 14 | VI_LOCK(vp); 15 | vp->v_vnlock = &vp->v_lock; 16 | @@ -2834,6 +2834,7 @@ 17 | vp->v_op = &dead_vnodeops; 18 | vp->v_tag = "none"; 19 | vp->v_type = VBAD; 20 | + VFS_KNOTE_LOCKED(vp, NOTE_REVOKE); 21 | } 22 | 23 | /* 24 | -------------------------------------------------------------------------------- /patches/freebsd11-O_SYMLINK.patch: -------------------------------------------------------------------------------- 1 | Index: lib/libc/sys/open.2 2 | =================================================================== 3 | --- lib/libc/sys/open.2 (revision 272513) 4 | +++ lib/libc/sys/open.2 (working copy) 5 | @@ -115,6 +115,7 @@ 6 | O_FSYNC synchronous writes 7 | O_SYNC synchronous writes 8 | O_NOFOLLOW do not follow symlinks 9 | +O_SYMLINK allow open of symlinks 10 | O_NOCTTY don't assign controlling terminal 11 | O_TTY_INIT restore default terminal attributes 12 | O_DIRECTORY error if file is not a directory 13 | @@ -179,6 +180,14 @@ 14 | .Fn open 15 | will fail. 16 | .Pp 17 | +If 18 | +.Dv O_SYMLINK 19 | +is used in the mask and the target file passed to 20 | +.Fn open 21 | +is a symbolic link then the 22 | +.Fn open 23 | +will be for the symbolic link itself, not what it links to. 24 | +.Pp 25 | When opening a file, a lock with 26 | .Xr flock 2 27 | semantics can be obtained by setting 28 | Index: sys/kern/vfs_vnops.c 29 | =================================================================== 30 | --- sys/kern/vfs_vnops.c (revision 272513) 31 | +++ sys/kern/vfs_vnops.c (working copy) 32 | @@ -243,8 +243,8 @@ 33 | } 34 | } else { 35 | ndp->ni_cnd.cn_nameiop = LOOKUP; 36 | - ndp->ni_cnd.cn_flags = ISOPEN | 37 | - ((fmode & O_NOFOLLOW) ? NOFOLLOW : FOLLOW) | LOCKLEAF; 38 | + ndp->ni_cnd.cn_flags = ISOPEN | LOCKLEAF | 39 | + ((fmode & (O_NOFOLLOW | O_SYMLINK)) ? NOFOLLOW : FOLLOW); 40 | if (!(fmode & FWRITE)) 41 | ndp->ni_cnd.cn_flags |= LOCKSHARED; 42 | if (!(vn_open_flags & VN_OPEN_NOAUDIT)) 43 | @@ -281,7 +281,7 @@ 44 | struct flock lf; 45 | int error, have_flock, lock_flags, type; 46 | 47 | - if (vp->v_type == VLNK) 48 | + if (vp->v_type == VLNK && fmode & O_NOFOLLOW) 49 | return (EMLINK); 50 | if (vp->v_type == VSOCK) 51 | return (EOPNOTSUPP); 52 | @@ -291,6 +291,8 @@ 53 | if (fmode & (FWRITE | O_TRUNC)) { 54 | if (vp->v_type == VDIR) 55 | return (EISDIR); 56 | + if (vp->v_type == VLNK) 57 | + return (EMLINK); 58 | accmode |= VWRITE; 59 | } 60 | if (fmode & FREAD) 61 | @@ -753,6 +755,8 @@ 62 | uio->uio_td, td)); 63 | KASSERT(flags & FOF_OFFSET, ("No FOF_OFFSET")); 64 | vp = fp->f_vnode; 65 | + if (vp->v_type == VLNK) 66 | + return (EOPNOTSUPP); 67 | ioflag = 0; 68 | if (fp->f_flag & FNONBLOCK) 69 | ioflag |= IO_NDELAY; 70 | @@ -839,6 +843,8 @@ 71 | uio->uio_td, td)); 72 | KASSERT(flags & FOF_OFFSET, ("No FOF_OFFSET")); 73 | vp = fp->f_vnode; 74 | + if (vp->v_type == VLNK) 75 | + return (EOPNOTSUPP); 76 | if (vp->v_type == VREG) 77 | bwillwrite(); 78 | ioflag = IO_UNIT; 79 | @@ -1290,6 +1296,10 @@ 80 | error = EISDIR; 81 | goto out; 82 | } 83 | + if (vp->v_type == VLNK) { 84 | + error = EINVAL; 85 | + goto out; 86 | + } 87 | #ifdef MAC 88 | error = mac_vnode_check_write(active_cred, fp->f_cred, vp); 89 | if (error) 90 | Index: sys/sys/fcntl.h 91 | =================================================================== 92 | --- sys/sys/fcntl.h (revision 272513) 93 | +++ sys/sys/fcntl.h (working copy) 94 | @@ -129,6 +129,11 @@ 95 | #define O_CLOEXEC 0x00100000 96 | #endif 97 | 98 | +#if __BSD_VISIBLE 99 | +/* allow open of a symlink */ 100 | +#define O_SYMLINK 0x00200000 101 | +#endif 102 | + 103 | /* 104 | * XXX missing O_DSYNC, O_RSYNC. 105 | */ 106 | -------------------------------------------------------------------------------- /sys/inotify.h: -------------------------------------------------------------------------------- 1 | #ifndef __BSD_INOTIFY_H__ 2 | #define __BSD_INOTIFY_H__ 3 | 4 | #include 5 | 6 | #ifndef __THROW 7 | #ifdef __cplusplus 8 | #define __THROW throw() 9 | #else 10 | #define __THROW 11 | #endif 12 | #endif 13 | 14 | #ifdef __cplusplus 15 | #define INO_EXPORT extern "C" 16 | #else 17 | #define INO_EXPORT 18 | #endif 19 | 20 | 21 | 22 | /* Flags for the parameter of inotify_init1. */ 23 | #define IN_CLOEXEC 02000000 /* Linux x86 O_CLOEXEC */ 24 | #define IN_NONBLOCK 00004000 /* Linux x86 O_NONBLOCK */ 25 | 26 | 27 | /* Structure describing an inotify event. */ 28 | struct inotify_event 29 | { 30 | int wd; /* Watch descriptor. */ 31 | uint32_t mask; /* Watch mask. */ 32 | uint32_t cookie; /* Cookie to synchronize two events. */ 33 | uint32_t len; /* Length (including NULLs) of name. */ 34 | char name[]; /* Name. */ 35 | }; 36 | 37 | 38 | /* Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. */ 39 | #define IN_ACCESS 0x00000001 /* File was accessed. */ 40 | #define IN_MODIFY 0x00000002 /* File was modified. */ 41 | #define IN_ATTRIB 0x00000004 /* Metadata changed. */ 42 | #define IN_CLOSE_WRITE 0x00000008 /* Writtable file was closed. */ 43 | #define IN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed. */ 44 | #define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* Close. */ 45 | #define IN_OPEN 0x00000020 /* File was opened. */ 46 | #define IN_MOVED_FROM 0x00000040 /* File was moved from X. */ 47 | #define IN_MOVED_TO 0x00000080 /* File was moved to Y. */ 48 | #define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) /* Moves. */ 49 | #define IN_CREATE 0x00000100 /* Subfile was created. */ 50 | #define IN_DELETE 0x00000200 /* Subfile was deleted. */ 51 | #define IN_DELETE_SELF 0x00000400 /* Self was deleted. */ 52 | #define IN_MOVE_SELF 0x00000800 /* Self was moved. */ 53 | 54 | 55 | /* Additional events and flags. Some of these flags are unsupported, 56 | but still should be present */ 57 | #define IN_UNMOUNT 0x00002000 /* Backing fs was unmounted. */ 58 | #define IN_Q_OVERFLOW 0x00004000 /* Event queued overflowed. */ 59 | #define IN_IGNORED 0x00008000 /* File was ignored. */ 60 | 61 | #define IN_ONLYDIR 0x01000000 /* Only watch the path if it is a 62 | directory. */ 63 | #define IN_DONT_FOLLOW 0x02000000 /* Do not follow a sym link. */ 64 | #define IN_EXCL_UNLINK 0x04000000 /* Exclude events on unlinked 65 | objects. */ 66 | #define IN_MASK_ADD 0x20000000 /* Add to the mask of an already 67 | existing watch. */ 68 | #define IN_ISDIR 0x40000000 /* Event occurred against dir. */ 69 | #define IN_ONESHOT 0x80000000 /* Only send event once. */ 70 | 71 | 72 | /* 73 | * All of the events - we build the list by hand so that we can add flags in 74 | * the future and not break backward compatibility. Apps will get only the 75 | * events that they originally wanted. Be sure to add new events here! 76 | */ 77 | #define IN_ALL_EVENTS (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \ 78 | IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | IN_MOVE_SELF | \ 79 | IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF) 80 | 81 | 82 | /* Create and initialize inotify-kqueue instance. */ 83 | INO_EXPORT int inotify_init (void) __THROW; 84 | 85 | /* Create and initialize inotify-kqueue instance. */ 86 | INO_EXPORT int inotify_init1 (int flags) __THROW; 87 | 88 | /* Add watch of object NAME to inotify-kqueue instance FD. Notify about 89 | events specified by MASK. */ 90 | INO_EXPORT int inotify_add_watch (int fd, const char *name, uint32_t mask) __THROW; 91 | 92 | /* Remove the watch specified by WD from the inotify instance FD. */ 93 | INO_EXPORT int inotify_rm_watch (int fd, int wd) __THROW; 94 | 95 | 96 | #endif /* __BSD_INOTIFY_H__ */ 97 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | 34 | void get_event (int fd, const char * target); 35 | void handle_error (int error); 36 | 37 | /* ----------------------------------------------------------------- */ 38 | 39 | int main (int argc, char *argv[]) 40 | { 41 | char target[FILENAME_MAX]; 42 | int fd; 43 | int wd; /* watch descriptor */ 44 | 45 | struct rlimit rl; 46 | rl.rlim_cur = 3072; 47 | rl.rlim_max = 8172; 48 | setrlimit (RLIMIT_NOFILE, &rl); 49 | 50 | if (argc < 2) { 51 | fprintf (stderr, "Watching the current directory\n"); 52 | strcpy (target, "."); 53 | } 54 | else { 55 | fprintf (stderr, "Watching %s\n", argv[1]); 56 | strcpy (target, argv[1]); 57 | } 58 | 59 | fd = inotify_init(); 60 | if (fd < 0) { 61 | printf("inotify_init failed\n"); 62 | handle_error (errno); 63 | return 1; 64 | } 65 | 66 | wd = inotify_add_watch (fd, target, IN_ALL_EVENTS); 67 | if (wd < 0) { 68 | printf("add_watch failed\n"); 69 | handle_error (errno); 70 | return 1; 71 | } 72 | 73 | int ev_count = 0; 74 | 75 | while (1) { 76 | /* if (ev_count == 3) { */ 77 | /* printf("stopping watch\n"); */ 78 | /* inotify_rm_watch (fd, wd); */ 79 | /* } */ 80 | 81 | ++ev_count; 82 | get_event(fd, target); 83 | } 84 | 85 | return 0; 86 | } 87 | 88 | /* ----------------------------------------------------------------- */ 89 | /* Allow for 1024 simultanious events */ 90 | #define BUFF_SIZE ((sizeof(struct inotify_event)+FILENAME_MAX)*1024) 91 | 92 | void get_event (int fd, const char * target) 93 | { 94 | ssize_t len, i = 0; 95 | char buff[BUFF_SIZE] = {0}; 96 | 97 | len = read (fd, buff, BUFF_SIZE); 98 | 99 | while (i < len) { 100 | struct inotify_event *pevent = (struct inotify_event *)&buff[i]; 101 | char action[81+FILENAME_MAX] = {0}; 102 | 103 | if (pevent->len) 104 | strcpy (action, pevent->name); 105 | else 106 | strcpy (action, target); 107 | 108 | if (pevent->mask & IN_ACCESS) 109 | strcat(action, " was read"); 110 | if (pevent->mask & IN_ATTRIB) 111 | strcat(action, " Metadata changed"); 112 | if (pevent->mask & IN_CLOSE_WRITE) 113 | strcat(action, " opened for writing was closed"); 114 | if (pevent->mask & IN_CLOSE_NOWRITE) 115 | strcat(action, " not opened for writing was closed"); 116 | if (pevent->mask & IN_CREATE) 117 | strcat(action, " created in watched directory"); 118 | if (pevent->mask & IN_DELETE) 119 | strcat(action, " deleted from watched directory"); 120 | if (pevent->mask & IN_DELETE_SELF) 121 | strcat(action, " watched file/directory was itself deleted"); 122 | if (pevent->mask & IN_MODIFY) 123 | strcat(action, " was modified"); 124 | if (pevent->mask & IN_MOVE_SELF) 125 | strcat(action, " watched file/directory was itself moved"); 126 | if (pevent->mask & IN_MOVED_FROM) 127 | strcat(action, " moved out of watched directory"); 128 | if (pevent->mask & IN_MOVED_TO) 129 | strcat(action, " moved into watched directory"); 130 | if (pevent->mask & IN_OPEN) 131 | strcat(action, " was opened"); 132 | if (pevent->mask & IN_IGNORED) 133 | strcat(action, " was ignored"); 134 | if (pevent->mask & IN_UNMOUNT) 135 | strcat(action, " was unmounted"); 136 | 137 | /* 138 | printf ("wd=%d mask=%x cookie=%d len=%d dir=%s\n", 139 | pevent->wd, pevent->mask, pevent->cookie, pevent->len, 140 | (pevent->mask & IN_ISDIR)?"yes":"no"); 141 | 142 | if (pevent->len) printf ("name=%s\n", pevent->name); 143 | */ 144 | 145 | printf ("%s [%s]\n", action, pevent->name); 146 | 147 | i += sizeof(struct inotify_event) + pevent->len; 148 | 149 | } 150 | 151 | } /* get_event */ 152 | 153 | /* ----------------------------------------------------------------- */ 154 | 155 | void handle_error (int error) 156 | { 157 | fprintf (stderr, "Error: %s\n", strerror(error)); 158 | 159 | } /* handle_error */ 160 | 161 | /* ----------------------------------------------------------------- */ 162 | -------------------------------------------------------------------------------- /tests/bugs_test.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2014 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include "bugs_test.hh" 25 | 26 | bugs_test::bugs_test (journal &j) 27 | : test ("Bugfix tests", j) 28 | { 29 | } 30 | 31 | void bugs_test::setup () 32 | { 33 | cleanup (); 34 | system ("mkdir bugst-workdir"); 35 | system ("touch bugst-workdir/1"); 36 | system ("touch bugst-workdir/2"); 37 | 38 | } 39 | 40 | void bugs_test::run () 41 | { 42 | consumer cons; 43 | events received; 44 | events::iterator iter; 45 | int wid = 0; 46 | 47 | /* Issue #12 - directory diff calculation is not triggered for now-empty directories. */ 48 | cons.input.setup ("bugst-workdir", 49 | IN_ATTRIB | IN_MODIFY 50 | | IN_CREATE | IN_DELETE 51 | | IN_MOVED_FROM | IN_MOVED_TO 52 | | IN_MOVE_SELF | IN_DELETE_SELF); 53 | cons.output.wait (); 54 | wid = cons.output.added_watch_id (); 55 | 56 | cons.output.reset (); 57 | cons.input.receive (2); 58 | 59 | system ("rm bugst-workdir/1"); 60 | system ("rm bugst-workdir/2"); 61 | 62 | cons.output.wait (); 63 | received = cons.output.registered (); 64 | should ("receive IN_DELETE for bugst-workdir/1", 65 | contains (received, event ("1", wid, IN_DELETE))); 66 | should ("receive IN_DELETE for bugst-workdir/2", 67 | contains (received, event ("2", wid, IN_DELETE))); 68 | 69 | 70 | /* Test for extraneous IN_ATTRIB event on subdirectory creation and deletion */ 71 | cons.output.reset (); 72 | cons.input.receive (2); 73 | 74 | system ("mkdir bugst-workdir/1"); 75 | 76 | cons.output.wait (); 77 | received = cons.output.registered (); 78 | should ("receive IN_CREATE for bugst-workdir/1", 79 | contains (received, event ("1", wid, IN_CREATE))); 80 | should ("Not receive IN_ATTRIB for bugst-workdir on subdirectory creation", 81 | !contains (received, event ("", wid, IN_ATTRIB))); 82 | 83 | cons.output.reset (); 84 | cons.input.receive (2); 85 | 86 | system ("rmdir bugst-workdir/1"); 87 | 88 | cons.output.wait (); 89 | received = cons.output.registered (); 90 | should ("receive IN_DELETE for bugst-workdir/1", 91 | contains (received, event ("1", wid, IN_DELETE))); 92 | should ("Not receive IN_ATTRIB for bugst-workdir on subdirectory deletion", 93 | !contains (received, event ("", wid, IN_ATTRIB))); 94 | 95 | 96 | /* Test for not issuing IN_DELETE_SELF on hardlink deletes */ 97 | system ("touch bugst-workdir/1"); 98 | system ("ln bugst-workdir/1 bugst-workdir/2"); 99 | 100 | cons.input.setup ("bugst-workdir/1", IN_ATTRIB | IN_DELETE_SELF); 101 | cons.output.wait (); 102 | wid = cons.output.added_watch_id (); 103 | 104 | cons.output.reset (); 105 | cons.input.receive (); 106 | 107 | system ("rm bugst-workdir/2"); 108 | 109 | cons.output.wait (); 110 | received = cons.output.registered (); 111 | should ("receive IN_ATTRIB for bugst-workdir/1 on hardlink delete", 112 | contains (received, event ("", wid, IN_ATTRIB))); 113 | should ("Not receive IN_DELETE_SELF for bugst-workdir/1 on hardlink delete", 114 | !contains (received, event ("", wid, IN_DELETE_SELF))); 115 | 116 | 117 | /* Test subwatch adding when no IN_(CREATE|DELETE|MOVE) flags specified */ 118 | cons.input.setup ("bugst-workdir", IN_ATTRIB | IN_MODIFY); 119 | cons.output.wait (); 120 | wid = cons.output.added_watch_id (); 121 | 122 | cons.output.reset (); 123 | cons.input.receive (); 124 | 125 | system ("touch bugst-workdir/2"); 126 | system ("touch bugst-workdir/2"); 127 | system ("echo test > bugst-workdir/2"); 128 | 129 | cons.output.wait (); 130 | received = cons.output.registered (); 131 | should ("receive IN_ATTRIB for bugst-workdir/2 on touch", 132 | contains (received, event ("2", wid, IN_ATTRIB))); 133 | should ("receive IN_MODIFY for bugst-workdir/2 on echo", 134 | contains (received, event ("2", wid, IN_MODIFY))); 135 | 136 | cons.input.interrupt (); 137 | } 138 | 139 | void bugs_test::cleanup () 140 | { 141 | system ("rm -rf bugst-workdir"); 142 | } 143 | -------------------------------------------------------------------------------- /tests/bugs_test.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2014 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __BUGS_TEST__HH__ 24 | #define __BUGS_TEST__HH__ 25 | 26 | #include "core/core.hh" 27 | 28 | class bugs_test: public test { 29 | protected: 30 | virtual void setup (); 31 | virtual void run (); 32 | virtual void cleanup (); 33 | 34 | public: 35 | bugs_test (journal &j); 36 | }; 37 | 38 | #endif // __BUGS_TEST_HH__ 39 | -------------------------------------------------------------------------------- /tests/core/action.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include "log.hh" 24 | #include "action.hh" 25 | 26 | 27 | action::action (const std::string &name_) 28 | : name (name_) 29 | , interrupted (false) 30 | { 31 | pthread_barrier_init (&barrier, NULL, 2); 32 | } 33 | 34 | action::~action () 35 | { 36 | pthread_barrier_destroy (&barrier); 37 | } 38 | 39 | bool action::wait () 40 | { 41 | pthread_barrier_wait (&barrier); 42 | return !interrupted; 43 | } 44 | 45 | void action::interrupt () 46 | { 47 | LOG (name << ": Marking action interrupted"); 48 | interrupted = true; 49 | wait (); 50 | } 51 | 52 | void action::reset () 53 | { 54 | LOG (name << ": Resetting"); 55 | interrupted = false; 56 | } 57 | 58 | std::string action::named () const 59 | { 60 | return name; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /tests/core/action.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __ACTION_HH__ 24 | #define __ACTION_HH__ 25 | 26 | #include "platform.hh" 27 | 28 | class action { 29 | pthread_barrier_t barrier; 30 | 31 | const std::string name; 32 | volatile bool interrupted; 33 | 34 | public: 35 | action (const std::string &name_); 36 | virtual ~action () = 0; 37 | 38 | bool wait (); 39 | void interrupt (); 40 | void reset (); 41 | 42 | std::string named () const; 43 | }; 44 | 45 | #endif // __ACTION_HH__ 46 | -------------------------------------------------------------------------------- /tests/core/consumer.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include 25 | #include 26 | #include "log.hh" 27 | #include "consumer.hh" 28 | 29 | consumer::consumer () 30 | { 31 | pthread_create (&self, NULL, consumer::run_, this); 32 | } 33 | 34 | consumer::~consumer () 35 | { 36 | // It is a trick. The consumer object lives in a separate thread that is created 37 | // in its constructor. However, the object itself is created in another (parent) 38 | // thread, so the destructor should work in the same thread (counting on static 39 | // allocation). 40 | LOG ("CONS: Joining on self"); 41 | pthread_join (self, NULL); 42 | } 43 | 44 | void* consumer::run_ (void *ptr) 45 | { 46 | assert (ptr != NULL); 47 | ((consumer *) ptr)->run(); 48 | return NULL; 49 | } 50 | 51 | void consumer::register_activity (request::activity activity) 52 | { 53 | events received = ino.receive_during (activity.timeout); 54 | LOG ("CONS: Okay, informing producer about results..."); 55 | input.reset (); 56 | output.setup (received); 57 | } 58 | 59 | void consumer::add_modify_watch (request::add_modify add_modify) 60 | { 61 | uint32_t id = ino.watch (add_modify.path, add_modify.mask); 62 | int error = errno; 63 | LOG ("CONS: Added watch"); 64 | input.reset (); 65 | output.setup (id, error); 66 | } 67 | 68 | void consumer::remove_watch (request::remove remove) 69 | { 70 | ino.cancel (remove.watch_id); 71 | LOG ("CONS: Cancelled watch"); 72 | input.reset (); 73 | output.wait (); 74 | } 75 | 76 | void consumer::run () 77 | { 78 | while (input.wait ()) { 79 | switch (input.current_variant ()) { 80 | case request::REGISTER_ACTIVITY: 81 | register_activity (input.activity_data ()); 82 | break; 83 | 84 | case request::ADD_MODIFY_WATCH: 85 | add_modify_watch (input.add_modify_data ()); 86 | break; 87 | 88 | case request::REMOVE_WATCH: 89 | remove_watch (input.remove_data ()); 90 | break; 91 | } 92 | 93 | LOG ("CONS: Sleeping on input"); 94 | } 95 | } 96 | 97 | int consumer::get_fd () 98 | { 99 | return ino.get_fd (); 100 | } 101 | -------------------------------------------------------------------------------- /tests/core/consumer.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __CONSUMER_HH__ 24 | #define __CONSUMER_HH__ 25 | 26 | #include "platform.hh" 27 | #include "request.hh" 28 | #include "response.hh" 29 | #include "inotify_client.hh" 30 | 31 | class consumer { 32 | std::string path; 33 | inotify_client ino; 34 | pthread_t self; 35 | 36 | // yes, these methods take arguments by a value. intentionally 37 | void register_activity (request::activity activity); 38 | void add_modify_watch (request::add_modify add_modify); 39 | void remove_watch (request::remove remove); 40 | 41 | public: 42 | request input; 43 | response output; 44 | 45 | consumer (); 46 | ~consumer (); 47 | static void* run_ (void *ptr); 48 | void run (); 49 | int get_fd (); 50 | }; 51 | 52 | 53 | #endif // __CONSUMER_HH__ 54 | -------------------------------------------------------------------------------- /tests/core/core.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __CORE_HH__ 24 | #define __CORE_HH__ 25 | 26 | #include "platform.hh" 27 | #include "log.hh" 28 | #include "event.hh" 29 | #include "action.hh" 30 | #include "request.hh" 31 | #include "response.hh" 32 | #include "inotify_client.hh" 33 | #include "consumer.hh" 34 | #include "journal.hh" 35 | #include "test.hh" 36 | 37 | #endif // __CORE_HH__ 38 | -------------------------------------------------------------------------------- /tests/core/event.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011-2014 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include "event.hh" 25 | #include "log.hh" 26 | 27 | event::event (const std::string &filename_, int watch_, uint32_t flags_) 28 | : filename (filename_) 29 | , watch (watch_) 30 | , flags (flags_) 31 | , cookie (0) 32 | { 33 | } 34 | 35 | bool event::operator< (const event &ev) const 36 | { 37 | if (watch == ev.watch) { 38 | if (filename == ev.filename) { 39 | return flags < ev.flags; 40 | } else { 41 | return filename < ev.filename; 42 | } 43 | } else { 44 | return watch < ev.watch; 45 | } 46 | } 47 | 48 | event_matcher::event_matcher (const event &ev_) 49 | : ev(ev_) 50 | { 51 | LOG ("Created matcher " << ev.filename << ':' << ev.watch); 52 | } 53 | 54 | bool event_matcher::operator() (const event &ev_) const 55 | { 56 | LOG ("matching " << ev.filename << ':' << ev.watch << '&' << ev.flags << 57 | " against " << ev_.filename << ':' << ev_.watch << '&' << ev_.flags); 58 | 59 | return (ev.filename == ev_.filename 60 | && ev.watch == ev_.watch 61 | && (ev.flags & ev_.flags)); 62 | } 63 | 64 | 65 | bool contains (const events &ev, const event &ev_) 66 | { 67 | event_matcher matcher (ev_); 68 | events::iterator iter = std::find_if (ev.begin(), ev.end(), matcher); 69 | return (iter != ev.end()); 70 | } 71 | -------------------------------------------------------------------------------- /tests/core/event.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __EVENT_HH__ 24 | #define __EVENT_HH__ 25 | 26 | #include 27 | #include "platform.hh" 28 | 29 | struct event { 30 | std::string filename; 31 | int watch; 32 | uint32_t flags; 33 | uint32_t cookie; 34 | 35 | event (const std::string &filename_ = "", int watch_ = 0, uint32_t flags_ = 0); 36 | bool operator< (const event &ev) const; 37 | }; 38 | 39 | typedef std::set events; 40 | 41 | class event_matcher { 42 | event ev; 43 | 44 | public: 45 | event_matcher (const event &ev_); 46 | bool operator() (const event &ev_) const; 47 | }; 48 | 49 | bool contains (const events &ev, const event &ev_); 50 | 51 | #endif // __EVENT_HH__ 52 | -------------------------------------------------------------------------------- /tests/core/inotify_client.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011-2014 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "inotify_client.hh" 30 | #include "log.hh" 31 | 32 | #include 33 | 34 | inotify_client::inotify_client () 35 | : fd (inotify_init()) 36 | { 37 | assert (fd != -1); 38 | } 39 | 40 | inotify_client::~inotify_client () 41 | { 42 | close (fd); 43 | } 44 | 45 | int inotify_client::watch (const std::string &filename, uint32_t flags) 46 | { 47 | assert (fd != -1); 48 | LOG ("INO: Adding " << VAR (filename) << VAR (flags)); 49 | 50 | int retval = inotify_add_watch (fd, filename.c_str(), flags); 51 | LOG ("INO: " << VAR (retval)); 52 | return retval; 53 | } 54 | 55 | void inotify_client::cancel (int watch_id) 56 | { 57 | assert (fd != -1); 58 | if (inotify_rm_watch (fd, watch_id) != 0) { 59 | LOG ("INO: rm watch failed " << VAR (fd) << VAR (watch_id)); 60 | } 61 | } 62 | 63 | #define IE_BUFSIZE (((sizeof (struct inotify_event) + FILENAME_MAX)) * 20) 64 | 65 | events inotify_client::receive_during (int timeout) const 66 | { 67 | events received; 68 | struct pollfd pfd; 69 | 70 | time_t start = time (NULL); 71 | time_t elapsed = 0; 72 | 73 | LOG ("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"); 74 | 75 | while ((elapsed = time (NULL) - start) < timeout) { 76 | memset (&pfd, 0, sizeof (struct pollfd)); 77 | pfd.fd = fd; 78 | pfd.events = POLLIN; 79 | 80 | LOG ("INO: Polling with " << VAR (timeout)); 81 | int pollretval = poll (&pfd, 1, timeout * 1000); 82 | LOG ("INO: Poll returned " << VAR (pollretval) << ", " << VAR(pfd.revents)); 83 | if (pollretval == -1) { 84 | return events(); 85 | } 86 | 87 | if (pfd.revents & POLLIN) { 88 | char buffer[IE_BUFSIZE]; 89 | char *ptr = buffer; 90 | int avail = read (fd, buffer, IE_BUFSIZE); 91 | 92 | /* The construction is probably harmful */ 93 | while (avail >= sizeof (struct inotify_event *)) { 94 | struct inotify_event *ie = (struct inotify_event *) ptr; 95 | event ev; 96 | 97 | if (ie->len) { 98 | ev.filename = ie->name; 99 | } 100 | ev.flags = ie->mask; 101 | ev.watch = ie->wd; 102 | ev.cookie = ie->cookie; 103 | 104 | LOG ("INO: Got next event! " << VAR (ev.filename) << VAR (ev.watch) << VAR (ev.flags)); 105 | received.insert (ev); 106 | 107 | int offset = sizeof (struct inotify_event) + ie->len; 108 | avail -= offset; 109 | ptr += offset; 110 | } 111 | } 112 | } 113 | 114 | LOG ("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv"); 115 | 116 | return received; 117 | } 118 | 119 | long inotify_client::bytes_available (int fd) 120 | { 121 | long int avail = 0; 122 | size_t bytes; 123 | 124 | if (ioctl (fd, FIONREAD, (char *) &bytes) >= 0) 125 | avail = (long int) *((int *) &bytes); 126 | 127 | return avail; 128 | } 129 | 130 | int inotify_client::get_fd () 131 | { 132 | return fd; 133 | } 134 | -------------------------------------------------------------------------------- /tests/core/inotify_client.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __INOTIFY_CLIENT_HH__ 24 | #define __INOTIFY_CLIENT_HH__ 25 | 26 | #include "platform.hh" 27 | #include "event.hh" 28 | 29 | class inotify_client { 30 | int fd; 31 | 32 | public: 33 | inotify_client (); 34 | ~inotify_client (); 35 | int watch (const std::string &filename, uint32_t flags); 36 | void cancel (int wid); 37 | events receive_during (int timeout) const; 38 | int get_fd (); 39 | 40 | static long bytes_available (int fd); 41 | }; 42 | 43 | #endif // __INOTIFY_CLIENT_HH__ 44 | -------------------------------------------------------------------------------- /tests/core/journal.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include "journal.hh" 25 | 26 | journal::entry::entry (const std::string &name_, status_e status_) 27 | : name (name_) 28 | , status (status_) 29 | { 30 | } 31 | 32 | journal::channel::channel (const std::string &name_) 33 | : name (name_) 34 | { 35 | } 36 | 37 | void journal::channel::pass (const std::string &test_name) 38 | { 39 | results.push_back (entry (test_name, entry::PASSED)); 40 | } 41 | 42 | void journal::channel::fail (const std::string &test_name) 43 | { 44 | results.push_back (entry (test_name, entry::FAILED)); 45 | } 46 | 47 | void journal::channel::summarize (int &passed, int &failed) const 48 | { 49 | passed = 0; 50 | failed = 0; 51 | 52 | bool header_printed = false; 53 | 54 | for (entry_list::const_iterator iter = results.begin(); 55 | iter != results.end(); 56 | ++iter) { 57 | 58 | if (iter->status == entry::PASSED) { 59 | ++passed; 60 | } else { 61 | ++failed; 62 | 63 | if (!header_printed) { 64 | header_printed = true; 65 | std::cout << std::endl << "In test \"" << name << "\":" << std::endl; 66 | } 67 | std::cout << " failed: " << iter->name << std::endl; 68 | } 69 | } 70 | 71 | // if (header_printed) { 72 | // std::cout << std::endl; 73 | // } 74 | } 75 | 76 | journal::journal () 77 | { 78 | pthread_mutex_init (&channels_mutex, NULL); 79 | } 80 | 81 | journal::~journal () 82 | { 83 | pthread_mutex_destroy (&channels_mutex); 84 | } 85 | 86 | journal::channel& journal::allocate_channel (const std::string &name) 87 | { 88 | pthread_mutex_lock (&channels_mutex); 89 | channels.push_back (journal::channel (name)); 90 | journal::channel &ref = *channels.rbegin(); 91 | pthread_mutex_unlock (&channels_mutex); 92 | return ref; 93 | } 94 | 95 | void journal::summarize () const 96 | { 97 | pthread_mutex_lock (&channels_mutex); 98 | 99 | int total_passed = 0, total_failed = 0; 100 | 101 | std::cout << std::endl; 102 | 103 | for (channel_list::const_iterator iter = channels.begin(); 104 | iter != channels.end(); 105 | ++iter) { 106 | int passed = 0, failed = 0; 107 | iter->summarize (passed, failed); 108 | total_passed += passed; 109 | total_failed += failed; 110 | } 111 | 112 | int total = total_passed + total_failed; 113 | if (total_failed) { 114 | std::cout << std::endl; 115 | } 116 | std::cout << "--------------------" << std::endl 117 | << " Run: " << total << std::endl 118 | << " Passed: " << total_passed << std::endl 119 | << " Failed: " << total_failed << std::endl; 120 | 121 | pthread_mutex_unlock (&channels_mutex); 122 | } 123 | -------------------------------------------------------------------------------- /tests/core/journal.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __JOURNAL_HH__ 24 | #define __JOURNAL_HH__ 25 | 26 | #include 27 | #include 28 | #include "platform.hh" 29 | 30 | class journal { 31 | public: 32 | struct entry { 33 | std::string name; 34 | enum status_e { 35 | PASSED, 36 | FAILED 37 | } status; 38 | 39 | explicit entry (const std::string &name_ = "", status_e status_ = PASSED); 40 | }; 41 | 42 | class channel { 43 | typedef std::list entry_list; 44 | entry_list results; 45 | std::string name; 46 | 47 | public: 48 | channel (const std::string &name_); 49 | 50 | void pass (const std::string &test_name); 51 | void fail (const std::string &test_name); 52 | 53 | void summarize (int &passed, int &failed) const; 54 | }; 55 | 56 | journal (); 57 | ~journal (); 58 | channel& allocate_channel (const std::string &name); 59 | void summarize () const; 60 | 61 | private: 62 | // It would be better to use shared pointers here, but I dont want to 63 | // introduce a dependency like Boost. Raw references are harmful anyway 64 | typedef std::list channel_list; 65 | mutable pthread_mutex_t channels_mutex; 66 | channel_list channels; 67 | }; 68 | 69 | #endif // __JOURNAL_HH__ 70 | -------------------------------------------------------------------------------- /tests/core/log.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #if defined(__FreeBSD__) 25 | #include 26 | #elif defined(__linux__) 27 | #include 28 | #endif 29 | 30 | static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; 31 | 32 | void acquire_log_lock () 33 | { 34 | pthread_mutex_lock (&log_mutex); 35 | } 36 | 37 | void release_log_lock () 38 | { 39 | pthread_mutex_unlock (&log_mutex); 40 | } 41 | 42 | unsigned long current_thread () 43 | { 44 | #ifdef __linux__ 45 | return static_cast(pthread_self ()); 46 | #elif defined (__NetBSD__) || defined (__OpenBSD__) || \ 47 | defined(__APPLE__) || defined(__DragonFly__) 48 | return reinterpret_cast(pthread_self ()); 49 | #elif defined (__FreeBSD__) 50 | return reinterpret_cast(pthread_self ()); 51 | #else 52 | # error Currently unsupported 53 | #endif 54 | } 55 | -------------------------------------------------------------------------------- /tests/core/log.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __LOG_HH__ 24 | #define __LOG_HH__ 25 | 26 | #include 27 | 28 | void acquire_log_lock (); 29 | void release_log_lock (); 30 | 31 | unsigned long current_thread (); 32 | 33 | 34 | #ifdef ENABLE_LOGGING 35 | # define LOG(X) \ 36 | do { \ 37 | acquire_log_lock (); \ 38 | std::cout \ 39 | << current_thread () << " " \ 40 | << X \ 41 | << std::endl; \ 42 | release_log_lock (); \ 43 | } while (0) 44 | # define VAR(X) \ 45 | '[' << #X << ": " << X << "] " 46 | #else 47 | # define LOG(X) 48 | # define VAR(X) 49 | #endif // ENABLE_LOGGING 50 | 51 | #endif // __LOG_HH__ 52 | -------------------------------------------------------------------------------- /tests/core/platform.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __PLATFORM_HH__ 24 | #define __PLATFORM_HH__ 25 | 26 | #include // NULL 27 | #include 28 | 29 | extern "C" { 30 | #include "compat.h" 31 | } 32 | 33 | #ifdef __linux__ 34 | # include 35 | #elif defined (__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ 36 | defined(__APPLE__) || defined(__DragonFly__) 37 | # include "sys/inotify.h" 38 | #else 39 | # error Currently unsupported 40 | #endif 41 | 42 | #endif // __PLATFORM_HH__ 43 | -------------------------------------------------------------------------------- /tests/core/request.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include "log.hh" 25 | #include "request.hh" 26 | 27 | request::request () 28 | : action ("REQUEST") 29 | { 30 | } 31 | 32 | void request::receive (unsigned int timeout) 33 | { 34 | LOG (named() << ": Setting up to register an activity"); 35 | current = REGISTER_ACTIVITY; 36 | variants._act.timeout = timeout; 37 | wait (); 38 | } 39 | 40 | void request::setup (const std::string &path, uint32_t mask) 41 | { 42 | LOG (named() << ": Setting up to watch a path"); 43 | current = ADD_MODIFY_WATCH; 44 | variants._am.path = path; 45 | variants._am.mask = mask; 46 | wait (); 47 | } 48 | 49 | void request::setup (int rm_id) 50 | { 51 | LOG (named() << ": Setting up to stop a watch"); 52 | current = REMOVE_WATCH; 53 | variants._rm.watch_id = rm_id; 54 | wait (); 55 | } 56 | 57 | request::variant request::current_variant () const 58 | { 59 | return current; 60 | } 61 | 62 | request::activity request::activity_data () const 63 | { 64 | assert (current == REGISTER_ACTIVITY); 65 | return variants._act; 66 | } 67 | 68 | request::add_modify request::add_modify_data () const 69 | { 70 | assert (current == ADD_MODIFY_WATCH); 71 | return variants._am; 72 | } 73 | 74 | request::remove request::remove_data () const 75 | { 76 | assert (current == REMOVE_WATCH); 77 | return variants._rm; 78 | } 79 | 80 | -------------------------------------------------------------------------------- /tests/core/request.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __REQUEST_HH__ 24 | #define __REQUEST_HH__ 25 | 26 | #include "platform.hh" 27 | #include "event.hh" 28 | #include "action.hh" 29 | 30 | class request: public action { 31 | public: 32 | enum variant { 33 | REGISTER_ACTIVITY, 34 | ADD_MODIFY_WATCH, 35 | REMOVE_WATCH, 36 | }; 37 | 38 | struct activity { 39 | int timeout; 40 | }; 41 | 42 | struct add_modify { 43 | std::string path; 44 | uint32_t mask; 45 | }; 46 | 47 | struct remove { 48 | int watch_id; 49 | }; 50 | 51 | private: 52 | // Not a union, because its members are non-POD 53 | struct { 54 | activity _act; 55 | add_modify _am; 56 | remove _rm; 57 | } variants; 58 | 59 | variant current; 60 | 61 | public: 62 | request (); 63 | void receive (unsigned int timeout = 2); 64 | void setup (const std::string &path, uint32_t mask); 65 | void setup (int rm_id); 66 | 67 | variant current_variant () const; 68 | activity activity_data () const; 69 | add_modify add_modify_data () const; 70 | remove remove_data () const; 71 | }; 72 | 73 | #endif // __REQUEST_HH__ 74 | -------------------------------------------------------------------------------- /tests/core/response.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include "log.hh" 25 | #include "response.hh" 26 | 27 | response::response () 28 | : action ("RESPONSE") 29 | { 30 | } 31 | 32 | void response::setup (const events ®istered) 33 | { 34 | LOG (named() << ": Passing back unregistered events"); 35 | current = REGISTERED_EVENTS; 36 | variants._registered = registered; 37 | wait (); 38 | LOG (named() << " YAY!!!"); 39 | } 40 | 41 | void response::setup (int watch_id, int error) 42 | { 43 | LOG (named() << ": Passing back new watch id"); 44 | current = WATCH_ID; 45 | variants._watch_id = watch_id; 46 | variants._error = error; 47 | // printf("Response: settup up for id %d\n", watch_id); 48 | wait (); 49 | } 50 | 51 | response::variant response::current_variant () const 52 | { 53 | return current; 54 | } 55 | 56 | events response::registered () const 57 | { 58 | assert (current == REGISTERED_EVENTS); 59 | return variants._registered; 60 | } 61 | 62 | int response::added_watch_id () const 63 | { 64 | assert (current == WATCH_ID); 65 | return variants._watch_id; 66 | } 67 | 68 | int response::added_watch_error () const 69 | { 70 | assert (current == WATCH_ID); 71 | return variants._error; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /tests/core/response.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __RESPONSE_HH__ 24 | #define __RESPONSE_HH__ 25 | 26 | #include "platform.hh" 27 | #include "event.hh" 28 | #include "action.hh" 29 | 30 | class response: public action { 31 | public: 32 | enum variant { 33 | REGISTERED_EVENTS, 34 | WATCH_ID, 35 | }; 36 | 37 | private: 38 | // Again, not a union 39 | struct { 40 | events _registered; 41 | int _watch_id; 42 | int _error; 43 | } variants; 44 | 45 | variant current; 46 | 47 | public: 48 | response (); 49 | void setup (const events ®istered); 50 | void setup (int watch_id, int error); 51 | 52 | variant current_variant () const; 53 | events registered () const; 54 | int added_watch_id () const; 55 | int added_watch_error () const; 56 | }; 57 | 58 | #endif // __RESPONSE_HH__ 59 | -------------------------------------------------------------------------------- /tests/core/test.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011-2014 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include "test.hh" 28 | 29 | test::test (const std::string &name_, journal &j) 30 | : jc (j.allocate_channel (name_)) 31 | { 32 | } 33 | 34 | test::~test () 35 | { 36 | } 37 | 38 | void* test::run_ (void *ptr) 39 | { 40 | assert (ptr != NULL); 41 | test *t = static_cast(ptr); 42 | t->setup (); 43 | t->run (); 44 | t->cleanup (); 45 | return NULL; 46 | } 47 | 48 | void test::start () 49 | { 50 | pthread_create (&thread, NULL, test::run_, this); 51 | } 52 | 53 | void test::wait_for_end () 54 | { 55 | pthread_join (thread, NULL); 56 | } 57 | 58 | bool test::should (const std::string &test_name, bool exp) 59 | { 60 | if (exp) { 61 | pass (test_name); 62 | } else { 63 | fail (test_name); 64 | } 65 | return exp; 66 | } 67 | 68 | void test::pass (const std::string &test_name) 69 | { 70 | std::cout << "."; 71 | std::cout.flush (); 72 | jc.pass (test_name); 73 | } 74 | 75 | void test::fail (const std::string &test_name) 76 | { 77 | std::cout << "x"; 78 | std::cout.flush (); 79 | jc.fail (test_name); 80 | } 81 | -------------------------------------------------------------------------------- /tests/core/test.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __TEST_HH__ 24 | #define __TEST_HH__ 25 | 26 | #include 27 | #include "platform.hh" 28 | #include "journal.hh" 29 | 30 | class test { 31 | journal::channel &jc; 32 | pthread_t thread; 33 | 34 | protected: 35 | static void* run_ (void *ptr); 36 | 37 | virtual void setup () = 0; 38 | virtual void run () = 0; 39 | virtual void cleanup () = 0; 40 | 41 | public: 42 | test (const std::string &name_, journal &j); 43 | virtual ~test (); 44 | 45 | void start (); 46 | void wait_for_end (); 47 | 48 | bool should (const std::string &test_name, bool exp); 49 | void pass (const std::string &test_name); 50 | void fail (const std::string &test_name); 51 | }; 52 | 53 | 54 | #endif // __TEST_HH__ 55 | -------------------------------------------------------------------------------- /tests/fail_test.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | Copyright (c) 2015 Vladimir Kondratiev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | *******************************************************************************/ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "fail_test.hh" 29 | 30 | #define INVALID_FILENO 10000 31 | #define NONINOTIFY_FILENO 1 /* stdout */ 32 | 33 | static std::jmp_buf jbuf; 34 | static void catch_segv (int signo) 35 | { 36 | std::longjmp (jbuf, 1); 37 | } 38 | 39 | fail_test::fail_test (journal &j) 40 | : test ("Failures", j) 41 | { 42 | } 43 | 44 | void fail_test::setup () 45 | { 46 | cleanup (); 47 | system ("touch fail-working"); 48 | } 49 | 50 | void fail_test::run () 51 | { 52 | consumer cons; 53 | sig_t oldsegvhandler; 54 | int wid = 0; 55 | int error = 0; 56 | 57 | wid = inotify_add_watch (INVALID_FILENO, "fail-working", IN_ALL_EVENTS); 58 | should ("watch id is -1, errno set to EBADF when starting watching an " 59 | "invalid file descriptor", wid == -1 && errno == EBADF); 60 | 61 | cons.output.reset (); 62 | error = inotify_rm_watch (INVALID_FILENO, 0); 63 | should ("watch id is -1, errno set to EBADF when stopping watching an " 64 | "invalid file descriptor", error == -1 && errno == EBADF); 65 | 66 | cons.output.reset (); 67 | wid = inotify_add_watch (NONINOTIFY_FILENO, "fail-working", IN_ALL_EVENTS); 68 | should ("watch id is -1, errno set to EINVAL when starting watching a " 69 | "valid noninotify file descriptor", wid == -1 && errno == EINVAL); 70 | 71 | cons.output.reset (); 72 | error = inotify_rm_watch (NONINOTIFY_FILENO, 0); 73 | should ("inotify_rm_watch returns -1, errno set to EINVAL when stopping " 74 | "watching a valid noninotify file descriptor", 75 | error == -1 && errno == EINVAL); 76 | 77 | cons.output.reset (); 78 | error = inotify_rm_watch (cons.get_fd (), INVALID_FILENO); 79 | should ("inotify_rm_watch returns -1, errno set to EINVAL when stopping " 80 | "watching an invalid watch descriptor", 81 | error == -1 && errno == EINVAL); 82 | 83 | cons.output.reset (); 84 | error = inotify_rm_watch (cons.get_fd (), NONINOTIFY_FILENO); 85 | should ("inotify_rm_watch returns -1, errno set to EINVAL when stopping " 86 | "watching an noninotify watch descriptor", 87 | error == -1 && errno == EINVAL); 88 | 89 | /* Add a watch, should fail */ 90 | cons.output.reset (); 91 | cons.input.setup ("non-existent", IN_ALL_EVENTS); 92 | cons.output.wait (); 93 | 94 | wid = cons.output.added_watch_id (); 95 | error = cons.output.added_watch_error (); 96 | should ("watch id is -1, errno set to ENOENT when starting watching a " 97 | "non-existent file", wid == -1 && error == ENOENT); 98 | 99 | cons.output.reset (); 100 | cons.input.setup ("fail-working", IN_ATTRIB | IN_ONLYDIR); 101 | cons.output.wait (); 102 | 103 | wid = cons.output.added_watch_id (); 104 | error = cons.output.added_watch_error (); 105 | should ("watch id is -1, errno set to ENOTDIR when starting watching a " 106 | "file with IN_ONLYDIR flag set", wid == -1 && error == ENOTDIR); 107 | 108 | cons.output.reset (); 109 | cons.input.setup ("fail-working", 0); 110 | cons.output.wait (); 111 | 112 | wid = cons.output.added_watch_id (); 113 | error = cons.output.added_watch_error (); 114 | should ("watch id is -1, errno set to EINVAL when starting watching a " 115 | "file with no event flags set", wid == -1 && error == EINVAL); 116 | 117 | cons.output.reset (); 118 | chmod ("fail-working", 0); 119 | cons.input.setup ("fail-working", IN_ALL_EVENTS); 120 | cons.output.wait (); 121 | 122 | wid = cons.output.added_watch_id (); 123 | error = cons.output.added_watch_error (); 124 | should ("watch id is -1, errno set to EACCES when starting watching a " 125 | "file without read access", wid == -1 && error == EACCES); 126 | 127 | cons.output.reset (); 128 | oldsegvhandler = std::signal (SIGSEGV, catch_segv); 129 | if (setjmp(jbuf) == 0) { 130 | wid = inotify_add_watch (cons.get_fd (), (char *)-1, IN_ALL_EVENTS); 131 | } else { 132 | std::cout << "SIGSEGV catched!!! other tests will probably fail\n"; 133 | wid = -1; 134 | errno = -SIGSEGV; /* Invalid errno */ 135 | } 136 | std::signal (SIGSEGV, oldsegvhandler); 137 | should ("watch id is -1, errno set to EFAULT when starting watching a " 138 | "file with pathname pointing outside of the process's accessible " 139 | "address space", wid == -1 && errno == EFAULT); 140 | 141 | cons.input.interrupt (); 142 | } 143 | 144 | void fail_test::cleanup () 145 | { 146 | system ("rm -rf fail-working"); 147 | } 148 | -------------------------------------------------------------------------------- /tests/fail_test.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __FAIL_TEST_HH__ 24 | #define __FAIL_TEST_HH__ 25 | 26 | #include "core/core.hh" 27 | 28 | class fail_test: public test { 29 | protected: 30 | virtual void setup (); 31 | virtual void run (); 32 | virtual void cleanup (); 33 | 34 | public: 35 | fail_test (journal &j); 36 | }; 37 | 38 | #endif // __NOTIFICAIONTS_TEST_HH__ 39 | -------------------------------------------------------------------------------- /tests/notifications_dir_test.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011-2014 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __NOTIFICATIONS_DIR_TEST_HH__ 24 | #define __NOTIFICATIONS_DIR_TEST_HH__ 25 | 26 | #include "core/core.hh" 27 | 28 | class notifications_dir_test: public test { 29 | protected: 30 | virtual void setup (); 31 | virtual void run (); 32 | virtual void cleanup (); 33 | 34 | public: 35 | notifications_dir_test (journal &j); 36 | }; 37 | 38 | #endif // __NOTIFICAIONS_DIR_TEST_HH__ 39 | -------------------------------------------------------------------------------- /tests/notifications_test.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | 25 | #include "notifications_test.hh" 26 | 27 | notifications_test::notifications_test (journal &j) 28 | : test ("File notifications", j) 29 | { 30 | } 31 | 32 | void notifications_test::setup () 33 | { 34 | cleanup (); 35 | system ("touch ntfst-working"); 36 | } 37 | 38 | void notifications_test::run () 39 | { 40 | consumer cons; 41 | events received; 42 | int wid = 0; 43 | 44 | 45 | cons.input.setup ("ntfst-working", IN_ATTRIB | IN_MODIFY | IN_MOVE_SELF | IN_DELETE_SELF); 46 | cons.output.wait (); 47 | 48 | wid = cons.output.added_watch_id (); 49 | should ("watch is added successfully", wid != -1); 50 | 51 | 52 | cons.output.reset (); 53 | cons.input.receive (2); 54 | 55 | system ("touch ntfst-working"); 56 | 57 | cons.output.wait (); 58 | received = cons.output.registered (); 59 | should ("receive IN_ATTRIB on touch", contains (received, event ("", wid, IN_ATTRIB))); 60 | 61 | 62 | cons.output.reset (); 63 | cons.input.receive (); 64 | 65 | system ("echo Hello >> ntfst-working"); 66 | 67 | cons.output.wait (); 68 | received = cons.output.registered (); 69 | should ("receive IN_MOFIFY on write", contains (received, event ("", wid, IN_MODIFY))); 70 | 71 | 72 | cons.output.reset (); 73 | cons.input.receive (); 74 | 75 | system ("mv ntfst-working ntfst-working-2"); 76 | 77 | cons.output.wait (); 78 | received = cons.output.registered (); 79 | should ("receive IN_MOVE_SELF on move", contains (received, event ("", wid, IN_MOVE_SELF))); 80 | 81 | 82 | cons.output.reset (); 83 | cons.input.receive (); 84 | 85 | system ("rm ntfst-working-2"); 86 | 87 | cons.output.wait (); 88 | received = cons.output.registered (); 89 | should ("receive IN_DELETE_SELF on remove", contains (received, event ("", wid, IN_DELETE_SELF))); 90 | should ("receive IN_IGNORED on remove", contains (received, event ("", wid, IN_IGNORED))); 91 | 92 | cons.input.interrupt (); 93 | } 94 | 95 | void notifications_test::cleanup () 96 | { 97 | system ("rm -rf ntfst-working"); 98 | } 99 | -------------------------------------------------------------------------------- /tests/notifications_test.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011-2014 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __NOTIFICATIONS_TEST_HH__ 24 | #define __NOTIFICATIONS_TEST_HH__ 25 | 26 | #include "core/core.hh" 27 | 28 | class notifications_test: public test { 29 | protected: 30 | virtual void setup (); 31 | virtual void run (); 32 | virtual void cleanup (); 33 | 34 | public: 35 | notifications_test (journal &j); 36 | }; 37 | 38 | #endif // __NOTIFICATIONS_TEST_HH__ 39 | -------------------------------------------------------------------------------- /tests/open_close_test.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | 25 | #include "open_close_test.hh" 26 | 27 | open_close_test::open_close_test (journal &j) 28 | : test ("Open/close notifications", j) 29 | { 30 | } 31 | 32 | void open_close_test::setup () 33 | { 34 | cleanup (); 35 | system ("touch oct-file-working"); 36 | system ("echo Hello >> oct-file-working"); 37 | system ("mkdir oct-dir-working"); 38 | } 39 | 40 | void open_close_test::run () 41 | { 42 | consumer cons; 43 | int file_wid = 0; 44 | int dir_wid = 0; 45 | events received; 46 | 47 | cons.input.setup ("oct-file-working", IN_OPEN | IN_CLOSE_WRITE | IN_CLOSE_NOWRITE); 48 | cons.output.wait (); 49 | 50 | file_wid = cons.output.added_watch_id (); 51 | should ("start watching on a file", file_wid != -1); 52 | 53 | 54 | cons.output.reset (); 55 | cons.input.setup ("oct-dir-working", IN_OPEN | IN_CLOSE_WRITE | IN_CLOSE_NOWRITE); 56 | cons.output.wait (); 57 | 58 | dir_wid = cons.output.added_watch_id (); 59 | should ("start watching on a directory", dir_wid != -1); 60 | 61 | cons.output.reset (); 62 | cons.input.receive (); 63 | 64 | system ("cat oct-file-working >> /dev/null"); 65 | 66 | cons.output.wait (); 67 | received = cons.output.registered (); 68 | should ("receive IN_OPEN on cat", 69 | contains (received, event ("", file_wid, IN_OPEN))); 70 | should ("receive IN_CLOSE_NOWRITE on cat", 71 | contains (received, event ("", file_wid, IN_CLOSE_NOWRITE))); 72 | 73 | 74 | cons.output.reset (); 75 | cons.input.receive (); 76 | 77 | system ("ls oct-dir-working >> /dev/null"); 78 | 79 | cons.output.wait (); 80 | received = cons.output.registered (); 81 | should ("receive IN_OPEN on ls", 82 | contains (received, event ("", dir_wid, IN_OPEN))); 83 | should ("receive IN_CLOSE_NOWRITE on ls", 84 | contains (received, event ("", dir_wid, IN_CLOSE_NOWRITE))); 85 | 86 | 87 | cons.output.reset (); 88 | cons.input.receive (); 89 | 90 | system ("echo Hello >> oct-file-working"); 91 | 92 | cons.output.wait (); 93 | received = cons.output.registered (); 94 | should ("receive IN_OPEN on modify", 95 | contains (received, event ("", file_wid, IN_OPEN))); 96 | should ("receive IN_CLOSE_WRITE on modify", 97 | contains (received, event ("", file_wid, IN_CLOSE_WRITE))); 98 | 99 | cons.input.interrupt (); 100 | } 101 | 102 | void open_close_test::cleanup () 103 | { 104 | system ("rm -rf oct-file-working"); 105 | system ("rm -rf oct-dir-working"); 106 | } 107 | -------------------------------------------------------------------------------- /tests/open_close_test.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __OPEN_CLOSE_TEST_HH__ 24 | #define __OPEN_CLOSE_TEST_HH__ 25 | 26 | #include "core/core.hh" 27 | 28 | class open_close_test: public test { 29 | protected: 30 | virtual void setup (); 31 | virtual void run (); 32 | virtual void cleanup (); 33 | 34 | public: 35 | open_close_test (journal &j); 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /tests/start_stop_dir_test.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include "start_stop_dir_test.hh" 25 | 26 | /* Always present on Linux (not sure about a concrete 2.6.x release) 27 | * May be "to be implemented" on BSDs */ 28 | #ifndef IN_IGNORED 29 | # define IN_IGNORED 0x00008000 30 | #endif 31 | 32 | start_stop_dir_test::start_stop_dir_test (journal &j) 33 | : test ("Start-stop directory", j) 34 | { 35 | } 36 | 37 | void start_stop_dir_test::setup () 38 | { 39 | cleanup (); 40 | 41 | system ("mkdir ssdt-working"); 42 | system ("touch ssdt-working/1"); 43 | system ("touch ssdt-working/2"); 44 | system ("touch ssdt-working/3"); 45 | } 46 | 47 | void start_stop_dir_test::run () 48 | { 49 | consumer cons; 50 | events received; 51 | int wid = 0; 52 | 53 | /* Add a watch */ 54 | cons.input.setup ("ssdt-working", IN_ATTRIB); 55 | cons.output.wait (); 56 | 57 | wid = cons.output.added_watch_id (); 58 | should ("watch is added successfully", wid != -1); 59 | 60 | /* Tell consumer to watch for an IN_ATTRIB event */ 61 | cons.output.reset (); 62 | cons.input.receive (); 63 | 64 | /* Produce activity */ 65 | system ("touch ssdt-working"); 66 | 67 | cons.output.wait (); 68 | received = cons.output.registered (); 69 | should ("events are registered on a directory", 70 | contains (received, event ("", wid, IN_ATTRIB))); 71 | 72 | /* Watch should also signal us about activity on files at the watched directory. */ 73 | cons.output.reset (); 74 | cons.input.receive (1); 75 | 76 | /* This events should be registered */ 77 | system ("touch ssdt-working/1"); 78 | system ("touch ssdt-working/2"); 79 | system ("touch ssdt-working/3"); 80 | 81 | cons.output.wait (); 82 | received = cons.output.registered (); 83 | should ("events are registered on the directory contents", 84 | contains (received, event ("1", wid, IN_ATTRIB)) 85 | && contains (received, event ("2", wid, IN_ATTRIB)) 86 | && contains (received, event ("3", wid, IN_ATTRIB))); 87 | 88 | /* Now stop watching */ 89 | cons.output.reset (); 90 | cons.input.setup (wid); 91 | cons.output.wait (); 92 | 93 | /* Linux inotify sends IN_IGNORED on stop */ 94 | cons.output.reset (); 95 | cons.input.receive (); 96 | 97 | cons.output.wait (); 98 | received = cons.output.registered (); 99 | should ("got IN_IGNORED on watch stop", 100 | contains (received, event ("", wid, IN_IGNORED))); 101 | 102 | /* These events should not be visible to watch */ 103 | cons.output.reset (); 104 | cons.input.receive (); 105 | 106 | system ("touch ssdt-working"); 107 | system ("touch ssdt-working/2"); 108 | 109 | cons.output.wait (); 110 | received = cons.output.registered (); 111 | should ("items on a stopped watch are unregistered", received.empty ()); 112 | 113 | /* Now resume watching */ 114 | cons.output.reset (); 115 | cons.input.setup ("ssdt-working", IN_ATTRIB); 116 | 117 | cons.output.wait (); 118 | wid = cons.output.added_watch_id (); 119 | should ("watch is added successfully again", wid != -1); 120 | 121 | cons.output.reset (); 122 | cons.input.receive (); 123 | 124 | system ("touch ssdt-working"); 125 | system ("touch ssdt-working/1"); 126 | system ("touch ssdt-working/2"); 127 | system ("touch ssdt-working/3"); 128 | 129 | cons.output.wait (); 130 | received = cons.output.registered (); 131 | 132 | should ("receive all events on a resumed watch", 133 | contains (received, (event ("", wid, IN_ATTRIB))) 134 | && contains (received, (event ("1", wid, IN_ATTRIB))) 135 | && contains (received, (event ("2", wid, IN_ATTRIB))) 136 | && contains (received, (event ("3", wid, IN_ATTRIB)))); 137 | 138 | /* Now, start watching on a file from a directory manually */ 139 | int child_wid = 0; 140 | 141 | cons.output.reset (); 142 | cons.input.setup ("ssdt-working/3", IN_ATTRIB); 143 | 144 | cons.output.wait (); 145 | child_wid = cons.output.added_watch_id (); 146 | should ("watch on a file in a directory is added successfully", wid != -1); 147 | 148 | /* On a single touch, should recive two events */ 149 | cons.output.reset (); 150 | cons.input.receive (); 151 | 152 | system ("touch ssdt-working/3"); 153 | 154 | cons.output.wait (); 155 | received = cons.output.registered (); 156 | should ("receive events for a same file from both watches " 157 | "(sometimes this test fails event on Linux, at least on 2.6.39)", 158 | contains (received, event ("3", wid, IN_ATTRIB)) 159 | && contains (received, event ("", child_wid, IN_ATTRIB))); 160 | 161 | /* Now stop a directory watch */ 162 | cons.output.reset (); 163 | cons.input.setup (wid); 164 | cons.output.wait (); 165 | 166 | /* Linux inotify sends IN_IGNORED on stop */ 167 | cons.output.reset (); 168 | cons.input.receive (); 169 | cons.output.wait (); 170 | 171 | /* Still should be able to receive notifications from an additional watch */ 172 | cons.output.reset (); 173 | cons.input.receive (); 174 | 175 | system ("touch ssdt-working/3"); 176 | 177 | cons.output.wait (); 178 | received = cons.output.registered (); 179 | should ("after stop on a directory watch, " 180 | "receive only a single event from a file watch", 181 | contains (received, event ("", child_wid, IN_ATTRIB))); 182 | 183 | /* IN_ONESHOT flag tests */ 184 | cons.input.setup ("ssdt-working", IN_CREATE | IN_DELETE | IN_ONESHOT); 185 | cons.output.wait (); 186 | wid = cons.output.added_watch_id (); 187 | 188 | cons.output.reset (); 189 | cons.input.receive (); 190 | 191 | system ("touch ssdt-working/4"); 192 | 193 | cons.output.wait (); 194 | received = cons.output.registered (); 195 | should ("receive IN_CREATE for ssdt_working on touch", 196 | contains (received, event ("4", wid, IN_CREATE))); 197 | should ("receive IN_IGNORED after one event have been received " 198 | "if watch was opened with IN_ONESHOT flag set", 199 | contains (received, event ("", wid, IN_IGNORED))); 200 | 201 | cons.output.reset (); 202 | cons.input.receive (); 203 | 204 | system ("rm ssdt-working/4"); 205 | 206 | cons.output.wait (); 207 | received = cons.output.registered (); 208 | should ("Stop receiving events after one event have been received " 209 | "if watch was opened with IN_ONESHOT flag set", 210 | !contains (received, event ("4", wid, IN_DELETE))); 211 | 212 | cons.input.interrupt (); 213 | } 214 | 215 | void start_stop_dir_test::cleanup () 216 | { 217 | system ("rm -rf ssdt-working"); 218 | } 219 | -------------------------------------------------------------------------------- /tests/start_stop_dir_test.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __START_STOP_DIR_TEST_HH__ 24 | #define __START_STOP_DIR_TEST_HH__ 25 | 26 | #include "core/core.hh" 27 | 28 | class start_stop_dir_test: public test { 29 | protected: 30 | virtual void setup (); 31 | virtual void run (); 32 | virtual void cleanup (); 33 | 34 | public: 35 | start_stop_dir_test (journal &j); 36 | }; 37 | 38 | #endif // __START_STOP_DIR_TEST_HH__ 39 | -------------------------------------------------------------------------------- /tests/start_stop_test.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include "start_stop_test.hh" 25 | 26 | /* Always present on Linux (not sure about a concrete 2.6.x release) 27 | * May be "to be implemented" on BSDs */ 28 | #ifndef IN_IGNORED 29 | # define IN_IGNORED 0x00008000 30 | #endif 31 | 32 | start_stop_test::start_stop_test (journal &j) 33 | : test ("Start-stop test", j) 34 | { 35 | } 36 | 37 | void start_stop_test::setup () 38 | { 39 | cleanup (); 40 | system ("touch sst-working"); 41 | system ("ln sst-working sst-working2"); 42 | system ("ln -s sst-working sst-working3"); 43 | } 44 | 45 | void start_stop_test::run () 46 | { 47 | consumer cons; 48 | int wid = 0; 49 | int wid2 = 0; 50 | events received; 51 | 52 | /* Add a watch */ 53 | cons.input.setup ("sst-working", IN_ATTRIB); 54 | cons.output.wait (); 55 | 56 | wid = cons.output.added_watch_id (); 57 | should ("watch is added successfully", wid != -1); 58 | 59 | /* Tell consumer to watch for an IN_ATTRIB event */ 60 | cons.output.reset (); 61 | cons.input.receive (); 62 | 63 | system ("touch sst-working"); 64 | 65 | cons.output.wait (); 66 | received = cons.output.registered (); 67 | should ("all produced events are registered", 68 | contains (received, event ("", wid, IN_ATTRIB))); 69 | 70 | /* Now stop watching */ 71 | cons.output.reset (); 72 | cons.input.setup (wid); 73 | cons.output.wait (); 74 | 75 | /* Linux inotify sends IN_IGNORED on stop */ 76 | cons.output.reset (); 77 | cons.input.receive (); 78 | cons.output.wait (); 79 | 80 | received = cons.output.registered (); 81 | should ("got IN_IGNORED on watch stop", 82 | contains (received, event ("", wid, IN_IGNORED))); 83 | 84 | /* Tell again to consumer to watch for an IN_ATTRIB event */ 85 | cons.output.reset (); 86 | cons.input.receive (); 87 | 88 | system ("touch sst-working"); 89 | 90 | cons.output.wait (); 91 | received = cons.output.registered (); 92 | should ("events should not be registered on a removed watch", 93 | received.size() == 0); 94 | 95 | /* Now start watching again. Everything should work */ 96 | cons.output.reset (); 97 | cons.input.setup ("sst-working", IN_ATTRIB); 98 | cons.output.wait (); 99 | 100 | wid = cons.output.added_watch_id (); 101 | should ("start watching a file after stop", wid != -1); 102 | 103 | /* Tell consumer to watch for an IN_ATTRIB event */ 104 | cons.output.reset (); 105 | cons.input.receive (); 106 | 107 | /* Produce activity, consumer should watch it */ 108 | system ("touch sst-working"); 109 | 110 | cons.output.wait (); 111 | received = cons.output.registered (); 112 | should ("all produced events are registered after resume", 113 | contains (received, event ("", wid, IN_ATTRIB))); 114 | 115 | cons.output.reset (); 116 | cons.input.setup ("sst-working2", IN_ATTRIB); 117 | cons.output.wait (); 118 | 119 | wid2 = cons.output.added_watch_id (); 120 | should ("pair of hardlinked files should be opened with the same watch ID", wid == wid2); 121 | 122 | cons.output.reset (); 123 | cons.input.setup ("sst-working3", IN_ATTRIB); 124 | cons.output.wait (); 125 | 126 | wid2 = cons.output.added_watch_id (); 127 | if (should ("watch on file is added successfully via softlink", wid2 != -1)) { 128 | should ("pair of softlinked files should be opened with the same watch ID", wid == wid2); 129 | } 130 | 131 | /* IN_ONESHOT flag tests */ 132 | cons.input.setup ("sst-working", IN_ATTRIB | IN_ONESHOT); 133 | cons.output.wait (); 134 | wid = cons.output.added_watch_id (); 135 | 136 | cons.output.reset (); 137 | cons.input.receive (); 138 | 139 | system ("touch sst-working"); 140 | 141 | cons.output.wait (); 142 | received = cons.output.registered (); 143 | should ("receive IN_ATTRIB for sst_working on touch", 144 | contains (received, event ("", wid, IN_ATTRIB))); 145 | should ("receive IN_IGNORED after one event have been received " 146 | "if watch was opened with IN_ONESHOT flag set", 147 | contains (received, event ("", wid, IN_IGNORED))); 148 | 149 | cons.output.reset (); 150 | cons.input.receive (); 151 | 152 | system ("touch sst-working"); 153 | 154 | cons.output.wait (); 155 | received = cons.output.registered (); 156 | should ("Stop receiving events after one event have been received " 157 | "if watch was opened with IN_ONESHOT flag set", 158 | !contains (received, event ("", wid, IN_ATTRIB))); 159 | 160 | cons.input.interrupt (); 161 | } 162 | 163 | void start_stop_test::cleanup () 164 | { 165 | system ("rm -rf sst-working"); 166 | system ("rm -rf sst-working2"); 167 | system ("rm -rf sst-working3"); 168 | } 169 | -------------------------------------------------------------------------------- /tests/start_stop_test.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __START_STOP_TEST_HH__ 24 | #define __START_STOP_TEST_HH__ 25 | 26 | #include "core/core.hh" 27 | 28 | class start_stop_test: public test { 29 | protected: 30 | virtual void setup (); 31 | virtual void run (); 32 | virtual void cleanup (); 33 | 34 | public: 35 | start_stop_test (journal &j); 36 | }; 37 | 38 | #endif // __START_STOP_TEST_HH__ 39 | -------------------------------------------------------------------------------- /tests/symlink_test.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2014 Dmitry Matveev 3 | Copyright (c) 2014 Vladimir Kondratiev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | *******************************************************************************/ 23 | 24 | #include 25 | 26 | #include 27 | #include "symlink_test.hh" 28 | 29 | symlink_test::symlink_test (journal &j) 30 | : test ("Symbolic links", j) 31 | { 32 | } 33 | 34 | void symlink_test::setup () 35 | { 36 | cleanup (); 37 | system ("mkdir slt-wd1"); 38 | system ("touch slt-wd1/foo"); 39 | system ("mkdir slt-wd2"); 40 | system ("mkdir slt-wd3"); 41 | system ("ln -s ../slt-wd1/foo slt-wd3/bar"); 42 | system ("ln -s ../slt-wd1/foo slt-wd3/baz"); 43 | } 44 | 45 | void symlink_test::run () 46 | { 47 | /* Issue #10 - do not reflect changes in files under watched symlinks */ 48 | { consumer cons; 49 | events received; 50 | events::iterator iter; 51 | int wid = 0; 52 | 53 | cons.input.setup ("slt-wd2", 54 | IN_ATTRIB | IN_MODIFY 55 | | IN_CREATE | IN_DELETE 56 | | IN_MOVED_FROM | IN_MOVED_TO 57 | | IN_MOVE_SELF | IN_DELETE_SELF); 58 | cons.output.wait (); 59 | wid = cons.output.added_watch_id (); 60 | 61 | cons.output.reset (); 62 | cons.input.receive (); 63 | 64 | system ("ln -s ../slt-wd1/foo slt-wd2/bar"); 65 | 66 | cons.output.wait (); 67 | received = cons.output.registered (); 68 | should ("receive IN_CREATE for slt-wd2/bar", 69 | contains (received, event ("bar", wid, IN_CREATE))); 70 | 71 | 72 | cons.output.reset (); 73 | cons.input.receive (); 74 | 75 | system ("touch slt-wd2/bar"); 76 | 77 | cons.output.wait (); 78 | received = cons.output.registered (); 79 | should ("No IN_ATTRIB after touching symlink", received.empty()); 80 | 81 | 82 | cons.output.reset (); 83 | cons.input.receive (); 84 | 85 | system ("touch slt-wd1/foo"); 86 | 87 | cons.output.wait (); 88 | received = cons.output.registered (); 89 | should ("No IN_ATTRIB after touching symlink source file", received.empty()); 90 | 91 | 92 | cons.output.reset (); 93 | cons.input.receive (); 94 | 95 | system ("echo hello >> slt-wd2/bar"); 96 | 97 | cons.output.wait (); 98 | received = cons.output.registered (); 99 | should ("No IN_MODIFY after modifying a file via symlink", received.empty()); 100 | 101 | 102 | cons.output.reset (); 103 | cons.input.receive (); 104 | 105 | system ("echo hello >> slt-wd1/foo"); 106 | 107 | cons.output.wait (); 108 | received = cons.output.registered (); 109 | should ("No IN_MODIFY after modifying symlink source file", received.empty()); 110 | 111 | 112 | cons.output.reset (); 113 | cons.input.receive (); 114 | 115 | system ("rm slt-wd2/bar"); 116 | 117 | cons.output.wait (); 118 | received = cons.output.registered (); 119 | should ("Receive IN_DELETE on removing a symlink from the watched directory", 120 | contains (received, event ("bar", wid, IN_DELETE))); 121 | 122 | cons.input.interrupt (); 123 | } 124 | /* For the direct user watches (not dependencies), symlinks should work as usual */ 125 | { consumer cons; 126 | events received; 127 | events::iterator iter; 128 | int wid = 0; 129 | 130 | cons.input.setup ("slt-wd3/bar", 131 | IN_ATTRIB | IN_MODIFY 132 | | IN_CREATE | IN_DELETE 133 | | IN_MOVED_FROM | IN_MOVED_TO 134 | | IN_MOVE_SELF | IN_DELETE_SELF); 135 | cons.output.wait (); 136 | wid = cons.output.added_watch_id (); 137 | 138 | cons.output.reset (); 139 | cons.input.receive (); 140 | 141 | system ("touch slt-wd3/bar"); 142 | 143 | cons.output.wait (); 144 | received = cons.output.registered (); 145 | should ("Receive IN_ATTRIB after touching symlink", 146 | contains (received, event ("", wid, IN_ATTRIB))); 147 | 148 | 149 | cons.output.reset (); 150 | cons.input.receive (); 151 | 152 | system ("touch slt-wd1/foo"); 153 | 154 | cons.output.wait (); 155 | received = cons.output.registered (); 156 | should ("Receive IN_ATTRIB after touching symlink source file", 157 | contains (received, event ("", wid, IN_ATTRIB))); 158 | 159 | 160 | cons.output.reset (); 161 | cons.input.receive (); 162 | 163 | system ("echo hello >> slt-wd3/bar"); 164 | 165 | cons.output.wait (); 166 | received = cons.output.registered (); 167 | should ("Receive IN_MODIFY after modifying a file via symlink", 168 | contains (received, event ("", wid, IN_MODIFY))); 169 | 170 | 171 | cons.output.reset (); 172 | cons.input.receive (); 173 | 174 | system ("echo hello >> slt-wd1/foo"); 175 | 176 | cons.output.wait (); 177 | received = cons.output.registered (); 178 | should ("Receive IN_MODIFY after modifying symlink source file", 179 | contains (received, event ("", wid, IN_MODIFY))); 180 | 181 | 182 | /* For me, this behavior is a bit weird, but it is still what we see in Linux: */ 183 | cons.output.reset (); 184 | cons.input.receive (); 185 | 186 | system ("rm slt-wd3/bar"); 187 | 188 | cons.output.wait (); 189 | received = cons.output.registered (); 190 | should ("No IN_DELETE_SELF on removing a symlink", received.empty()); 191 | 192 | cons.input.interrupt (); 193 | } 194 | /* Test IN_DONT_FOLLOW */ 195 | { consumer cons; 196 | events received; 197 | events::iterator iter; 198 | int wid = 0; 199 | 200 | cons.input.setup ("slt-wd3/baz", 201 | IN_ATTRIB | IN_MODIFY 202 | | IN_CREATE | IN_DELETE 203 | | IN_MOVED_FROM | IN_MOVED_TO 204 | | IN_MOVE_SELF | IN_DELETE_SELF 205 | | IN_DONT_FOLLOW); 206 | cons.output.wait (); 207 | wid = cons.output.added_watch_id (); 208 | should ("Start watch successfully on a symlink file with IN_DONT_FOLLOW", 209 | wid != -1); 210 | 211 | cons.output.reset (); 212 | cons.input.receive (); 213 | 214 | lchown ("slt-wd3/baz", getuid(), -1); 215 | 216 | cons.output.wait (); 217 | received = cons.output.registered (); 218 | should ("Receive IN_ATTRIB after touching symlink itself", 219 | contains (received, event ("", wid, IN_ATTRIB))); 220 | 221 | 222 | cons.output.reset (); 223 | cons.input.receive (); 224 | 225 | system ("echo hello >> slt-wd1/foo"); 226 | 227 | cons.output.wait (); 228 | received = cons.output.registered (); 229 | should ("No IN_MODIFY after modifying symlink source file", 230 | !contains (received, event ("", wid, IN_MODIFY))); 231 | 232 | 233 | cons.output.reset (); 234 | cons.input.receive (); 235 | 236 | system ("echo hello >> slt-wd3/baz"); 237 | 238 | cons.output.wait (); 239 | received = cons.output.registered (); 240 | should ("No IN_MODIFY after modifying file via symlink", 241 | !contains (received, event ("", wid, IN_MODIFY))); 242 | 243 | 244 | cons.output.reset (); 245 | cons.input.receive (); 246 | 247 | system ("mv slt-wd3/baz slt-wd3/bazz"); 248 | 249 | cons.output.wait (); 250 | received = cons.output.registered (); 251 | should ("Receive IN_MOVE_SELF after moving the symlink", 252 | contains (received, event ("", wid, IN_MOVE_SELF))); 253 | 254 | 255 | cons.output.reset (); 256 | cons.input.receive (); 257 | 258 | system ("rm slt-wd3/bazz"); 259 | 260 | cons.output.wait (); 261 | received = cons.output.registered (); 262 | should ("Receive IN_DELETE_SELF after removing the symlink", 263 | contains (received, event ("", wid, IN_DELETE_SELF))); 264 | 265 | cons.input.interrupt (); 266 | } 267 | } 268 | 269 | void symlink_test::cleanup () 270 | { 271 | system ("rm -rf slt-wd1"); 272 | system ("rm -rf slt-wd2"); 273 | system ("rm -rf slt-wd3"); 274 | } 275 | -------------------------------------------------------------------------------- /tests/symlink_test.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2014 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __SYMLINK_TEST__HH__ 24 | #define __SYMLINK_TEST__HH__ 25 | 26 | #include "core/core.hh" 27 | 28 | class symlink_test: public test { 29 | protected: 30 | virtual void setup (); 31 | virtual void run (); 32 | virtual void cleanup (); 33 | 34 | public: 35 | symlink_test (journal &j); 36 | }; 37 | 38 | #endif // __SYMLINK_TEST_HH__ 39 | -------------------------------------------------------------------------------- /tests/tests.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011-2014 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include "start_stop_test.hh" 25 | #include "start_stop_dir_test.hh" 26 | #include "notifications_test.hh" 27 | #include "notifications_dir_test.hh" 28 | #include "fail_test.hh" 29 | #include "update_flags_test.hh" 30 | #include "update_flags_dir_test.hh" 31 | #include "open_close_test.hh" 32 | #include "symlink_test.hh" 33 | #include "bugs_test.hh" 34 | 35 | #define CONCURRENT 36 | 37 | int main (int argc, char *argv[]) { 38 | journal j; 39 | 40 | test *tests[] = { 41 | new start_stop_test (j), 42 | new start_stop_dir_test (j), 43 | new notifications_test (j), 44 | new notifications_dir_test (j), 45 | new update_flags_test (j), 46 | new update_flags_dir_test (j), 47 | new open_close_test (j), 48 | new symlink_test (j), 49 | new fail_test (j), 50 | new bugs_test (j), 51 | }; 52 | const int num_tests = sizeof(tests)/sizeof(tests[0]); 53 | 54 | #ifdef CONCURRENT 55 | for (int i = 0; i < num_tests; i++) { 56 | tests[i]->start (); 57 | } 58 | for (int i = 0; i < num_tests; i++) { 59 | tests[i]->wait_for_end (); 60 | } 61 | #else 62 | for (int i = 0; i < num_tests; i++) { 63 | tests[i]->start (); 64 | tests[i]->wait_for_end (); 65 | } 66 | #endif 67 | 68 | j.summarize (); 69 | 70 | for (int i = 0; i < num_tests; i++) { 71 | delete tests[i]; 72 | } 73 | 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /tests/update_flags_dir_test.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | 25 | #include "update_flags_dir_test.hh" 26 | 27 | update_flags_dir_test::update_flags_dir_test (journal &j) 28 | : test ("Update directory flags", j) 29 | { 30 | } 31 | 32 | void update_flags_dir_test::setup () 33 | { 34 | cleanup (); 35 | system ("mkdir ufdt-working"); 36 | system ("touch ufdt-working/1"); 37 | } 38 | 39 | void update_flags_dir_test::run () 40 | { 41 | consumer cons; 42 | int wid = 0; 43 | int new_wid = 0; 44 | events received; 45 | 46 | /* Add a watch */ 47 | cons.input.setup ("ufdt-working", IN_ATTRIB); 48 | cons.output.wait (); 49 | 50 | wid = cons.output.added_watch_id (); 51 | should ("start watching successfully", wid != -1); 52 | 53 | 54 | cons.output.reset (); 55 | cons.input.receive (); 56 | 57 | system ("touch ufdt-working/1"); 58 | 59 | cons.output.wait (); 60 | received = cons.output.registered (); 61 | 62 | should ("receive touch notifications for files in a directory", 63 | contains (received, event ("1", wid, IN_ATTRIB))); 64 | 65 | 66 | cons.output.reset (); 67 | cons.input.receive (); 68 | 69 | system ("echo Hello >> ufdt-working/1"); 70 | 71 | cons.output.wait (); 72 | received = cons.output.registered (); 73 | 74 | should ("do not receive modify notifications for files in a directory without IN_MODIFY", 75 | received.empty ()); 76 | 77 | 78 | cons.output.reset (); 79 | cons.input.setup ("ufdt-working", IN_ATTRIB | IN_MODIFY); 80 | cons.output.wait (); 81 | 82 | new_wid = cons.output.added_watch_id (); 83 | should ("update flags successfully", wid == new_wid); 84 | 85 | 86 | cons.output.reset (); 87 | cons.input.receive (); 88 | 89 | system ("echo Hello >> ufdt-working/1"); 90 | 91 | cons.output.wait (); 92 | received = cons.output.registered (); 93 | 94 | should ("receive modify notifications for files in a directory with IN_MODIFY", 95 | contains (received, event ("1", wid, IN_MODIFY))); 96 | 97 | cons.input.interrupt (); 98 | } 99 | 100 | void update_flags_dir_test::cleanup () 101 | { 102 | system ("rm -rf ufdt-working"); 103 | } 104 | -------------------------------------------------------------------------------- /tests/update_flags_dir_test.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __UPDATE_FLAGS_DIR_TEST_HH__ 24 | #define __UPDATE_FLAGS_DIR_TEST_HH__ 25 | 26 | #include "core/core.hh" 27 | 28 | class update_flags_dir_test: public test { 29 | protected: 30 | virtual void setup (); 31 | virtual void run (); 32 | virtual void cleanup (); 33 | 34 | public: 35 | update_flags_dir_test (journal &j); 36 | }; 37 | 38 | #endif // __UPDATE_FLAGS_DIR_TEST_HH__ 39 | -------------------------------------------------------------------------------- /tests/update_flags_test.cc: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | 25 | #include "update_flags_test.hh" 26 | 27 | update_flags_test::update_flags_test (journal &j) 28 | : test ("Update watch flags", j) 29 | { 30 | } 31 | 32 | void update_flags_test::setup () 33 | { 34 | cleanup (); 35 | system ("touch uft-working"); 36 | } 37 | 38 | void update_flags_test::run () 39 | { 40 | consumer cons; 41 | int wid = 0; 42 | int updated_wid = 0; 43 | events received; 44 | 45 | /* Add a watch */ 46 | cons.input.setup ("uft-working", IN_ATTRIB); 47 | cons.output.wait (); 48 | 49 | wid = cons.output.added_watch_id (); 50 | should ("start watching successfully", wid != -1); 51 | 52 | 53 | cons.output.reset (); 54 | cons.input.receive (); 55 | 56 | system ("touch uft-working"); 57 | 58 | cons.output.wait (); 59 | received = cons.output.registered (); 60 | should ("receive notifications on touch", 61 | contains (received, event ("", wid, IN_ATTRIB))); 62 | 63 | 64 | cons.output.reset (); 65 | cons.input.receive (); 66 | 67 | system ("echo Hello >> uft-working"); 68 | 69 | cons.output.wait (); 70 | received = cons.output.registered (); 71 | should ("do not receive notifications on modify with flags = IN_ATTRIB", 72 | received.empty()); 73 | 74 | 75 | cons.output.reset (); 76 | cons.input.setup ("uft-working", IN_ATTRIB | IN_MODIFY); 77 | cons.output.wait (); 78 | 79 | updated_wid = cons.output.added_watch_id (); 80 | should ("modify flags updated successfully", wid == updated_wid); 81 | 82 | 83 | cons.output.reset (); 84 | cons.input.receive (); 85 | 86 | system ("echo Hello >> uft-working"); 87 | 88 | cons.output.wait (); 89 | received = cons.output.registered (); 90 | should ("receive notifications on modify with flags = IN_ATTRIB | IN_MODIFY", 91 | contains (received, event ("", wid, IN_MODIFY))); 92 | 93 | 94 | cons.output.reset (); 95 | cons.input.receive (); 96 | 97 | system ("touch uft-working"); 98 | 99 | cons.output.wait (); 100 | received = cons.output.registered (); 101 | should ("receive notifications on touch with flags = IN_ATTRIB | IN_MODIFY ", 102 | contains (received, event ("", wid, IN_ATTRIB))); 103 | 104 | 105 | cons.output.reset (); 106 | cons.input.setup ("uft-working", IN_MODIFY); 107 | cons.output.wait (); 108 | updated_wid = cons.output.added_watch_id (); 109 | should ("modify flags updated successfully, again", wid == updated_wid); 110 | 111 | 112 | cons.output.reset (); 113 | cons.input.receive (); 114 | 115 | system ("touch uft-working"); 116 | 117 | cons.output.wait (); 118 | received = cons.output.registered (); 119 | should ("do not receive notifications on touch with flags = IN_MODIFY ", 120 | received.empty()); 121 | 122 | 123 | cons.output.reset (); 124 | cons.input.setup ("uft-working", IN_ATTRIB | IN_MASK_ADD); 125 | cons.output.wait (); 126 | updated_wid = cons.output.added_watch_id (); 127 | should ("modify flags updated successfully, again", wid == updated_wid); 128 | 129 | 130 | cons.output.reset (); 131 | cons.input.receive (); 132 | 133 | system ("echo Hello >> uft-working"); 134 | 135 | cons.output.wait (); 136 | received = cons.output.registered (); 137 | should ("receive notifications on modify after watch with IN_MODIFY flag " 138 | "set has been updated with IN_MASK_ADD set and IN_MODIFY unset", 139 | contains (received, event ("", updated_wid, IN_MODIFY))); 140 | 141 | cons.input.interrupt (); 142 | } 143 | 144 | void update_flags_test::cleanup () 145 | { 146 | system ("rm -rf uft-working"); 147 | } 148 | -------------------------------------------------------------------------------- /tests/update_flags_test.hh: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __UPDATE_FLAGS_HH__ 24 | #define __UPDATE_FLAGS_HH__ 25 | 26 | #include "core/core.hh" 27 | 28 | class update_flags_test: public test { 29 | protected: 30 | virtual void setup (); 31 | virtual void run (); 32 | virtual void cleanup (); 33 | 34 | public: 35 | update_flags_test (journal &j); 36 | }; 37 | 38 | #endif // __UPDATE_FLAGS_HH__ 39 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __UTILS_H__ 24 | #define __UTILS_H__ 25 | 26 | #include "config.h" 27 | #include "compat.h" 28 | 29 | #include /* S_ISDIR */ 30 | #include /* iovec */ 31 | 32 | #include /* errno */ 33 | #include /* fprintf */ 34 | #include /* strerror */ 35 | #include 36 | 37 | /** 38 | * Print an error message, if allowed. 39 | * 40 | * Print a file name, line number and errno-based error description as well. 41 | * The library should be built with --enable-perrors configure option. 42 | * 43 | * @param[in] msg A message format to print. 44 | * @param[in] ... A set of parameters to include in the message, according 45 | * to the format string. 46 | **/ 47 | #ifdef ENABLE_PERRORS 48 | #define perror_msg(msg, ...) \ 49 | do { \ 50 | int saved_errno_ = errno; \ 51 | fprintf (stderr, "%s.%d: " msg ": %d (%s)\n", __FILE__, __LINE__, \ 52 | ##__VA_ARGS__, errno, strerror (errno)); \ 53 | errno = saved_errno_; \ 54 | } while (0) 55 | #else 56 | #define perror_msg(msg, ...) 57 | #endif 58 | 59 | struct inotify_event* create_inotify_event (int wd, 60 | uint32_t mask, 61 | uint32_t cookie, 62 | const char *name, 63 | size_t *event_len); 64 | 65 | ssize_t safe_read (int fd, void *data, size_t size); 66 | ssize_t safe_write (int fd, const void *data, size_t size); 67 | ssize_t safe_writev (int fd, const struct iovec iov[], int iovcnt); 68 | 69 | int is_opened (int fd); 70 | int is_deleted (int fd); 71 | int set_cloexec_flag (int fd, int value); 72 | int set_nonblock_flag (int fd, int value); 73 | int dup_cloexec (int oldd); 74 | 75 | #endif /* __UTILS_H__ */ 76 | -------------------------------------------------------------------------------- /watch-set.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include "compat.h" 24 | 25 | #include 26 | #include /* NULL */ 27 | #include 28 | #include /* ino_t */ 29 | 30 | #include "watch-set.h" 31 | #include "watch.h" 32 | 33 | /** 34 | * Initialize the watch set. 35 | * 36 | * @param[in] ws A pointer to the watch set. 37 | **/ 38 | void 39 | watch_set_init (watch_set *ws) 40 | { 41 | assert (ws != NULL); 42 | 43 | RB_INIT (ws); 44 | } 45 | 46 | /** 47 | * Free the memory allocated for the watch set. 48 | * 49 | * @param[in] ws A pointer the the watch set. 50 | **/ 51 | void 52 | watch_set_free (watch_set *ws) 53 | { 54 | assert (ws != NULL); 55 | 56 | watch *w, *tmp; 57 | 58 | RB_FOREACH_SAFE (w, watch_set, ws, tmp) { 59 | watch_set_delete (ws, w); 60 | } 61 | } 62 | 63 | /** 64 | * Remove a watch from watch set. 65 | * 66 | * @param[in] ws A pointer to the watch set. 67 | * @param[in] w A pointer to watch to remove. 68 | **/ 69 | void 70 | watch_set_delete (watch_set *ws, watch *w) 71 | { 72 | assert (ws != NULL); 73 | assert (w != NULL); 74 | 75 | RB_REMOVE (watch_set, ws, w); 76 | watch_free (w); 77 | } 78 | 79 | /** 80 | * Insert watch into watch set. 81 | * 82 | * @param[in] ws A pointer to #watch_set. 83 | * @param[in] w A pointer to inserted watch. 84 | **/ 85 | void 86 | watch_set_insert (watch_set *ws, watch *w) 87 | { 88 | assert (ws != NULL); 89 | assert (w != NULL); 90 | 91 | RB_INSERT (watch_set, ws, w); 92 | } 93 | 94 | /** 95 | * Find kqueue watch corresponding for dependency item 96 | * 97 | * @param[in] ws A pointer to #watch_set. 98 | * @param[in] inode A inode number of watch 99 | * @return A pointer to kqueue watch if found NULL otherwise 100 | **/ 101 | watch * 102 | watch_set_find (watch_set *ws, ino_t inode) 103 | { 104 | assert (ws != NULL); 105 | 106 | watch find = { .inode = inode }; 107 | return RB_FIND (watch_set, ws, &find); 108 | } 109 | /** 110 | * Custom comparison function that can compare kqueue watch inode values 111 | * through pointers passed by RB tree functions 112 | * 113 | * @param[in] w1 A pointer to a first kqueue watch to compare 114 | * @param[in] w2 A pointer to a second kqueue watch to compare 115 | * @return An -1, 0, or +1 if the first watch is considered to be respectively 116 | * less than, equal to, or greater than the second one. 117 | **/ 118 | static int 119 | watch_set_cmp (watch *w1, watch *w2) 120 | { 121 | return ((w1->inode > w2->inode) - (w1->inode < w2->inode)); 122 | } 123 | 124 | RB_GENERATE(watch_set, watch, link, watch_set_cmp); 125 | -------------------------------------------------------------------------------- /watch-set.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __WATCH_SET_H__ 24 | #define __WATCH_SET_H__ 25 | 26 | #include "compat.h" 27 | 28 | #include /* size_t */ 29 | #include /* ino_t */ 30 | 31 | typedef RB_HEAD(watch_set, watch) watch_set; 32 | 33 | #include "watch.h" 34 | 35 | void watch_set_init (watch_set *ws); 36 | void watch_set_free (watch_set *ws); 37 | void watch_set_delete (watch_set *ws, watch *w); 38 | void watch_set_insert (watch_set *ws, watch *w); 39 | watch *watch_set_find (watch_set *ws, ino_t inode); 40 | 41 | RB_PROTOTYPE(watch_set, watch, link, watch_cmp); 42 | 43 | 44 | #endif /* __WATCH_SET_H__ */ 45 | -------------------------------------------------------------------------------- /watch.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011-2014 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include "compat.h" 24 | 25 | #include /* errno */ 26 | #include /* open */ 27 | #include /* close */ 28 | #include /* strdup */ 29 | #include /* free */ 30 | #include 31 | 32 | #include 33 | #include /* kevent */ 34 | #include /* stat */ 35 | #include /* snprintf */ 36 | 37 | #include "utils.h" 38 | #include "watch.h" 39 | #include "sys/inotify.h" 40 | 41 | /** 42 | * Convert the inotify watch mask to the kqueue event filter flags. 43 | * 44 | * @param[in] flags An inotify watch mask. 45 | * @param[in] wf A kqueue watch internal flags. 46 | * @return Converted kqueue event filter flags. 47 | **/ 48 | uint32_t 49 | inotify_to_kqueue (uint32_t flags, watch_flags_t wf) 50 | { 51 | uint32_t result = 0; 52 | 53 | if (!(S_ISREG (wf) || S_ISDIR (wf) || S_ISLNK (wf))) { 54 | return result; 55 | } 56 | 57 | #ifdef NOTE_OPEN 58 | if (flags & IN_OPEN) 59 | result |= NOTE_OPEN; 60 | #endif 61 | #ifdef NOTE_CLOSE 62 | if (flags & IN_CLOSE_NOWRITE) 63 | result |= NOTE_CLOSE; 64 | if (flags & IN_CLOSE_WRITE && S_ISREG (wf)) 65 | result |= (NOTE_CLOSE | NOTE_WRITE); 66 | #endif 67 | #ifdef NOTE_READ 68 | if (flags & IN_ACCESS && S_ISREG (wf)) 69 | result |= NOTE_READ; 70 | #endif 71 | if (flags & IN_ATTRIB) 72 | result |= NOTE_ATTRIB; 73 | if (flags & IN_MODIFY && S_ISREG (wf)) 74 | result |= NOTE_WRITE; 75 | if (!(wf & WF_ISSUBWATCH)) { 76 | if (S_ISDIR (wf)) { 77 | result |= NOTE_WRITE; 78 | #ifdef HAVE_NOTE_EXTEND_ON_SUBFILE_RENAME 79 | result |= NOTE_EXTEND; 80 | #endif 81 | } 82 | if (flags & IN_ATTRIB && S_ISREG (wf)) 83 | result |= NOTE_LINK; 84 | if (flags & IN_MOVE_SELF) 85 | result |= NOTE_RENAME; 86 | result |= NOTE_DELETE | NOTE_REVOKE; 87 | } 88 | return result; 89 | } 90 | 91 | /** 92 | * Convert the kqueue event filter flags to the inotify watch mask. 93 | * 94 | * @param[in] flags A kqueue filter flags. 95 | * @param[in] wf A kqueue watch internal flags. 96 | * @return Converted inotify watch mask. 97 | **/ 98 | uint32_t 99 | kqueue_to_inotify (uint32_t flags, watch_flags_t wf) 100 | { 101 | uint32_t result = 0; 102 | 103 | #ifdef NOTE_OPEN 104 | if (flags & NOTE_OPEN) 105 | result |= IN_OPEN; 106 | #endif 107 | #ifdef NOTE_CLOSE 108 | if (flags & NOTE_CLOSE) { 109 | if (wf & WF_MODIFIED && S_ISREG (wf)) 110 | result |= IN_CLOSE_WRITE; 111 | else 112 | result |= IN_CLOSE_NOWRITE; 113 | } 114 | #endif 115 | #ifdef NOTE_READ 116 | if (flags & NOTE_READ && S_ISREG (wf)) 117 | result |= IN_ACCESS; 118 | #endif 119 | 120 | if (flags & NOTE_ATTRIB) 121 | result |= IN_ATTRIB; 122 | 123 | if (flags & NOTE_LINK && S_ISREG (wf) && !(wf & WF_ISSUBWATCH)) 124 | result |= IN_ATTRIB; 125 | 126 | if (flags & NOTE_WRITE && S_ISREG (wf)) 127 | result |= IN_MODIFY; 128 | 129 | if (flags & NOTE_DELETE && !(wf & WF_ISSUBWATCH)) { 130 | /* Treat deletes as link number changes if links still exist */ 131 | if (wf & WF_DELETED || !S_ISREG (wf)) 132 | result |= IN_DELETE_SELF; 133 | else 134 | result |= IN_ATTRIB; 135 | } 136 | 137 | if (flags & NOTE_RENAME && !(wf & WF_ISSUBWATCH)) 138 | result |= IN_MOVE_SELF; 139 | 140 | if (flags & NOTE_REVOKE && !(wf & WF_ISSUBWATCH)) 141 | result |= IN_UNMOUNT; 142 | 143 | /* IN_ISDIR flag for subwatches is set in the enqueue_event routine */ 144 | if ((result & (IN_ATTRIB | IN_OPEN | IN_ACCESS | IN_CLOSE)) 145 | && S_ISDIR (wf) && !(wf & WF_ISSUBWATCH)) { 146 | result |= IN_ISDIR; 147 | } 148 | 149 | return result; 150 | } 151 | 152 | /* struct kevent is declared slightly differently on the different BSDs. 153 | * This macros will help to avoid cast warnings on the supported platforms. */ 154 | #if defined (__NetBSD__) 155 | #define PTR_TO_UDATA(X) ((intptr_t)X) 156 | #else 157 | #define PTR_TO_UDATA(X) (X) 158 | #endif 159 | 160 | /** 161 | * Register vnode kqueue watch in kernel kqueue(2) subsystem 162 | * 163 | * @param[in] w A pointer to a watch 164 | * @param[in] fflags A filter flags in kqueue format 165 | * @return 1 on success, -1 on error and 0 if no events have been registered 166 | **/ 167 | int 168 | watch_register_event (watch *w, uint32_t fflags) 169 | { 170 | assert (w != NULL); 171 | int kq = w->iw->wrk->kq; 172 | assert (kq != -1); 173 | 174 | struct kevent ev; 175 | 176 | EV_SET (&ev, 177 | w->fd, 178 | EVFILT_VNODE, 179 | EV_ADD | EV_ENABLE | EV_CLEAR, 180 | fflags, 181 | 0, 182 | PTR_TO_UDATA (w)); 183 | 184 | return kevent (kq, &ev, 1, NULL, 0, NULL); 185 | } 186 | 187 | /** 188 | * Opens a file descriptor of kqueue watch 189 | * 190 | * @param[in] dirfd A filedes of parent directory or AT_FDCWD. 191 | * @param[in] path A pointer to filename 192 | * @param[in] flags A watch flags in inotify format 193 | * @return A file descriptor of opened kqueue watch 194 | **/ 195 | int 196 | watch_open (int dirfd, const char *path, uint32_t flags) 197 | { 198 | assert (path != NULL); 199 | 200 | int openflags = O_NONBLOCK; 201 | #ifdef O_EVTONLY 202 | openflags |= O_EVTONLY; 203 | #else 204 | openflags |= O_RDONLY; 205 | #endif 206 | #ifdef O_CLOEXEC 207 | openflags |= O_CLOEXEC; 208 | #endif 209 | if (flags & IN_DONT_FOLLOW) { 210 | #ifdef O_SYMLINK 211 | openflags |= O_SYMLINK; 212 | #else 213 | openflags |= O_NOFOLLOW; 214 | #endif 215 | } 216 | #ifdef O_DIRECTORY 217 | if (flags & IN_ONLYDIR) { 218 | openflags |= O_DIRECTORY; 219 | } 220 | #endif 221 | 222 | int fd = openat (dirfd, path, openflags); 223 | if (fd == -1) { 224 | return -1; 225 | } 226 | 227 | #ifndef O_DIRECTORY 228 | if (flags & IN_ONLYDIR) { 229 | struct stat st; 230 | if (fstat (fd, &st) == -1) { 231 | perror_msg ("Failed to fstat on watch open %s", path); 232 | close (fd); 233 | return -1; 234 | } 235 | 236 | if (!S_ISDIR (st.st_mode)) { 237 | errno = ENOTDIR; 238 | close (fd); 239 | return -1; 240 | } 241 | } 242 | #endif 243 | 244 | #ifndef O_CLOEXEC 245 | if (set_cloexec_flag (fd, 1) == -1) { 246 | close (fd); 247 | return -1; 248 | } 249 | #endif 250 | 251 | return fd; 252 | } 253 | 254 | /** 255 | * Initialize a watch. 256 | * 257 | * @param[in] iw; A backreference to parent #i_watch. 258 | * @param[in] watch_type The type of the watch. 259 | * @param[in] fd A file descriptor of a watched entry. 260 | * @param[in] st A stat structure of watch. 261 | * @return A pointer to a watch on success, NULL on failure. 262 | **/ 263 | watch * 264 | watch_init (i_watch *iw, watch_type_t watch_type, int fd, struct stat *st) 265 | { 266 | assert (iw != NULL); 267 | assert (fd != -1); 268 | 269 | watch_flags_t wf = watch_type != WATCH_USER ? WF_ISSUBWATCH : 0; 270 | wf |= st->st_mode & S_IFMT; 271 | 272 | uint32_t fflags = inotify_to_kqueue (iw->flags, wf); 273 | /* Skip watches with empty kqueue filter flags */ 274 | if (fflags == 0) { 275 | return NULL; 276 | } 277 | 278 | watch *w = calloc (1, sizeof (struct watch)); 279 | if (w == NULL) { 280 | perror_msg ("Failed to allocate watch"); 281 | return NULL; 282 | } 283 | 284 | w->iw = iw; 285 | w->fd = fd; 286 | w->flags = wf; 287 | w->refcount = 0; 288 | /* Inode number obtained via fstat call cannot be used here as it 289 | * differs from readdir`s one at mount points. */ 290 | w->inode = st->st_ino; 291 | 292 | if (watch_register_event (w, fflags) == -1) { 293 | free (w); 294 | return NULL; 295 | } 296 | 297 | return w; 298 | } 299 | 300 | /** 301 | * Free a watch and all the associated memory. 302 | * 303 | * @param[in] w A pointer to a watch. 304 | **/ 305 | void 306 | watch_free (watch *w) 307 | { 308 | assert (w != NULL); 309 | if (w->fd != -1) { 310 | close (w->fd); 311 | } 312 | free (w); 313 | } 314 | -------------------------------------------------------------------------------- /watch.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __WATCH_H__ 24 | #define __WATCH_H__ 25 | 26 | #include "compat.h" 27 | 28 | #include /* ino_t */ 29 | 30 | #include 31 | #include /* stat */ 32 | 33 | typedef struct watch watch; 34 | /* Inherit watch_flags_t from mode_t type. 35 | * It is hackish but allow to use existing stat macroses */ 36 | typedef mode_t watch_flags_t; 37 | 38 | #include "inotify-watch.h" 39 | 40 | #define WF_ISSUBWATCH S_IXOTH /* a type of watch */ 41 | #define WF_DELETED S_IROTH /* file`s link count == 0 */ 42 | #define WF_MODIFIED S_IWOTH /* file has been modified i.e. received 43 | * NOTE_WRITE since last NOTE_CLOSE event */ 44 | 45 | typedef enum watch_type { 46 | WATCH_USER, 47 | WATCH_DEPENDENCY, 48 | } watch_type_t; 49 | 50 | 51 | struct watch { 52 | i_watch *iw; /* A pointer to parent inotify watch */ 53 | watch_flags_t flags; /* A watch flags. Not in inotify/kqueue format */ 54 | size_t refcount; /* number of dependency list items corresponding 55 | * to that watch */ 56 | int fd; /* file descriptor of a watched entry */ 57 | ino_t inode; /* inode number taken from readdir call */ 58 | RB_ENTRY(watch) link; /* RB tree links */ 59 | }; 60 | 61 | uint32_t inotify_to_kqueue (uint32_t flags, watch_flags_t wf); 62 | uint32_t kqueue_to_inotify (uint32_t flags, watch_flags_t wf); 63 | 64 | int watch_open (int dirfd, const char *path, uint32_t flags); 65 | watch *watch_init (i_watch *iw, 66 | watch_type_t watch_type, 67 | int fd, 68 | struct stat *st); 69 | void watch_free (watch *w); 70 | 71 | int watch_register_event (watch *w, uint32_t fflags); 72 | 73 | #endif /* __WATCH_H__ */ 74 | -------------------------------------------------------------------------------- /worker-thread.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __WORKER_THREAD_H__ 24 | #define __WORKER_THREAD_H__ 25 | 26 | #include "compat.h" 27 | 28 | #include "inotify-watch.h" 29 | #include "worker.h" 30 | 31 | void* worker_thread (void *arg); 32 | int enqueue_event (i_watch *iw, uint32_t mask, const dep_item *di); 33 | void flush_events (worker *wrk); 34 | 35 | #endif /* __WORKER_THREAD_H__ */ 36 | -------------------------------------------------------------------------------- /worker.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2011-2014 Dmitry Matveev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef __WORKER_H__ 24 | #define __WORKER_H__ 25 | 26 | #include "compat.h" 27 | 28 | #include /* iovec */ 29 | 30 | #include 31 | 32 | typedef struct worker worker; 33 | 34 | #include "worker-thread.h" 35 | #include "dep-list.h" 36 | #include "inotify-watch.h" 37 | #include "watch.h" 38 | 39 | #define INOTIFY_FD 0 40 | #define KQUEUE_FD 1 41 | 42 | typedef enum { 43 | WCMD_NONE = 0, /* uninitialized state */ 44 | WCMD_ADD, /* add or modify a watch */ 45 | WCMD_REMOVE, /* remove a watch */ 46 | } worker_cmd_type_t; 47 | 48 | /** 49 | * This structure represents a user call to the inotify API. 50 | * It is also used to synchronize a user thread with a worker thread. 51 | **/ 52 | typedef struct worker_cmd { 53 | worker_cmd_type_t type; 54 | int retval; 55 | int error; 56 | 57 | union { 58 | struct { 59 | const char *filename; 60 | uint32_t mask; 61 | } add; 62 | 63 | int rm_id; 64 | }; 65 | 66 | pthread_barrier_t sync; 67 | } worker_cmd; 68 | 69 | void worker_cmd_init (worker_cmd *cmd); 70 | void worker_cmd_add (worker_cmd *cmd, const char *filename, uint32_t mask); 71 | void worker_cmd_remove (worker_cmd *cmd, int watch_id); 72 | void worker_cmd_wait (worker_cmd *cmd); 73 | void worker_cmd_release (worker_cmd *cmd); 74 | 75 | struct worker { 76 | int kq; /* kqueue descriptor */ 77 | volatile int io[2]; /* a socket pair */ 78 | struct iovec *iov; /* inotify events to send */ 79 | int iovcnt; /* number of events enqueued */ 80 | int iovalloc; /* number of iovs allocated */ 81 | pthread_t thread; /* worker thread */ 82 | SLIST_HEAD(, i_watch) head; /* linked list of inotify watches */ 83 | volatile int closed; /* closed flag */ 84 | 85 | pthread_mutex_t mutex; /* worker mutex */ 86 | worker_cmd cmd; /* operation to perform on a worker */ 87 | }; 88 | 89 | 90 | worker* worker_create (int flags); 91 | void worker_free (worker *wrk); 92 | 93 | int worker_add_or_modify (worker *wrk, const char *path, uint32_t flags); 94 | int worker_remove (worker *wrk, int id); 95 | 96 | #endif /* __WORKER_H__ */ 97 | --------------------------------------------------------------------------------