Line data Source code
1 : /*
2 : * This program is free software; you can redistribute it and/or modify
3 : * it under the terms of the GNU General Public License as published by
4 : * the Free Software Foundation; either version 3 of the License, or
5 : * (at your option) any later version.
6 : *
7 : * This program is distributed in the hope that it will be useful,
8 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 : * GNU General Public License for more details.
11 : *
12 : * You should have received a copy of the GNU General Public License
13 : * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 : */
15 :
16 : #include "replace.h"
17 : #include "fcn_wait.h"
18 : #include "notifyd.h"
19 : #include "lib/util/tevent_ntstatus.h"
20 :
21 : struct fcn_event {
22 : struct fcn_event *prev, *next;
23 : struct notify_event_msg msg;
24 : };
25 :
26 : struct fcn_wait_state {
27 : struct tevent_context *ev;
28 : struct messaging_context *msg_ctx;
29 : struct server_id notifyd;
30 : const char *path;
31 :
32 : struct tevent_req *recv_subreq;
33 :
34 : struct fcn_event *events;
35 : };
36 :
37 : static bool fcn_wait_cancel(struct tevent_req *req);
38 : static void fcn_wait_cleanup(
39 : struct tevent_req *req, enum tevent_req_state req_state);
40 : static bool fcn_wait_filter(struct messaging_rec *rec, void *private_data);
41 : static void fcn_wait_done(struct tevent_req *subreq);
42 :
43 4 : struct tevent_req *fcn_wait_send(
44 : TALLOC_CTX *mem_ctx,
45 : struct tevent_context *ev,
46 : struct messaging_context *msg_ctx,
47 : struct server_id notifyd,
48 : const char *path,
49 : uint32_t filter,
50 : uint32_t subdir_filter)
51 : {
52 4 : struct tevent_req *req = NULL;
53 4 : struct fcn_wait_state *state = NULL;
54 4 : struct notify_rec_change_msg msg = {
55 : .instance.filter = filter,
56 : .instance.subdir_filter = subdir_filter,
57 : };
58 0 : struct iovec iov[2];
59 0 : NTSTATUS status;
60 :
61 4 : req = tevent_req_create(mem_ctx, &state, struct fcn_wait_state);
62 4 : if (req == NULL) {
63 0 : return NULL;
64 : }
65 4 : state->ev = ev;
66 4 : state->msg_ctx = msg_ctx;
67 4 : state->notifyd = notifyd;
68 4 : state->path = path;
69 :
70 4 : state->recv_subreq = messaging_filtered_read_send(
71 : state, ev, msg_ctx, fcn_wait_filter, req);
72 4 : if (tevent_req_nomem(state->recv_subreq, req)) {
73 0 : return tevent_req_post(req, ev);
74 : }
75 4 : tevent_req_set_callback(state->recv_subreq, fcn_wait_done, req);
76 4 : tevent_req_set_cleanup_fn(req, fcn_wait_cleanup);
77 :
78 4 : clock_gettime_mono(&msg.instance.creation_time);
79 4 : msg.instance.private_data = state;
80 :
81 4 : iov[0].iov_base = &msg;
82 4 : iov[0].iov_len = offsetof(struct notify_rec_change_msg, path);
83 4 : iov[1].iov_base = discard_const_p(char, path);
84 4 : iov[1].iov_len = strlen(path)+1;
85 :
86 4 : status = messaging_send_iov(
87 : msg_ctx, /* msg_ctx */
88 : notifyd, /* dst */
89 : MSG_SMB_NOTIFY_REC_CHANGE, /* mst_type */
90 : iov, /* iov */
91 : ARRAY_SIZE(iov), /* iovlen */
92 : NULL, /* fds */
93 : 0); /* num_fds */
94 4 : if (tevent_req_nterror(req, status)) {
95 0 : DBG_DEBUG("messaging_send_iov failed: %s\n",
96 : nt_errstr(status));
97 0 : return tevent_req_post(req, ev);
98 : }
99 4 : tevent_req_set_cancel_fn(req, fcn_wait_cancel);
100 :
101 4 : return req;
102 : }
103 :
104 4 : static bool fcn_wait_cancel(struct tevent_req *req)
105 : {
106 4 : struct fcn_wait_state *state = tevent_req_data(
107 : req, struct fcn_wait_state);
108 4 : struct notify_rec_change_msg msg = {
109 : .instance.filter = 0, /* filter==0 is a delete msg */
110 : .instance.subdir_filter = 0,
111 : };
112 0 : struct iovec iov[2];
113 0 : NTSTATUS status;
114 :
115 4 : clock_gettime_mono(&msg.instance.creation_time);
116 4 : msg.instance.private_data = state;
117 :
118 4 : iov[0].iov_base = &msg;
119 4 : iov[0].iov_len = offsetof(struct notify_rec_change_msg, path);
120 4 : iov[1].iov_base = discard_const_p(char, state->path);
121 4 : iov[1].iov_len = strlen(state->path)+1;
122 :
123 4 : status = messaging_send_iov(
124 : state->msg_ctx, /* msg_ctx */
125 : state->notifyd, /* dst */
126 : MSG_SMB_NOTIFY_REC_CHANGE, /* mst_type */
127 : iov, /* iov */
128 : ARRAY_SIZE(iov), /* iovlen */
129 : NULL, /* fds */
130 : 0); /* num_fds */
131 4 : if (!NT_STATUS_IS_OK(status)) {
132 0 : DBG_DEBUG("messaging_send_iov failed: %s\n",
133 : nt_errstr(status));
134 0 : return false;
135 : }
136 :
137 4 : fcn_wait_cleanup(req, 0); /* fcn_wait_cleanup ignores req_state */
138 4 : tevent_req_defer_callback(req, state->ev);
139 4 : tevent_req_nterror(req, NT_STATUS_CANCELLED);
140 :
141 4 : return true;
142 : }
143 :
144 12 : static void fcn_wait_cleanup(
145 : struct tevent_req *req, enum tevent_req_state req_state)
146 : {
147 12 : struct fcn_wait_state *state = tevent_req_data(
148 : req, struct fcn_wait_state);
149 12 : TALLOC_FREE(state->recv_subreq);
150 12 : }
151 :
152 2 : static bool fcn_wait_filter(struct messaging_rec *rec, void *private_data)
153 : {
154 2 : struct tevent_req *req = talloc_get_type_abort(
155 : private_data, struct tevent_req);
156 2 : struct fcn_wait_state *state = tevent_req_data(
157 : req, struct fcn_wait_state);
158 2 : struct notify_event_msg msg = { .action = 0 };
159 2 : struct fcn_event *evt = NULL;
160 :
161 2 : if (rec->msg_type != MSG_PVFS_NOTIFY) {
162 0 : DBG_DEBUG("Ignoring msg %"PRIu32"\n", rec->msg_type);
163 0 : return false;
164 : }
165 :
166 : /*
167 : * We need at least the trailing '\0' for the path
168 : */
169 2 : if (rec->buf.length < (offsetof(struct notify_event_msg, path) + 1)) {
170 0 : DBG_DEBUG("Ignoring short (%zu) msg\n", rec->buf.length);
171 0 : return false;
172 : }
173 2 : if (rec->buf.data[rec->buf.length-1] != '\0') {
174 0 : DBG_DEBUG("Expected 0-terminated path\n");
175 0 : return false;
176 : }
177 :
178 2 : memcpy(&msg, rec->buf.data, sizeof(msg));
179 :
180 2 : if (msg.private_data != state) {
181 0 : DBG_DEBUG("Got private_data=%p, expected %p\n",
182 : msg.private_data,
183 : state);
184 0 : return false;
185 : }
186 :
187 2 : evt = talloc_memdup(state, rec->buf.data, rec->buf.length);
188 2 : if (evt == NULL) {
189 0 : DBG_DEBUG("talloc_memdup failed\n");
190 0 : return false;
191 : }
192 2 : talloc_set_name_const(evt, "struct fcn_event");
193 :
194 : /*
195 : * TODO: Sort by timestamp
196 : */
197 :
198 2 : DLIST_ADD_END(state->events, evt);
199 :
200 2 : tevent_req_defer_callback(req, state->ev);
201 2 : tevent_req_notify_callback(req);
202 :
203 2 : return false;
204 : }
205 :
206 0 : static void fcn_wait_done(struct tevent_req *subreq)
207 : {
208 0 : struct tevent_req *req = tevent_req_callback_data(
209 : subreq, struct tevent_req);
210 0 : int ret;
211 :
212 0 : ret = messaging_filtered_read_recv(subreq, NULL, NULL);
213 0 : TALLOC_FREE(subreq);
214 0 : if (ret != 0) {
215 0 : DBG_DEBUG("messaging_filtered_read failed: %s\n",
216 : strerror(ret));
217 0 : tevent_req_nterror(req, map_nt_error_from_unix(ret));
218 0 : return;
219 : }
220 :
221 : /*
222 : * We should never have gotten here, all work is done from the
223 : * filter function.
224 : */
225 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
226 : }
227 :
228 6 : NTSTATUS fcn_wait_recv(
229 : struct tevent_req *req,
230 : TALLOC_CTX *mem_ctx,
231 : struct timespec *when,
232 : uint32_t *action,
233 : char **path)
234 : {
235 6 : struct fcn_wait_state *state = tevent_req_data(
236 : req, struct fcn_wait_state);
237 6 : struct fcn_event *evt = NULL;
238 0 : NTSTATUS status;
239 :
240 10 : if (!tevent_req_is_in_progress(req) &&
241 4 : tevent_req_is_nterror(req, &status)) {
242 4 : return status;
243 : }
244 2 : evt = state->events;
245 2 : if (evt == NULL) {
246 0 : return NT_STATUS_RETRY;
247 : }
248 :
249 2 : if (path != NULL) {
250 0 : *path = talloc_strdup(mem_ctx, evt->msg.path);
251 0 : if ((*path) == NULL) {
252 0 : return NT_STATUS_NO_MEMORY;
253 : }
254 : }
255 2 : if (when != NULL) {
256 0 : *when = evt->msg.when;
257 : }
258 2 : if (action != NULL) {
259 0 : *action = evt->msg.action;
260 : }
261 :
262 2 : DLIST_REMOVE(state->events, evt);
263 :
264 2 : if (state->events != NULL) {
265 0 : tevent_req_defer_callback(req, state->ev);
266 0 : tevent_req_notify_callback(req);
267 : }
268 :
269 2 : return NT_STATUS_OK;
270 : }
|