00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "includes.h"
00028 #include "librpc/gen_ndr/ndr_notify.h"
00029
00030 struct notify_context {
00031 struct tdb_wrap *w;
00032 struct server_id server;
00033 struct messaging_context *messaging_ctx;
00034 struct notify_list *list;
00035 struct notify_array *array;
00036 int seqnum;
00037 struct sys_notify_context *sys_notify_ctx;
00038 };
00039
00040
00041 struct notify_list {
00042 struct notify_list *next, *prev;
00043 void *private_data;
00044 void (*callback)(void *, const struct notify_event *);
00045 void *sys_notify_handle;
00046 int depth;
00047 };
00048
00049 #define NOTIFY_KEY "notify array"
00050
00051 #define NOTIFY_ENABLE "notify:enable"
00052 #define NOTIFY_ENABLE_DEFAULT True
00053
00054 static NTSTATUS notify_remove_all(struct notify_context *notify,
00055 const struct server_id *server);
00056 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
00057 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
00058
00059
00060
00061
00062 static int notify_destructor(struct notify_context *notify)
00063 {
00064 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
00065
00066 if (notify->list != NULL) {
00067 notify_remove_all(notify, ¬ify->server);
00068 }
00069
00070 return 0;
00071 }
00072
00073
00074
00075
00076
00077
00078 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
00079 struct messaging_context *messaging_ctx,
00080 struct event_context *ev,
00081 connection_struct *conn)
00082 {
00083 struct notify_context *notify;
00084
00085 if (!lp_change_notify(conn->params)) {
00086 return NULL;
00087 }
00088
00089 notify = talloc(mem_ctx, struct notify_context);
00090 if (notify == NULL) {
00091 return NULL;
00092 }
00093
00094 notify->w = tdb_wrap_open(notify, lock_path("notify.tdb"),
00095 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST,
00096 O_RDWR|O_CREAT, 0644);
00097 if (notify->w == NULL) {
00098 talloc_free(notify);
00099 return NULL;
00100 }
00101
00102 notify->server = server;
00103 notify->messaging_ctx = messaging_ctx;
00104 notify->list = NULL;
00105 notify->array = NULL;
00106 notify->seqnum = tdb_get_seqnum(notify->w->tdb);
00107
00108 talloc_set_destructor(notify, notify_destructor);
00109
00110
00111
00112 messaging_register(notify->messaging_ctx, notify,
00113 MSG_PVFS_NOTIFY, notify_handler);
00114
00115 notify->sys_notify_ctx = sys_notify_context_create(conn, notify, ev);
00116
00117 return notify;
00118 }
00119
00120
00121
00122
00123
00124 static NTSTATUS notify_lock(struct notify_context *notify)
00125 {
00126 if (tdb_lock_bystring(notify->w->tdb, NOTIFY_KEY) != 0) {
00127 return NT_STATUS_INTERNAL_DB_CORRUPTION;
00128 }
00129 return NT_STATUS_OK;
00130 }
00131
00132
00133
00134
00135 static void notify_unlock(struct notify_context *notify)
00136 {
00137 tdb_unlock_bystring(notify->w->tdb, NOTIFY_KEY);
00138 }
00139
00140
00141
00142
00143 static NTSTATUS notify_load(struct notify_context *notify)
00144 {
00145 TDB_DATA dbuf;
00146 DATA_BLOB blob;
00147 NTSTATUS status;
00148 int seqnum;
00149
00150 seqnum = tdb_get_seqnum(notify->w->tdb);
00151
00152 if (seqnum == notify->seqnum && notify->array != NULL) {
00153 return NT_STATUS_OK;
00154 }
00155
00156 notify->seqnum = seqnum;
00157
00158 talloc_free(notify->array);
00159 notify->array = TALLOC_ZERO_P(notify, struct notify_array);
00160 NT_STATUS_HAVE_NO_MEMORY(notify->array);
00161
00162 dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
00163 if (dbuf.dptr == NULL) {
00164 return NT_STATUS_OK;
00165 }
00166
00167 blob.data = (uint8 *)dbuf.dptr;
00168 blob.length = dbuf.dsize;
00169
00170 status = ndr_pull_struct_blob(&blob, notify->array, notify->array,
00171 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
00172
00173 if (DEBUGLEVEL >= 10) {
00174 DEBUG(10, ("notify_load:\n"));
00175 NDR_PRINT_DEBUG(notify_array, notify->array);
00176 }
00177
00178 free(dbuf.dptr);
00179
00180 return status;
00181 }
00182
00183
00184
00185
00186 static int notify_compare(const void *p1, const void *p2)
00187 {
00188 const struct notify_entry *e1 = (const struct notify_entry *)p1;
00189 const struct notify_entry *e2 = (const struct notify_entry *)p2;
00190 return strcmp(e1->path, e2->path);
00191 }
00192
00193
00194
00195
00196 static NTSTATUS notify_save(struct notify_context *notify)
00197 {
00198 TDB_DATA dbuf;
00199 DATA_BLOB blob;
00200 NTSTATUS status;
00201 int ret;
00202 TALLOC_CTX *tmp_ctx;
00203
00204
00205 while (notify->array->num_depths > 0 &&
00206 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
00207 notify->array->num_depths--;
00208 }
00209
00210
00211 if (notify->array->num_depths == 0) {
00212 ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
00213 if (ret != 0) {
00214 return NT_STATUS_INTERNAL_DB_CORRUPTION;
00215 }
00216 return NT_STATUS_OK;
00217 }
00218
00219 tmp_ctx = talloc_new(notify);
00220 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
00221
00222 status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
00223 (ndr_push_flags_fn_t)ndr_push_notify_array);
00224 if (!NT_STATUS_IS_OK(status)) {
00225 talloc_free(tmp_ctx);
00226 return status;
00227 }
00228
00229 if (DEBUGLEVEL >= 10) {
00230 DEBUG(10, ("notify_save:\n"));
00231 NDR_PRINT_DEBUG(notify_array, notify->array);
00232 }
00233
00234 dbuf.dptr = (char *)blob.data;
00235 dbuf.dsize = blob.length;
00236
00237 ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
00238 talloc_free(tmp_ctx);
00239 if (ret != 0) {
00240 return NT_STATUS_INTERNAL_DB_CORRUPTION;
00241 }
00242
00243 return NT_STATUS_OK;
00244 }
00245
00246
00247
00248
00249
00250 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
00251 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
00252 {
00253 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
00254 NTSTATUS status;
00255 struct notify_event ev;
00256 TALLOC_CTX *tmp_ctx = talloc_new(notify);
00257 struct notify_list *listel;
00258
00259 if (tmp_ctx == NULL) {
00260 return;
00261 }
00262
00263 status = ndr_pull_struct_blob(data, tmp_ctx, &ev,
00264 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
00265 if (!NT_STATUS_IS_OK(status)) {
00266 talloc_free(tmp_ctx);
00267 return;
00268 }
00269
00270 for (listel=notify->list;listel;listel=listel->next) {
00271 if (listel->private_data == ev.private_data) {
00272 listel->callback(listel->private_data, &ev);
00273 break;
00274 }
00275 }
00276
00277 talloc_free(tmp_ctx);
00278 }
00279
00280
00281
00282
00283 static void sys_notify_callback(struct sys_notify_context *ctx,
00284 void *ptr, struct notify_event *ev)
00285 {
00286 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
00287 ev->private_data = listel;
00288 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
00289 ev->action, ev->path));
00290 listel->callback(listel->private_data, ev);
00291 }
00292
00293
00294
00295
00296 static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
00297 void *private_data, int depth)
00298 {
00299 int i;
00300 struct notify_depth *d;
00301 struct notify_entry *ee;
00302
00303
00304 if (depth >= notify->array->num_depths) {
00305 d = talloc_realloc(notify->array, notify->array->depth,
00306 struct notify_depth, depth+1);
00307 NT_STATUS_HAVE_NO_MEMORY(d);
00308 for (i=notify->array->num_depths;i<=depth;i++) {
00309 ZERO_STRUCT(d[i]);
00310 }
00311 notify->array->depth = d;
00312 notify->array->num_depths = depth+1;
00313 }
00314 d = ¬ify->array->depth[depth];
00315
00316
00317 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
00318 d->num_entries+1);
00319 NT_STATUS_HAVE_NO_MEMORY(ee);
00320 d->entries = ee;
00321
00322 d->entries[d->num_entries] = *e;
00323 d->entries[d->num_entries].private_data = private_data;
00324 d->entries[d->num_entries].server = notify->server;
00325 d->entries[d->num_entries].path_len = strlen(e->path);
00326 d->num_entries++;
00327
00328 d->max_mask |= e->filter;
00329 d->max_mask_subdir |= e->subdir_filter;
00330
00331 if (d->num_entries > 1) {
00332 qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
00333 }
00334
00335
00336 d->max_mask = 0;
00337 d->max_mask_subdir = 0;
00338
00339 for (i=0;i<d->num_entries;i++) {
00340 d->max_mask |= d->entries[i].filter;
00341 d->max_mask_subdir |= d->entries[i].subdir_filter;
00342 }
00343
00344 return notify_save(notify);
00345 }
00346
00347
00348
00349
00350
00351 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
00352 void (*callback)(void *, const struct notify_event *),
00353 void *private_data)
00354 {
00355 struct notify_entry e = *e0;
00356 NTSTATUS status;
00357 char *tmp_path = NULL;
00358 struct notify_list *listel;
00359 size_t len;
00360 int depth;
00361
00362
00363 if (notify == NULL) {
00364 return NT_STATUS_NOT_IMPLEMENTED;
00365 }
00366
00367 status = notify_lock(notify);
00368 NT_STATUS_NOT_OK_RETURN(status);
00369
00370 status = notify_load(notify);
00371 if (!NT_STATUS_IS_OK(status)) {
00372 goto done;
00373 }
00374
00375
00376 len = strlen(e.path);
00377 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
00378 tmp_path = talloc_strndup(notify, e.path, len-2);
00379 if (tmp_path == NULL) {
00380 status = NT_STATUS_NO_MEMORY;
00381 goto done;
00382 }
00383 e.path = tmp_path;
00384 }
00385
00386 depth = count_chars(e.path, '/');
00387
00388 listel = TALLOC_ZERO_P(notify, struct notify_list);
00389 if (listel == NULL) {
00390 status = NT_STATUS_NO_MEMORY;
00391 goto done;
00392 }
00393
00394 listel->private_data = private_data;
00395 listel->callback = callback;
00396 listel->depth = depth;
00397 DLIST_ADD(notify->list, listel);
00398
00399
00400 if (notify->sys_notify_ctx != NULL) {
00401
00402
00403
00404
00405 status = sys_notify_watch(notify->sys_notify_ctx, &e,
00406 sys_notify_callback, listel,
00407 &listel->sys_notify_handle);
00408 if (NT_STATUS_IS_OK(status)) {
00409 talloc_steal(listel, listel->sys_notify_handle);
00410 }
00411 }
00412
00413
00414
00415
00416
00417 if (e.filter != 0 || e.subdir_filter != 0) {
00418 status = notify_add_array(notify, &e, private_data, depth);
00419 }
00420
00421 done:
00422 notify_unlock(notify);
00423 talloc_free(tmp_path);
00424
00425 return status;
00426 }
00427
00428
00429
00430
00431 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
00432 {
00433 NTSTATUS status;
00434 struct notify_list *listel;
00435 int i, depth;
00436 struct notify_depth *d;
00437
00438
00439 if (notify == NULL) {
00440 return NT_STATUS_NOT_IMPLEMENTED;
00441 }
00442
00443 for (listel=notify->list;listel;listel=listel->next) {
00444 if (listel->private_data == private_data) {
00445 DLIST_REMOVE(notify->list, listel);
00446 break;
00447 }
00448 }
00449 if (listel == NULL) {
00450 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
00451 }
00452
00453 depth = listel->depth;
00454
00455 talloc_free(listel);
00456
00457 status = notify_lock(notify);
00458 NT_STATUS_NOT_OK_RETURN(status);
00459
00460 status = notify_load(notify);
00461 if (!NT_STATUS_IS_OK(status)) {
00462 notify_unlock(notify);
00463 return status;
00464 }
00465
00466 if (depth >= notify->array->num_depths) {
00467 notify_unlock(notify);
00468 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
00469 }
00470
00471
00472 d = ¬ify->array->depth[depth];
00473
00474 for (i=0;i<d->num_entries;i++) {
00475 if (private_data == d->entries[i].private_data &&
00476 cluster_id_equal(¬ify->server, &d->entries[i].server)) {
00477 break;
00478 }
00479 }
00480 if (i == d->num_entries) {
00481 notify_unlock(notify);
00482 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
00483 }
00484
00485 if (i < d->num_entries-1) {
00486 memmove(&d->entries[i], &d->entries[i+1],
00487 sizeof(d->entries[i])*(d->num_entries-(i+1)));
00488 }
00489 d->num_entries--;
00490
00491 status = notify_save(notify);
00492
00493 notify_unlock(notify);
00494
00495 return status;
00496 }
00497
00498
00499
00500
00501 static NTSTATUS notify_remove_all(struct notify_context *notify,
00502 const struct server_id *server)
00503 {
00504 NTSTATUS status;
00505 int i, depth, del_count=0;
00506
00507 status = notify_lock(notify);
00508 NT_STATUS_NOT_OK_RETURN(status);
00509
00510 status = notify_load(notify);
00511 if (!NT_STATUS_IS_OK(status)) {
00512 notify_unlock(notify);
00513 return status;
00514 }
00515
00516
00517
00518 for (depth=0;depth<notify->array->num_depths;depth++) {
00519 struct notify_depth *d = ¬ify->array->depth[depth];
00520 for (i=0;i<d->num_entries;i++) {
00521 if (cluster_id_equal(server, &d->entries[i].server)) {
00522 if (i < d->num_entries-1) {
00523 memmove(&d->entries[i], &d->entries[i+1],
00524 sizeof(d->entries[i])*(d->num_entries-(i+1)));
00525 }
00526 i--;
00527 d->num_entries--;
00528 del_count++;
00529 }
00530 }
00531 }
00532
00533 if (del_count > 0) {
00534 status = notify_save(notify);
00535 }
00536
00537 notify_unlock(notify);
00538
00539 return status;
00540 }
00541
00542
00543
00544
00545
00546 static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
00547 const char *path, uint32_t action)
00548 {
00549 struct notify_event ev;
00550 DATA_BLOB data;
00551 NTSTATUS status;
00552 TALLOC_CTX *tmp_ctx;
00553
00554 ev.action = action;
00555 ev.path = path;
00556 ev.private_data = e->private_data;
00557
00558 tmp_ctx = talloc_new(notify);
00559
00560 status = ndr_push_struct_blob(&data, tmp_ctx, &ev,
00561 (ndr_push_flags_fn_t)ndr_push_notify_event);
00562 if (!NT_STATUS_IS_OK(status)) {
00563 talloc_free(tmp_ctx);
00564 return status;
00565 }
00566
00567 status = messaging_send(notify->messaging_ctx, e->server,
00568 MSG_PVFS_NOTIFY, &data);
00569 talloc_free(tmp_ctx);
00570 return status;
00571 }
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581 void notify_trigger(struct notify_context *notify,
00582 uint32_t action, uint32_t filter, const char *path)
00583 {
00584 NTSTATUS status;
00585 int depth;
00586 const char *p, *next_p;
00587
00588 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
00589 "path=%s\n", (unsigned)action, (unsigned)filter, path));
00590
00591
00592 if (notify == NULL) {
00593 return;
00594 }
00595
00596 again:
00597 status = notify_load(notify);
00598 if (!NT_STATUS_IS_OK(status)) {
00599 return;
00600 }
00601
00602
00603 for (depth=0,p=path;
00604 p && depth < notify->array->num_depths;
00605 p=next_p,depth++) {
00606 int p_len = p - path;
00607 int min_i, max_i, i;
00608 struct notify_depth *d = ¬ify->array->depth[depth];
00609 next_p = strchr(p+1, '/');
00610
00611
00612 if (d->num_entries == 0) continue;
00613
00614
00615
00616
00617 if (next_p != NULL) {
00618 if (0 == (filter & d->max_mask_subdir)) {
00619 continue;
00620 }
00621 } else {
00622 if (0 == (filter & d->max_mask)) {
00623 continue;
00624 }
00625 }
00626
00627
00628
00629
00630 min_i = 0;
00631 max_i = d->num_entries-1;
00632
00633 while (min_i < max_i) {
00634 struct notify_entry *e;
00635 int cmp;
00636 i = (min_i+max_i)/2;
00637 e = &d->entries[i];
00638 cmp = strncmp(path, e->path, p_len);
00639 if (cmp == 0) {
00640 if (p_len == e->path_len) {
00641 max_i = i;
00642 } else {
00643 max_i = i-1;
00644 }
00645 } else if (cmp < 0) {
00646 max_i = i-1;
00647 } else {
00648 min_i = i+1;
00649 }
00650 }
00651
00652 if (min_i != max_i) {
00653
00654 continue;
00655 }
00656
00657
00658 for (i=min_i;i<d->num_entries;i++) {
00659 struct notify_entry *e = &d->entries[i];
00660 if (p_len != e->path_len ||
00661 strncmp(path, e->path, p_len) != 0) break;
00662 if (next_p != NULL) {
00663 if (0 == (filter & e->subdir_filter)) {
00664 continue;
00665 }
00666 } else {
00667 if (0 == (filter & e->filter)) {
00668 continue;
00669 }
00670 }
00671 status = notify_send(notify, e, path + e->path_len + 1,
00672 action);
00673
00674 if (NT_STATUS_EQUAL(
00675 status, NT_STATUS_INVALID_HANDLE)) {
00676 struct server_id server = e->server;
00677
00678 DEBUG(10, ("Deleting notify entries for "
00679 "process %s because it's gone\n",
00680 procid_str_static(&e->server.id)));
00681 notify_remove_all(notify, &server);
00682 goto again;
00683 }
00684 }
00685 }
00686 }