2 a test implementation of a HSM daemon
4 Andrew Tridgell August 2008
14 unsigned recall_delay;
16 .blocking_wait = true,
28 static struct hsm_store_context *store_ctx;
30 #define SESSION_NAME "hacksmd"
32 /* no special handling on terminate in hacksmd, as we want existing
33 events to stay around so we can continue them on restart */
34 static void hsm_term_handler(int signal)
36 printf("Got signal %d - exiting\n", signal);
42 initialise DMAPI, possibly recovering an existing session. The
43 hacksmd session is never destroyed, to allow for recovery of
44 partially completed events
46 static void hsm_init(void)
48 char *dmapi_version = NULL;
49 dm_eventset_t eventSet;
54 hsm_store_shutdown(store_ctx);
57 store_ctx = hsm_store_init(void);
58 if (store_ctx == NULL) {
59 printf("Unable to open HSM store - %s\n", strerror(errno));
63 while ((ret = dm_init_service(&dmapi_version)) == -1) {
64 if (errno != errcode) {
66 printf("Waiting for DMAPI to initialise (%d: %s)\n",
67 errno, strerror(errno));
72 printf("Initialised DMAPI version '%s'\n", dmapi_version);
74 hsm_recover_session(SESSION_NAME, &dmapi.sid);
76 /* we want mount events only initially */
78 DMEV_SET(DM_EVENT_MOUNT, eventSet);
79 ret = dm_set_disp(dmapi.sid, DM_GLOBAL_HANP, DM_GLOBAL_HLEN, DM_NO_TOKEN,
80 &eventSet, DM_EVENT_MAX);
82 printf("Failed to setup events\n");
89 called on a DM_EVENT_MOUNT event . This just needs to acknowledge
90 the mount. We don't have any sort of 'setup' step before running
91 hacksmd on a filesystem, so it just accepts mount events from any
92 filesystem that supports DMAPI
94 static void hsm_handle_mount(dm_eventmsg_t *msg)
96 dm_mount_event_t *mount;
99 dm_eventset_t eventSet;
102 mount = DM_GET_VALUE(msg, ev_data, dm_mount_event_t*);
103 hand1 = DM_GET_VALUE(mount , me_handle1, void *);
104 hand1len = DM_GET_LEN(mount, me_handle1);
107 DMEV_SET(DM_EVENT_READ, eventSet);
108 DMEV_SET(DM_EVENT_WRITE, eventSet);
109 DMEV_SET(DM_EVENT_TRUNCATE, eventSet);
110 DMEV_SET(DM_EVENT_DESTROY, eventSet);
111 ret = dm_set_eventlist(dmapi.sid, hand1, hand1len,
112 DM_NO_TOKEN, &eventSet, DM_EVENT_MAX);
114 printf("Failed to setup all event handler\n");
118 ret = dm_set_disp(dmapi.sid, hand1, hand1len, DM_NO_TOKEN,
119 &eventSet, DM_EVENT_MAX);
121 printf("Failed to setup disposition for all events\n");
125 ret = dm_respond_event(dmapi.sid, msg->ev_token,
126 DM_RESP_CONTINUE, 0, 0, NULL);
128 printf("Failed to respond to mount event\n");
134 called on a data event from DMAPI. Check the files attribute, and if
135 it is migrated then do a recall
137 static void hsm_handle_recall(dm_eventmsg_t *msg)
143 dm_attrname_t attrname;
144 dm_token_t token = msg->ev_token;
146 dm_boolean_t exactFlag;
150 dm_response_t response = DM_RESP_CONTINUE;
152 struct hsm_store_handle *h;
154 ev = DM_GET_VALUE(msg, ev_data, dm_data_event_t *);
155 hanp = DM_GET_VALUE(ev, de_handle, void *);
156 hlen = DM_GET_LEN(ev, de_handle);
158 memset(attrname.an_chars, 0, DM_ATTR_NAME_SIZE);
159 strncpy((char*)attrname.an_chars, HSM_ATTRNAME, DM_ATTR_NAME_SIZE);
161 /* make sure we have an exclusive right on the file */
162 ret = dm_query_right(dmapi.sid, hanp, hlen, token, &right);
163 if (ret != 0 && errno != ENOENT) {
164 printf("dm_query_right failed - %s\n", strerror(errno));
166 response = DM_RESP_ABORT;
170 if (right != DM_RIGHT_EXCL || errno == ENOENT) {
171 ret = dm_request_right(dmapi.sid, hanp, hlen, token, DM_RR_WAIT, DM_RIGHT_EXCL);
173 printf("dm_request_right failed - %s\n", strerror(errno));
175 response = DM_RESP_ABORT;
180 /* get the attribute from the file, and make sure it is
182 ret = dm_get_dmattr(dmapi.sid, hanp, hlen, token, &attrname,
183 sizeof(h), &h, &rlen);
185 if (errno == ENOENT) {
186 if (options.debug > 2) {
187 printf("File already recalled (no attribute)\n");
191 printf("dm_get_dmattr failed - %s\n", strerror(errno));
193 response = DM_RESP_ABORT;
197 if (rlen != sizeof(h)) {
198 printf("hsm_handle_read - bad attribute size %d\n", (int)rlen);
200 response = DM_RESP_ABORT;
204 if (strncmp(h.magic, HSM_MAGIC, sizeof(h.magic)) != 0) {
205 printf("Bad magic '%*.*s'\n", (int)sizeof(h.magic), (int)sizeof(h.magic),
208 response = DM_RESP_ABORT;
212 /* mark the file as being recalled. This ensures that if
213 hacksmd dies part way through the recall that another
214 migrate won't happen until the recall is completed by a
216 h.state = HSM_STATE_RECALL;
217 ret = dm_set_dmattr(dmapi.sid, hanp, hlen, token, &attrname, 0, sizeof(h), (void*)&h);
219 printf("dm_set_dmattr failed - %s\n", strerror(errno));
221 response = DM_RESP_ABORT;
225 /* get the migrated data from the store, and put it in the
226 file with invisible writes */
227 h = hsm_store_open(store_ctx, h.device, h.inode, O_RDONLY);
229 printf("Failed to open store file for file 0x%llx:0x%llx - %s\n",
230 (unsigned long long)h.device, (unsigned long long)h.inode,
233 response = DM_RESP_ABORT;
237 if (options.debug > 1) {
238 printf("%s %s: Recalling file %llx:%llx of size %d\n",
240 dmapi_event_string(msg->ev_type),
241 (unsigned long long)h.device, (unsigned long long)h.inode,
245 if (options.recall_delay) {
246 sleep(random() % options.recall_delay);
250 while ((ret = hsm_store_read(h, buf, sizeof(buf))) > 0) {
251 int ret2 = dm_write_invis(dmapi.sid, hanp, hlen, token, DM_WRITE_SYNC, ofs, ret, buf);
253 printf("dm_write_invis failed - %s\n", strerror(errno));
255 response = DM_RESP_ABORT;
262 /* remove the attribute from the file - it is now fully recalled */
263 ret = dm_remove_dmattr(dmapi.sid, hanp, hlen, token, 0, &attrname);
265 printf("dm_remove_dmattr failed - %s\n", strerror(errno));
267 response = DM_RESP_ABORT;
271 /* remove the store file */
272 ret = hsm_store_remove(store_ctx, h.device, h.inode);
274 printf("WARNING: Failed to unlink store file\n");
277 /* remove the managed region from the file */
278 ret = dm_set_region(dmapi.sid, hanp, hlen, token, 0, NULL, &exactFlag);
280 printf("failed dm_set_region - %s\n", strerror(errno));
282 response = DM_RESP_ABORT;
287 /* tell the kernel that the event has been handled */
288 ret = dm_respond_event(dmapi.sid, msg->ev_token,
289 response, retcode, 0, NULL);
291 printf("Failed to respond to read event\n");
298 called on a DM_EVENT_DESTROY event, when a file is being deleted
300 static void hsm_handle_destroy(dm_eventmsg_t *msg)
302 dm_destroy_event_t *ev;
306 dm_attrname_t attrname;
307 dm_token_t token = msg->ev_token;
310 dm_response_t response = DM_RESP_CONTINUE;
312 dm_boolean_t exactFlag;
314 ev = DM_GET_VALUE(msg, ev_data, dm_destroy_event_t *);
315 hanp = DM_GET_VALUE(ev, ds_handle, void *);
316 hlen = DM_GET_LEN(ev, ds_handle);
318 if (DM_TOKEN_EQ(token, DM_INVALID_TOKEN)) {
322 /* make sure we have an exclusive lock on the file */
323 ret = dm_query_right(dmapi.sid, hanp, hlen, token, &right);
324 if (ret != 0 && errno != ENOENT) {
325 printf("dm_query_right failed - %s\n", strerror(errno));
327 response = DM_RESP_ABORT;
331 if (right != DM_RIGHT_EXCL || errno == ENOENT) {
332 ret = dm_request_right(dmapi.sid, hanp, hlen, token, DM_RR_WAIT, DM_RIGHT_EXCL);
334 printf("dm_request_right failed - %s\n", strerror(errno));
336 response = DM_RESP_ABORT;
341 memset(attrname.an_chars, 0, DM_ATTR_NAME_SIZE);
342 strncpy((char*)attrname.an_chars, HSM_ATTRNAME, DM_ATTR_NAME_SIZE);
344 /* get the attribute and check it is valid. This is just
345 paranoia really, as the file is going away */
346 ret = dm_get_dmattr(dmapi.sid, hanp, hlen, token, &attrname,
347 sizeof(h), &h, &rlen);
349 printf("WARNING: dm_get_dmattr failed - %s\n", strerror(errno));
353 if (rlen != sizeof(h)) {
354 printf("hsm_handle_read - bad attribute size %d\n", (int)rlen);
356 response = DM_RESP_ABORT;
360 if (strncmp(h.magic, HSM_MAGIC, sizeof(h.magic)) != 0) {
361 printf("Bad magic '%*.*s'\n", (int)sizeof(h.magic), (int)sizeof(h.magic), h.magic);
363 response = DM_RESP_ABORT;
367 if (options.debug > 1) {
368 printf("%s: Destroying file %llx:%llx of size %d\n",
369 dmapi_event_string(msg->ev_type),
370 (unsigned long long)h.device, (unsigned long long)h.inode,
374 /* remove the store file */
375 ret = hsm_store_remove(store_ctx, h.device, h.inode);
377 printf("WARNING: Failed to unlink store file for file 0x%llx:0x%llx\n",
378 (unsigned long long)h.device, (unsigned long long)h.inode);
381 /* remove the attribute */
382 ret = dm_remove_dmattr(dmapi.sid, hanp, hlen, token, 0, &attrname);
384 printf("dm_remove_dmattr failed - %s\n", strerror(errno));
386 response = DM_RESP_ABORT;
390 /* and clear the managed region */
391 ret = dm_set_region(dmapi.sid, hanp, hlen, token, 0, NULL, &exactFlag);
393 printf("WARNING: failed dm_set_region - %s\n", strerror(errno));
397 /* only respond if the token is real */
398 if (!DM_TOKEN_EQ(msg->ev_token,DM_NO_TOKEN) &&
399 !DM_TOKEN_EQ(msg->ev_token, DM_INVALID_TOKEN)) {
400 ret = dm_respond_event(dmapi.sid, msg->ev_token,
401 response, retcode, 0, NULL);
403 printf("Failed to respond to destroy event\n");
410 main switch for DMAPI messages
412 static void hsm_handle_message(dm_eventmsg_t *msg)
414 switch (msg->ev_type) {
416 hsm_handle_mount(msg);
420 hsm_handle_recall(msg);
422 case DM_EVENT_DESTROY:
423 hsm_handle_destroy(msg);
426 if (!DM_TOKEN_EQ(msg->ev_token,DM_NO_TOKEN) &&
427 !DM_TOKEN_EQ(msg->ev_token, DM_INVALID_TOKEN)) {
428 printf("Giving default response\n");
429 int ret = dm_respond_event(dmapi.sid, msg->ev_token,
430 DM_RESP_CONTINUE, 0, 0, NULL);
432 printf("Failed to respond to mount event\n");
441 wait for DMAPI events to come in and dispatch them
443 static void hsm_wait_events(void)
449 printf("Waiting for events\n");
453 if (options.blocking_wait) {
454 ret = dm_get_events(dmapi.sid, 0, DM_EV_WAIT, sizeof(buf), buf, &rlen);
456 /* optionally don't use DM_RR_WAIT to ensure
457 that the daemon can be killed. This is only
458 needed because GPFS uses an uninterruptible
459 sleep for dm_get_events with DM_EV_WAIT. It
460 should be an interruptible sleep */
462 ret = dm_get_events(dmapi.sid, 0, 0, sizeof(buf), buf, &rlen);
465 if (errno == EAGAIN) continue;
466 if (errno == ESTALE) {
467 printf("DMAPI service has shutdown - restarting\n");
471 printf("Failed to get event (%s)\n", strerror(errno));
475 /* loop over all the messages we received */
476 for (msg=(dm_eventmsg_t *)buf;
478 msg = DM_STEP_TO_NEXT(msg, dm_eventmsg_t *)) {
479 /* optionally fork on each message, thus
480 giving parallelism and allowing us to delay
481 recalls, simulating slow tape speeds */
482 if (options.use_fork) {
483 if (fork() != 0) continue;
484 srandom(getpid() ^ time(NULL));
485 hsm_handle_message(msg);
488 hsm_handle_message(msg);
495 on startup we look for partially completed events from an earlier
496 instance of hacksmd, and continue them if we can
498 static void hsm_cleanup_events(void)
502 dm_token_t *tok = NULL;
508 ret = dm_getall_tokens(dmapi.sid, n, tok, &n2);
509 if (ret == -1 && errno == E2BIG) {
511 tok = realloc(tok, sizeof(dm_token_t)*n);
515 printf("dm_getall_tokens - %s\n", strerror(errno));
518 if (ret == 0 && n2 == 0) {
521 printf("Cleaning up %u tokens\n", n2);
524 /* get the message associated with this token
525 back from the kernel */
526 ret = dm_find_eventmsg(dmapi.sid, tok[i], sizeof(buf), buf, &rlen);
528 printf("Unable to find message for token in cleanup\n");
531 msg = (dm_eventmsg_t *)buf;
532 /* there seems to be a bug where GPFS
533 sometimes gives us a garbage token here */
534 if (!DM_TOKEN_EQ(tok[i], msg->ev_token)) {
535 printf("Message token mismatch in cleanup\n");
536 dm_respond_event(dmapi.sid, tok[i],
537 DM_RESP_ABORT, EINTR, 0, NULL);
539 unsigned saved_delay = options.recall_delay;
540 options.recall_delay = 0;
541 hsm_handle_message(msg);
542 options.recall_delay = saved_delay;
552 static void usage(void)
554 printf("Usage: hacksmd <options>\n");
555 printf("\n\tOptions:\n");
556 printf("\t\t -c cleanup lost tokens\n");
557 printf("\t\t -N use a non-blocking event wait\n");
558 printf("\t\t -d level choose debug level\n");
559 printf("\t\t -F fork to handle each event\n");
560 printf("\t\t -R delay set a random delay on recall up to 'delay' seconds\n");
565 int main(int argc, char * const argv[])
568 bool cleanup = false;
570 /* parse command-line options */
571 while ((opt = getopt(argc, argv, "chNd:FR:")) != -1) {
577 options.debug = strtoul(optarg, NULL, 0);
580 options.recall_delay = strtoul(optarg, NULL, 0);
583 options.blocking_wait = false;
586 options.use_fork = true;
600 signal(SIGCHLD, SIG_IGN);
602 signal(SIGTERM, hsm_term_handler);
603 signal(SIGINT, hsm_term_handler);
608 hsm_cleanup_tokens(dmapi.sid, DM_RESP_ABORT, EINTR);
612 hsm_cleanup_events();