]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/src/core/fsnotify.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / seastar / src / core / fsnotify.cc
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 <seastar/core/fsnotify.hh>
26
27 namespace seastar::experimental {
28
29 class fsnotifier::impl : public enable_shared_from_this<impl> {
30 class my_poll_fd : public pollable_fd {
31 public:
32 using pollable_fd::pollable_fd;
33 using pollable_fd::get_fd;
34
35 operator int() const {
36 return get_fd();
37 }
38 };
39 my_poll_fd _fd;
40 watch_token _close_dummy = -1;
41 public:
42 impl()
43 : _fd(file_desc::inotify_init(IN_NONBLOCK | IN_CLOEXEC))
44 {}
45 void remove_watch(watch_token);
46 future<watch_token> create_watch(const sstring& path, flags events);
47 future<std::vector<event>> wait();
48 void shutdown();
49 bool active() const {
50 return bool(_fd);
51 }
52 };
53
54 void fsnotifier::impl::remove_watch(watch_token token) {
55 if (active()) {
56 auto res = ::inotify_rm_watch(_fd, token);
57 // throw if any other error than EINVAL.
58 throw_system_error_on(res == -1 && errno != EINVAL, "could not remove inotify watch");
59 }
60 }
61
62 future<fsnotifier::watch_token> fsnotifier::impl::create_watch(const sstring& path, flags events) {
63 if (!active()) {
64 throw std::runtime_error("attempting to use closed notifier");
65 }
66 return engine().inotify_add_watch(_fd, path, uint32_t(events));
67 }
68
69 future<std::vector<fsnotifier::event>> fsnotifier::impl::wait() {
70 // be paranoid about buffer alignment
71 auto buf = temporary_buffer<char>::aligned(std::max(alignof(::inotify_event), alignof(int64_t)), 4096);
72 auto f = _fd.read_some(buf.get_write(), buf.size());
73 return f.then([me = shared_from_this(), buf = std::move(buf)](size_t n) {
74 auto p = buf.get();
75 auto e = buf.get() + n;
76
77 std::vector<event> events;
78
79 while (p < e) {
80 auto ev = reinterpret_cast<const ::inotify_event*>(p);
81 if (ev->wd == me->_close_dummy && me->_close_dummy != -1) {
82 me->_fd.close();
83 } else {
84 events.emplace_back(event {
85 ev->wd, flags(ev->mask), ev->cookie,
86 ev->len != 0 ? sstring(ev->name) : sstring{}
87 });
88 }
89 p += sizeof(::inotify_event) + ev->len;
90 }
91
92 return events;
93 });
94 }
95
96 void fsnotifier::impl::shutdown() {
97 // reactor does not yet have
98 // any means of "shutting down" a non-socket read,
99 // so we work around this by creating a watch for something ubiquitous,
100 // then removing the watch while adding a mark.
101 // This will cause any event waiter to wake up, but ignore the event for our
102 // dummy.
103 (void)create_watch("/", flags::delete_self).then([me = shared_from_this()](watch_token t) {
104 me->_close_dummy = t;
105 me->remove_watch(t);
106 });
107 }
108
109 fsnotifier::watch::~watch() {
110 if (_impl) {
111 _impl->remove_watch(_token);
112 }
113 }
114
115 fsnotifier::watch::watch(watch&&) noexcept = default;
116 fsnotifier::watch& fsnotifier::watch::operator=(watch&&) noexcept = default;
117
118 fsnotifier::watch_token fsnotifier::watch::release() {
119 _impl = {};
120 return _token;
121 }
122
123 fsnotifier::watch::watch(shared_ptr<impl> impl, watch_token token)
124 : _token(token)
125 , _impl(std::move(impl))
126 {}
127
128 fsnotifier::fsnotifier()
129 : _impl(make_shared<impl>())
130 {}
131
132 fsnotifier::~fsnotifier() = default;
133
134 fsnotifier::fsnotifier(fsnotifier&&) = default;
135 fsnotifier& fsnotifier::operator=(fsnotifier&&) = default;
136
137 future<fsnotifier::watch> fsnotifier::create_watch(const sstring& path, flags events) {
138 return _impl->create_watch(path, events).then([this](watch_token token) {
139 return watch(_impl, token);
140 });
141 }
142
143 future<std::vector<fsnotifier::event>> fsnotifier::wait() const {
144 return _impl->wait();
145 }
146
147 void fsnotifier::shutdown() {
148 _impl->shutdown();
149 }
150
151 bool fsnotifier::active() const {
152 return _impl->active();
153 }
154
155 }