]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | /* |
2 | * This file is open source software, licensed to you under the terms | |
3 | * of the Apache License, Version 2.0 (the "License"). See the NOTICE file | |
4 | * distributed with this work for additional information regarding copyright | |
5 | * ownership. You may not use this file except in compliance with the License. | |
6 | * | |
7 | * You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, | |
12 | * software distributed under the License is distributed on an | |
13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
14 | * KIND, either express or implied. See the License for the | |
15 | * specific language governing permissions and limitations | |
16 | * under the License. | |
17 | */ | |
18 | /* | |
19 | * Copyright 2020 ScyllaDB Ltd. | |
20 | */ | |
21 | ||
22 | #include <seastar/core/internal/pollable_fd.hh> | |
23 | #include <seastar/core/posix.hh> | |
24 | #include <seastar/core/reactor.hh> | |
25 | #include "fsnotify.hh" | |
26 | ||
27 | class seastar::fsnotifier::impl : public enable_shared_from_this<impl> { | |
28 | class my_poll_fd : public pollable_fd { | |
29 | public: | |
30 | using pollable_fd::pollable_fd; | |
31 | using pollable_fd::get_fd; | |
32 | ||
33 | operator int() const { | |
34 | return get_fd(); | |
35 | } | |
36 | }; | |
37 | my_poll_fd _fd; | |
38 | watch_token _close_dummy = -1; | |
39 | public: | |
40 | impl() | |
41 | : _fd(file_desc::inotify_init(IN_NONBLOCK | IN_CLOEXEC)) | |
42 | {} | |
43 | void remove_watch(watch_token); | |
44 | future<watch_token> create_watch(const sstring& path, flags events); | |
45 | future<std::vector<event>> wait(); | |
46 | void shutdown(); | |
47 | bool active() const { | |
48 | return bool(_fd); | |
49 | } | |
50 | }; | |
51 | ||
52 | void seastar::fsnotifier::impl::remove_watch(watch_token token) { | |
53 | if (active()) { | |
54 | auto res = ::inotify_rm_watch(_fd, token); | |
55 | // throw if any other error than EINVAL. | |
56 | throw_system_error_on(res == -1 && errno != EINVAL, "could not remove inotify watch"); | |
57 | } | |
58 | } | |
59 | ||
60 | seastar::future<seastar::fsnotifier::watch_token> seastar::fsnotifier::impl::create_watch(const sstring& path, flags events) { | |
61 | if (!active()) { | |
62 | throw std::runtime_error("attempting to use closed notifier"); | |
63 | } | |
64 | return engine().inotify_add_watch(_fd, path, uint32_t(events)); | |
65 | } | |
66 | ||
67 | seastar::future<std::vector<seastar::fsnotifier::event>> seastar::fsnotifier::impl::wait() { | |
68 | // be paranoid about buffer alignment | |
69 | auto buf = temporary_buffer<char>::aligned(std::max(alignof(::inotify_event), alignof(int64_t)), 4096); | |
70 | auto f = _fd.read_some(buf.get_write(), buf.size()); | |
71 | return f.then([me = shared_from_this(), buf = std::move(buf)](size_t n) { | |
72 | auto p = buf.get(); | |
73 | auto e = buf.get() + n; | |
74 | ||
75 | std::vector<event> events; | |
76 | ||
77 | while (p < e) { | |
78 | auto ev = reinterpret_cast<const ::inotify_event*>(p); | |
79 | if (ev->wd == me->_close_dummy && me->_close_dummy != -1) { | |
80 | me->_fd.close(); | |
81 | } else { | |
82 | events.emplace_back(event { | |
83 | ev->wd, flags(ev->mask), ev->cookie, | |
84 | ev->len != 0 ? sstring(ev->name) : sstring{} | |
85 | }); | |
86 | } | |
87 | p += sizeof(::inotify_event) + ev->len; | |
88 | } | |
89 | ||
90 | return events; | |
91 | }); | |
92 | } | |
93 | ||
94 | void seastar::fsnotifier::impl::shutdown() { | |
95 | // reactor does not yet have | |
96 | // any means of "shutting down" a non-socket read, | |
97 | // so we work around this by creating a watch for something ubiquitous, | |
98 | // then removing the watch while adding a mark. | |
99 | // This will cause any event waiter to wake up, but ignore the event for our | |
100 | // dummy. | |
101 | (void)create_watch("/", flags::delete_self).then([me = shared_from_this()](watch_token t) { | |
102 | me->_close_dummy = t; | |
103 | me->remove_watch(t); | |
104 | }); | |
105 | } | |
106 | ||
107 | seastar::fsnotifier::watch::~watch() { | |
108 | if (_impl) { | |
109 | _impl->remove_watch(_token); | |
110 | } | |
111 | } | |
112 | ||
113 | seastar::fsnotifier::watch::watch(watch&&) noexcept = default; | |
114 | seastar::fsnotifier::watch& seastar::fsnotifier::watch::operator=(watch&&) noexcept = default; | |
115 | ||
116 | seastar::fsnotifier::watch_token seastar::fsnotifier::watch::release() { | |
117 | _impl = {}; | |
118 | return _token; | |
119 | } | |
120 | ||
121 | seastar::fsnotifier::watch::watch(shared_ptr<impl> impl, watch_token token) | |
122 | : _token(token) | |
123 | , _impl(std::move(impl)) | |
124 | {} | |
125 | ||
126 | seastar::fsnotifier::fsnotifier() | |
127 | : _impl(make_shared<impl>()) | |
128 | {} | |
129 | ||
130 | seastar::fsnotifier::~fsnotifier() = default; | |
131 | ||
132 | seastar::fsnotifier::fsnotifier(fsnotifier&&) = default; | |
133 | seastar::fsnotifier& seastar::fsnotifier::operator=(fsnotifier&&) = default; | |
134 | ||
135 | seastar::future<seastar::fsnotifier::watch> seastar::fsnotifier::create_watch(const sstring& path, flags events) { | |
136 | return _impl->create_watch(path, events).then([this](watch_token token) { | |
137 | return watch(_impl, token); | |
138 | }); | |
139 | } | |
140 | ||
141 | seastar::future<std::vector<seastar::fsnotifier::event>> seastar::fsnotifier::wait() const { | |
142 | return _impl->wait(); | |
143 | } | |
144 | ||
145 | void seastar::fsnotifier::shutdown() { | |
146 | _impl->shutdown(); | |
147 | } | |
148 | ||
149 | bool seastar::fsnotifier::active() const { | |
150 | return _impl->active(); | |
151 | } |