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.
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.
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/>.
19 #include "lib/util/tevent_ntstatus.h"
22 struct fcn_event
*prev
, *next
;
23 struct notify_event_msg msg
;
26 struct fcn_wait_state
{
27 struct tevent_context
*ev
;
28 struct messaging_context
*msg_ctx
;
29 struct server_id notifyd
;
32 struct tevent_req
*recv_subreq
;
34 struct fcn_event
*events
;
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
);
43 struct tevent_req
*fcn_wait_send(
45 struct tevent_context
*ev
,
46 struct messaging_context
*msg_ctx
,
47 struct server_id notifyd
,
50 uint32_t subdir_filter
)
52 struct tevent_req
*req
= NULL
;
53 struct fcn_wait_state
*state
= NULL
;
54 struct notify_rec_change_msg msg
= {
55 .instance
.filter
= filter
,
56 .instance
.subdir_filter
= subdir_filter
,
61 req
= tevent_req_create(mem_ctx
, &state
, struct fcn_wait_state
);
66 state
->msg_ctx
= msg_ctx
;
67 state
->notifyd
= notifyd
;
70 state
->recv_subreq
= messaging_filtered_read_send(
71 state
, ev
, msg_ctx
, fcn_wait_filter
, req
);
72 if (tevent_req_nomem(state
->recv_subreq
, req
)) {
73 return tevent_req_post(req
, ev
);
75 tevent_req_set_callback(state
->recv_subreq
, fcn_wait_done
, req
);
76 tevent_req_set_cleanup_fn(req
, fcn_wait_cleanup
);
78 clock_gettime_mono(&msg
.instance
.creation_time
);
79 msg
.instance
.private_data
= state
;
81 iov
[0].iov_base
= &msg
;
82 iov
[0].iov_len
= offsetof(struct notify_rec_change_msg
, path
);
83 iov
[1].iov_base
= discard_const_p(char, path
);
84 iov
[1].iov_len
= strlen(path
)+1;
86 status
= messaging_send_iov(
87 msg_ctx
, /* msg_ctx */
89 MSG_SMB_NOTIFY_REC_CHANGE
, /* mst_type */
91 ARRAY_SIZE(iov
), /* iovlen */
94 if (tevent_req_nterror(req
, status
)) {
95 DBG_DEBUG("messaging_send_iov failed: %s\n",
97 return tevent_req_post(req
, ev
);
99 tevent_req_set_cancel_fn(req
, fcn_wait_cancel
);
104 static bool fcn_wait_cancel(struct tevent_req
*req
)
106 struct fcn_wait_state
*state
= tevent_req_data(
107 req
, struct fcn_wait_state
);
108 struct notify_rec_change_msg msg
= {
109 .instance
.filter
= 0, /* filter==0 is a delete msg */
110 .instance
.subdir_filter
= 0,
115 clock_gettime_mono(&msg
.instance
.creation_time
);
116 msg
.instance
.private_data
= state
;
118 iov
[0].iov_base
= &msg
;
119 iov
[0].iov_len
= offsetof(struct notify_rec_change_msg
, path
);
120 iov
[1].iov_base
= discard_const_p(char, state
->path
);
121 iov
[1].iov_len
= strlen(state
->path
)+1;
123 status
= messaging_send_iov(
124 state
->msg_ctx
, /* msg_ctx */
125 state
->notifyd
, /* dst */
126 MSG_SMB_NOTIFY_REC_CHANGE
, /* mst_type */
128 ARRAY_SIZE(iov
), /* iovlen */
131 if (!NT_STATUS_IS_OK(status
)) {
132 DBG_DEBUG("messaging_send_iov failed: %s\n",
137 fcn_wait_cleanup(req
, 0); /* fcn_wait_cleanup ignores req_state */
138 tevent_req_defer_callback(req
, state
->ev
);
139 tevent_req_nterror(req
, NT_STATUS_CANCELLED
);
144 static void fcn_wait_cleanup(
145 struct tevent_req
*req
, enum tevent_req_state req_state
)
147 struct fcn_wait_state
*state
= tevent_req_data(
148 req
, struct fcn_wait_state
);
149 TALLOC_FREE(state
->recv_subreq
);
152 static bool fcn_wait_filter(struct messaging_rec
*rec
, void *private_data
)
154 struct tevent_req
*req
= talloc_get_type_abort(
155 private_data
, struct tevent_req
);
156 struct fcn_wait_state
*state
= tevent_req_data(
157 req
, struct fcn_wait_state
);
158 struct notify_event_msg msg
= { .action
= 0 };
159 struct fcn_event
*evt
= NULL
;
161 if (rec
->msg_type
!= MSG_PVFS_NOTIFY
) {
162 DBG_DEBUG("Ignoring msg %"PRIu32
"\n", rec
->msg_type
);
167 * We need at least the trailing '\0' for the path
169 if (rec
->buf
.length
< (offsetof(struct notify_event_msg
, path
) + 1)) {
170 DBG_DEBUG("Ignoring short (%zu) msg\n", rec
->buf
.length
);
173 if (rec
->buf
.data
[rec
->buf
.length
-1] != '\0') {
174 DBG_DEBUG("Expected 0-terminated path\n");
178 memcpy(&msg
, rec
->buf
.data
, sizeof(msg
));
180 if (msg
.private_data
!= state
) {
181 DBG_DEBUG("Got private_data=%p, expected %p\n",
187 evt
= talloc_memdup(state
, rec
->buf
.data
, rec
->buf
.length
);
189 DBG_DEBUG("talloc_memdup failed\n");
192 talloc_set_name_const(evt
, "struct fcn_event");
195 * TODO: Sort by timestamp
198 DLIST_ADD_END(state
->events
, evt
);
200 tevent_req_defer_callback(req
, state
->ev
);
201 tevent_req_notify_callback(req
);
206 static void fcn_wait_done(struct tevent_req
*subreq
)
208 struct tevent_req
*req
= tevent_req_callback_data(
209 subreq
, struct tevent_req
);
212 ret
= messaging_filtered_read_recv(subreq
, NULL
, NULL
);
215 DBG_DEBUG("messaging_filtered_read failed: %s\n",
217 tevent_req_nterror(req
, map_nt_error_from_unix(ret
));
222 * We should never have gotten here, all work is done from the
225 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
228 NTSTATUS
fcn_wait_recv(
229 struct tevent_req
*req
,
231 struct timespec
*when
,
235 struct fcn_wait_state
*state
= tevent_req_data(
236 req
, struct fcn_wait_state
);
237 struct fcn_event
*evt
= NULL
;
240 if (!tevent_req_is_in_progress(req
) &&
241 tevent_req_is_nterror(req
, &status
)) {
246 return NT_STATUS_RETRY
;
250 *path
= talloc_strdup(mem_ctx
, evt
->msg
.path
);
251 if ((*path
) == NULL
) {
252 return NT_STATUS_NO_MEMORY
;
256 *when
= evt
->msg
.when
;
258 if (action
!= NULL
) {
259 *action
= evt
->msg
.action
;
262 DLIST_REMOVE(state
->events
, evt
);
264 if (state
->events
!= NULL
) {
265 tevent_req_defer_callback(req
, state
->ev
);
266 tevent_req_notify_callback(req
);