static void s_check_directory (s_agent_t *self) { // Get latest snapshot and build a patches list for any changes // All patches are built using a virtual path starting at "/" zdir_t *dir = zdir_new (self->path, NULL); zlist_t *patches = zdir_diff (self->dir, dir, "/"); // Drop old directory and replace with latest version zdir_destroy (&self->dir); self->dir = dir; while (zlist_size (patches)) { zdir_patch_t *patch = (zdir_patch_t *) zlist_pop (patches); if (zdir_patch_op (patch) == patch_create) { // Shout new files to DROPS group // Stupidest possible approach: send whole file as one frame // Truncate file at arbitrary limit of 10MB zfile_t *file = zdir_patch_file (patch); if (zfile_input (file) == 0) { zchunk_t *chunk = zfile_read (file, 10 * 1024 * 1024, 0); assert (chunk); zmsg_t *msg = zmsg_new (); zmsg_addstr (msg, "CREATE"); zmsg_addstr (msg, zdir_patch_vpath (patch)); zmsg_add (msg, zframe_new (zchunk_data (chunk), zchunk_size (chunk))); zchunk_destroy (&chunk); zyre_shout (self->zyre, "DROPS", &msg); } } zdir_patch_destroy (&patch); } zlist_destroy (&patches); }
void zdir_patch_test (bool verbose) { printf (" * zdir_patch: "); // @selftest zfile_t *file = zfile_new (".", "bilbo"); assert (file); zdir_patch_t *patch = zdir_patch_new (".", file, patch_create, "/"); assert (patch); zfile_destroy (&file); file = zdir_patch_file (patch); assert (file); assert (streq (zfile_filename (file, "."), "bilbo")); assert (streq (zdir_patch_vpath (patch), "/bilbo")); zdir_patch_destroy (&patch); #if defined (__WINDOWS__) zsys_shutdown(); #endif // @end printf ("OK\n"); }
static int s_on_read_timer (zloop_t *loop, int timer_id, void *arg) { zdir_watch_t *watch = (zdir_watch_t *) arg; void *data; for (data = zhash_first (watch->subs); data != NULL; data = zhash_next (watch->subs)) { zdir_watch_sub_t *sub = (zdir_watch_sub_t *) data; zdir_t *new_dir = zdir_new (zdir_path (sub->dir), NULL); if (!new_dir) { if (watch->verbose) zsys_error ("zdir_watch: Unable to create new zdir for path %s", zdir_path (sub->dir)); continue; } // Determine if anything has changed. zlist_t *diff = zdir_diff (sub->dir, new_dir, ""); // Do memory management before error handling... zdir_destroy (&sub->dir); sub->dir = new_dir; if (!diff) { if (watch->verbose) zsys_error ("zdir_watch: Unable to create diff for path %s", zdir_path (sub->dir)); continue; } if (zlist_size (diff) > 0) { if (watch->verbose) { zdir_patch_t *patch = (zdir_patch_t *) zlist_first (diff); zsys_info ("zdir_watch: Found %d changes in %s:", zlist_size (diff), zdir_path (sub->dir)); while (patch) { zsys_info ("zdir_watch: %s %s", zfile_filename (zdir_patch_file (patch), NULL), zdir_patch_op (patch) == ZDIR_PATCH_CREATE? "created": "deleted"); patch = (zdir_patch_t *) zlist_next (diff); } } if (zsock_send (watch->pipe, "sp", zdir_path (sub->dir), diff) != 0) { if (watch->verbose) zsys_error ("zdir_watch: Unable to send patch list for path %s", zdir_path (sub->dir)); zlist_destroy (&diff); } // Successfully sent `diff` list - now owned by receiver } else { zlist_destroy (&diff); } } return 0; }
// -------------------------------------------------------------------------- // Self test of this class int zdir_patch_test (bool verbose) { printf (" * zdir_patch: "); // @selftest zfile_t *file = zfile_new (".", "bilbo"); zdir_patch_t *patch = zdir_patch_new (".", file, patch_create, "/"); zfile_destroy (&file); file = zdir_patch_file (patch); assert (streq (zfile_filename (file, "."), "bilbo")); assert (streq (zdir_patch_vpath (patch), "/bilbo")); zdir_patch_destroy (&patch); // @end printf ("OK\n"); return 0; }
static void get_next_patch_for_client (server_t *self, client_t *client) { // Get next patch for client if we're not doing one already if (client->patch == NULL) client->patch = (zdir_patch_t *) zlist_pop (client->patches); if (client->patch == NULL) { client->next_event = finished_event; return; } // Get virtual path from patch fmq_msg_set_filename (client->reply, zdir_patch_vpath (client->patch)); // We can process a delete patch right away if (zdir_patch_op (client->patch) == patch_delete) { fmq_msg_set_sequence (client->reply, client->sequence++); fmq_msg_set_operation (client->reply, FMQ_MSG_FILE_DELETE); client->next_event = send_delete_event; // No reliability in this version, assume patch delivered safely zdir_patch_destroy (&client->patch); } else if (zdir_patch_op (client->patch) == patch_create) { // Create patch refers to file, open that for input if needed if (client->file == NULL) { client->file = zfile_dup (zdir_patch_file (client->patch)); if (zfile_input (client->file)) { // File no longer available, skip it zdir_patch_destroy (&client->patch); zfile_destroy (&client->file); client->next_event = next_patch_event; return; } client->offset = 0; } // Get next chunk for file zchunk_t *chunk = zfile_read (client->file, CHUNK_SIZE, client->offset); assert (chunk); // Check if we have the credit to send chunk if (zchunk_size (chunk) <= client->credit) { fmq_msg_set_sequence (client->reply, client->sequence++); fmq_msg_set_operation (client->reply, FMQ_MSG_FILE_CREATE); fmq_msg_set_offset (client->reply, client->offset); fmq_msg_set_chunk (client->reply, zframe_new ( zchunk_data (chunk), zchunk_size (chunk))); client->offset += zchunk_size (chunk); client->credit -= zchunk_size (chunk); client->next_event = send_chunk_event; // Zero-sized chunk means end of file if (zchunk_size (chunk) == 0) { zfile_destroy (&client->file); zdir_patch_destroy (&client->patch); } } else client->next_event = no_credit_event; zchunk_destroy (&chunk); } }
/// // Return patch file item QZfile * QZdirPatch::file () { QZfile *rv = new QZfile (zdir_patch_file (self)); return rv; }
void zdir_test (bool verbose) { printf (" * zdir: "); // @selftest // need to create a file in the test directory we're watching // in order to ensure the directory exists zfile_t *initfile = zfile_new ("./zdir-test-dir", "initial_file"); assert (initfile); zfile_output (initfile); fprintf (zfile_handle (initfile), "initial file\n"); zfile_close (initfile); zfile_destroy (&initfile); zdir_t *older = zdir_new ("zdir-test-dir", NULL); assert (older); if (verbose) { printf ("\n"); zdir_dump (older, 0); } zdir_t *newer = zdir_new (".", NULL); assert (newer); zlist_t *patches = zdir_diff (older, newer, "/"); assert (patches); while (zlist_size (patches)) { zdir_patch_t *patch = (zdir_patch_t *) zlist_pop (patches); zdir_patch_destroy (&patch); } zlist_destroy (&patches); zdir_destroy (&older); zdir_destroy (&newer); zdir_t *nosuch = zdir_new ("does-not-exist", NULL); assert (nosuch == NULL); // zdir_watch test: zactor_t *watch = zactor_new (zdir_watch, NULL); assert (watch); int synced; if (verbose) { zsock_send (watch, "s", "VERBOSE"); synced = zsock_wait(watch); assert ( synced == 0); } zclock_sleep (1001); // wait for initial file to become 'stable' zsock_send (watch, "si", "TIMEOUT", 100); synced = zsock_wait(watch); assert (synced == 0); zsock_send (watch, "ss", "SUBSCRIBE", "zdir-test-dir"); synced = zsock_wait(watch); assert(synced == 0); zsock_send (watch, "ss", "UNSUBSCRIBE", "zdir-test-dir"); synced = zsock_wait(watch); assert(synced == 0); zsock_send (watch, "ss", "SUBSCRIBE", "zdir-test-dir"); synced = zsock_wait(watch); assert(synced == 0); zfile_t *newfile = zfile_new ("zdir-test-dir", "test_abc"); zfile_output (newfile); fprintf (zfile_handle (newfile), "test file\n"); zfile_close (newfile); zpoller_t *watch_poll = zpoller_new (watch, NULL); // poll for a certain timeout before giving up and failing the test. void* polled = zpoller_wait(watch_poll, 1001); assert (polled == watch); // wait for notification of the file being added char *path; int rc = zsock_recv (watch, "sp", &path, &patches); assert (rc == 0); assert (streq (path, "zdir-test-dir")); freen (path); assert (zlist_size (patches) == 1); zdir_patch_t *patch = (zdir_patch_t *) zlist_pop (patches); assert (streq (zdir_patch_path (patch), "zdir-test-dir")); zfile_t *patch_file = zdir_patch_file (patch); assert (streq (zfile_filename (patch_file, ""), "zdir-test-dir/test_abc")); zdir_patch_destroy (&patch); zlist_destroy (&patches); // remove the file zfile_remove (newfile); zfile_destroy (&newfile); // poll for a certain timeout before giving up and failing the test. polled = zpoller_wait(watch_poll, 1001); assert (polled == watch); // wait for notification of the file being removed rc = zsock_recv (watch, "sp", &path, &patches); assert (rc == 0); assert (streq (path, "zdir-test-dir")); freen (path); assert (zlist_size (patches) == 1); patch = (zdir_patch_t *) zlist_pop (patches); assert (streq (zdir_patch_path (patch), "zdir-test-dir")); patch_file = zdir_patch_file (patch); assert (streq (zfile_filename (patch_file, ""), "zdir-test-dir/test_abc")); zdir_patch_destroy (&patch); zlist_destroy (&patches); zpoller_destroy (&watch_poll); zactor_destroy (&watch); // clean up by removing the test directory. zdir_t *testdir = zdir_new ("zdir-test-dir", NULL); zdir_remove (testdir, true); zdir_destroy (&testdir); #if defined (__WINDOWS__) zsys_shutdown(); #endif // @end printf ("OK\n"); }