2 a test implementation of a HSM daemon
4 Andrew Tridgell August 2008
14 unsigned recall_delay;
16 .blocking_wait = true,
28 #define SESSION_NAME "hacksmd"
30 /* no special handling on terminate in hacksmd, as we want existing
31 events to stay around so we can continue them on restart */
32 static void hsm_term_handler(int signal)
34 printf("Got signal %d - exiting\n", signal);
40 initialise DMAPI, possibly recovering an existing session. The
41 hacksmd session is never destroyed, to allow for recovery of
42 partially completed events
44 static void hsm_init(void)
46 char *dmapi_version = NULL;
47 dm_eventset_t eventSet;
51 while ((ret = dm_init_service(&dmapi_version)) == -1) {
52 if (errno != errcode) {
54 printf("Waiting for DMAPI to initialise (%d: %s)\n",
55 errno, strerror(errno));
60 printf("Initialised DMAPI version '%s'\n", dmapi_version);
62 hsm_recover_session(SESSION_NAME, &dmapi.sid);
64 /* we want mount events only initially */
66 DMEV_SET(DM_EVENT_MOUNT, eventSet);
67 ret = dm_set_disp(dmapi.sid, DM_GLOBAL_HANP, DM_GLOBAL_HLEN, DM_NO_TOKEN,
68 &eventSet, DM_EVENT_MAX);
70 printf("Failed to setup events\n");
77 called on a DM_EVENT_MOUNT event . This just needs to acknowledge
78 the mount. We don't have any sort of 'setup' step before running
79 hacksmd on a filesystem, so it just accepts mount events from any
80 filesystem that supports DMAPI
82 static void hsm_handle_mount(dm_eventmsg_t *msg)
84 dm_mount_event_t *mount;
87 dm_eventset_t eventSet;
90 mount = DM_GET_VALUE(msg, ev_data, dm_mount_event_t*);
91 hand1 = DM_GET_VALUE(mount , me_handle1, void *);
92 hand1len = DM_GET_LEN(mount, me_handle1);
95 DMEV_SET(DM_EVENT_READ, eventSet);
96 DMEV_SET(DM_EVENT_WRITE, eventSet);
97 DMEV_SET(DM_EVENT_TRUNCATE, eventSet);
98 DMEV_SET(DM_EVENT_DESTROY, eventSet);
99 ret = dm_set_eventlist(dmapi.sid, hand1, hand1len,
100 DM_NO_TOKEN, &eventSet, DM_EVENT_MAX);
102 printf("Failed to setup all event handler\n");
106 ret = dm_set_disp(dmapi.sid, hand1, hand1len, DM_NO_TOKEN,
107 &eventSet, DM_EVENT_MAX);
109 printf("Failed to setup disposition for all events\n");
113 ret = dm_respond_event(dmapi.sid, msg->ev_token,
114 DM_RESP_CONTINUE, 0, 0, NULL);
116 printf("Failed to respond to mount event\n");
122 called on a data event from DMAPI. Check the files attribute, and if
123 it is migrated then do a recall
125 static void hsm_handle_recall(dm_eventmsg_t *msg)
131 dm_attrname_t attrname;
132 dm_token_t token = msg->ev_token;
134 dm_boolean_t exactFlag;
139 dm_response_t response = DM_RESP_CONTINUE;
142 ev = DM_GET_VALUE(msg, ev_data, dm_data_event_t *);
143 hanp = DM_GET_VALUE(ev, de_handle, void *);
144 hlen = DM_GET_LEN(ev, de_handle);
146 memset(attrname.an_chars, 0, DM_ATTR_NAME_SIZE);
147 strncpy((char*)attrname.an_chars, HSM_ATTRNAME, DM_ATTR_NAME_SIZE);
149 /* make sure we have an exclusive right on the file */
150 ret = dm_query_right(dmapi.sid, hanp, hlen, token, &right);
151 if (ret != 0 && errno != ENOENT) {
152 printf("dm_query_right failed - %s\n", strerror(errno));
154 response = DM_RESP_ABORT;
158 if (right != DM_RIGHT_EXCL || errno == ENOENT) {
159 ret = dm_request_right(dmapi.sid, hanp, hlen, token, DM_RR_WAIT, DM_RIGHT_EXCL);
161 printf("dm_request_right failed - %s\n", strerror(errno));
163 response = DM_RESP_ABORT;
168 /* get the attribute from the file, and make sure it is
170 ret = dm_get_dmattr(dmapi.sid, hanp, hlen, token, &attrname,
171 sizeof(h), &h, &rlen);
173 if (errno == ENOENT) {
174 if (options.debug > 2) {
175 printf("File already recalled (no attribute)\n");
179 printf("dm_get_dmattr failed - %s\n", strerror(errno));
181 response = DM_RESP_ABORT;
185 if (rlen != sizeof(h)) {
186 printf("hsm_handle_read - bad attribute size %d\n", (int)rlen);
188 response = DM_RESP_ABORT;
192 if (strncmp(h.magic, HSM_MAGIC, sizeof(h.magic)) != 0) {
193 printf("Bad magic '%*.*s'\n", (int)sizeof(h.magic), (int)sizeof(h.magic),
196 response = DM_RESP_ABORT;
200 /* mark the file as being recalled. This ensures that if
201 hacksmd dies part way through the recall that another
202 migrate won't happen until the recall is completed by a
204 h.state = HSM_STATE_RECALL;
205 ret = dm_set_dmattr(dmapi.sid, hanp, hlen, token, &attrname, 0, sizeof(h), (void*)&h);
207 printf("dm_set_dmattr failed - %s\n", strerror(errno));
209 response = DM_RESP_ABORT;
213 /* get the migrated data from the store, and put it in the
214 file with invisible writes */
215 fd = hsm_store_open(h.device, h.inode, O_RDONLY);
217 printf("Failed to open store file for file 0x%llx:0x%llx\n",
218 (unsigned long long)h.device, (unsigned long long)h.inode);
220 response = DM_RESP_ABORT;
224 if (options.debug > 1) {
225 printf("%s %s: Recalling file %llx:%llx of size %d\n",
227 dmapi_event_string(msg->ev_type),
228 (unsigned long long)h.device, (unsigned long long)h.inode,
232 if (options.recall_delay) {
233 sleep(random() % options.recall_delay);
237 while ((ret = read(fd, buf, sizeof(buf))) > 0) {
238 int ret2 = dm_write_invis(dmapi.sid, hanp, hlen, token, DM_WRITE_SYNC, ofs, ret, buf);
240 printf("dm_write_invis failed - %s\n", strerror(errno));
242 response = DM_RESP_ABORT;
249 /* remove the attribute from the file - it is now fully recalled */
250 ret = dm_remove_dmattr(dmapi.sid, hanp, hlen, token, 0, &attrname);
252 printf("dm_remove_dmattr failed - %s\n", strerror(errno));
254 response = DM_RESP_ABORT;
258 /* remove the store file */
259 ret = hsm_store_unlink(h.device, h.inode);
261 printf("WARNING: Failed to unlink store file\n");
264 /* remove the managed region from the file */
265 ret = dm_set_region(dmapi.sid, hanp, hlen, token, 0, NULL, &exactFlag);
267 printf("failed dm_set_region - %s\n", strerror(errno));
269 response = DM_RESP_ABORT;
274 /* tell the kernel that the event has been handled */
275 ret = dm_respond_event(dmapi.sid, msg->ev_token,
276 response, retcode, 0, NULL);
278 printf("Failed to respond to read event\n");
285 called on a DM_EVENT_DESTROY event, when a file is being deleted
287 static void hsm_handle_destroy(dm_eventmsg_t *msg)
289 dm_destroy_event_t *ev;
293 dm_attrname_t attrname;
294 dm_token_t token = msg->ev_token;
297 dm_response_t response = DM_RESP_CONTINUE;
299 dm_boolean_t exactFlag;
301 ev = DM_GET_VALUE(msg, ev_data, dm_destroy_event_t *);
302 hanp = DM_GET_VALUE(ev, ds_handle, void *);
303 hlen = DM_GET_LEN(ev, ds_handle);
305 if (DM_TOKEN_EQ(token, DM_INVALID_TOKEN)) {
309 /* make sure we have an exclusive lock on the file */
310 ret = dm_query_right(dmapi.sid, hanp, hlen, token, &right);
311 if (ret != 0 && errno != ENOENT) {
312 printf("dm_query_right failed - %s\n", strerror(errno));
314 response = DM_RESP_ABORT;
318 if (right != DM_RIGHT_EXCL || errno == ENOENT) {
319 ret = dm_request_right(dmapi.sid, hanp, hlen, token, DM_RR_WAIT, DM_RIGHT_EXCL);
321 printf("dm_request_right failed - %s\n", strerror(errno));
323 response = DM_RESP_ABORT;
328 memset(attrname.an_chars, 0, DM_ATTR_NAME_SIZE);
329 strncpy((char*)attrname.an_chars, HSM_ATTRNAME, DM_ATTR_NAME_SIZE);
331 /* get the attribute and check it is valid. This is just
332 paranoia really, as the file is going away */
333 ret = dm_get_dmattr(dmapi.sid, hanp, hlen, token, &attrname,
334 sizeof(h), &h, &rlen);
336 printf("WARNING: dm_get_dmattr failed - %s\n", strerror(errno));
340 if (rlen != sizeof(h)) {
341 printf("hsm_handle_read - bad attribute size %d\n", (int)rlen);
343 response = DM_RESP_ABORT;
347 if (strncmp(h.magic, HSM_MAGIC, sizeof(h.magic)) != 0) {
348 printf("Bad magic '%*.*s'\n", (int)sizeof(h.magic), (int)sizeof(h.magic), h.magic);
350 response = DM_RESP_ABORT;
354 if (options.debug > 1) {
355 printf("%s: Destroying file %llx:%llx of size %d\n",
356 dmapi_event_string(msg->ev_type),
357 (unsigned long long)h.device, (unsigned long long)h.inode,
361 /* remove the store file */
362 ret = hsm_store_unlink(h.device, h.inode);
364 printf("WARNING: Failed to unlink store file for file 0x%llx:0x%llx\n",
365 (unsigned long long)h.device, (unsigned long long)h.inode);
368 /* remove the attribute */
369 ret = dm_remove_dmattr(dmapi.sid, hanp, hlen, token, 0, &attrname);
371 printf("dm_remove_dmattr failed - %s\n", strerror(errno));
373 response = DM_RESP_ABORT;
377 /* and clear the managed region */
378 ret = dm_set_region(dmapi.sid, hanp, hlen, token, 0, NULL, &exactFlag);
380 printf("WARNING: failed dm_set_region - %s\n", strerror(errno));
384 /* only respond if the token is real */
385 if (!DM_TOKEN_EQ(msg->ev_token,DM_NO_TOKEN) &&
386 !DM_TOKEN_EQ(msg->ev_token, DM_INVALID_TOKEN)) {
387 ret = dm_respond_event(dmapi.sid, msg->ev_token,
388 response, retcode, 0, NULL);
390 printf("Failed to respond to destroy event\n");
397 main switch for DMAPI messages
399 static void hsm_handle_message(dm_eventmsg_t *msg)
401 switch (msg->ev_type) {
403 hsm_handle_mount(msg);
407 hsm_handle_recall(msg);
409 case DM_EVENT_DESTROY:
410 hsm_handle_destroy(msg);
413 if (!DM_TOKEN_EQ(msg->ev_token,DM_NO_TOKEN) &&
414 !DM_TOKEN_EQ(msg->ev_token, DM_INVALID_TOKEN)) {
415 printf("Giving default response\n");
416 int ret = dm_respond_event(dmapi.sid, msg->ev_token,
417 DM_RESP_CONTINUE, 0, 0, NULL);
419 printf("Failed to respond to mount event\n");
428 wait for DMAPI events to come in and dispatch them
430 static void hsm_wait_events(void)
436 printf("Waiting for events\n");
440 if (options.blocking_wait) {
441 ret = dm_get_events(dmapi.sid, 0, DM_EV_WAIT, sizeof(buf), buf, &rlen);
443 /* optionally don't use DM_RR_WAIT to ensure
444 that the daemon can be killed. This is only
445 needed because GPFS uses an uninterruptible
446 sleep for dm_get_events with DM_EV_WAIT. It
447 should be an interruptible sleep */
449 ret = dm_get_events(dmapi.sid, 0, 0, sizeof(buf), buf, &rlen);
452 if (errno == EAGAIN) continue;
453 if (errno == ESTALE) {
454 printf("DMAPI service has shutdown - restarting\n");
458 printf("Failed to get event (%s)\n", strerror(errno));
462 /* loop over all the messages we received */
463 for (msg=(dm_eventmsg_t *)buf;
465 msg = DM_STEP_TO_NEXT(msg, dm_eventmsg_t *)) {
466 /* optionally fork on each message, thus
467 giving parallelism and allowing us to delay
468 recalls, simulating slow tape speeds */
469 if (options.use_fork) {
470 if (fork() != 0) continue;
471 srandom(getpid() ^ time(NULL));
472 hsm_handle_message(msg);
475 hsm_handle_message(msg);
482 on startup we look for partially completed events from an earlier
483 instance of hacksmd, and continue them if we can
485 static void hsm_cleanup_events(void)
489 dm_token_t *tok = NULL;
495 ret = dm_getall_tokens(dmapi.sid, n, tok, &n2);
496 if (ret == -1 && errno == E2BIG) {
498 tok = realloc(tok, sizeof(dm_token_t)*n);
502 printf("dm_getall_tokens - %s\n", strerror(errno));
505 if (ret == 0 && n2 == 0) {
508 printf("Cleaning up %u tokens\n", n2);
511 /* get the message associated with this token
512 back from the kernel */
513 ret = dm_find_eventmsg(dmapi.sid, tok[i], sizeof(buf), buf, &rlen);
515 printf("Unable to find message for token in cleanup\n");
518 msg = (dm_eventmsg_t *)buf;
519 /* there seems to be a bug where GPFS
520 sometimes gives us a garbage token here */
521 if (!DM_TOKEN_EQ(tok[i], msg->ev_token)) {
522 printf("Message token mismatch in cleanup\n");
523 dm_respond_event(dmapi.sid, tok[i],
524 DM_RESP_ABORT, EINTR, 0, NULL);
526 unsigned saved_delay = options.recall_delay;
527 hsm_handle_message(msg);
528 options.recall_delay = saved_delay;
538 static void usage(void)
540 printf("Usage: hacksmd <options>\n");
541 printf("\n\tOptions:\n");
542 printf("\t\t -c cleanup lost tokens\n");
543 printf("\t\t -N use a non-blocking event wait\n");
544 printf("\t\t -d level choose debug level\n");
545 printf("\t\t -F fork to handle each event\n");
546 printf("\t\t -R delay set a random delay on recall up to 'delay' seconds\n");
551 int main(int argc, char * const argv[])
554 bool cleanup = false;
556 /* parse command-line options */
557 while ((opt = getopt(argc, argv, "chNd:FR:")) != -1) {
563 options.debug = strtoul(optarg, NULL, 0);
566 options.recall_delay = strtoul(optarg, NULL, 0);
569 options.blocking_wait = false;
572 options.use_fork = true;
586 signal(SIGCHLD, SIG_IGN);
588 signal(SIGTERM, hsm_term_handler);
589 signal(SIGINT, hsm_term_handler);
594 hsm_cleanup_tokens(dmapi.sid, DM_RESP_ABORT, EINTR);
598 hsm_cleanup_events();