more tweaks
[tridge/hacksm.git] / hacksmd.c
1 /*
2   a test implementation of a HSM daemon
3
4   Andrew Tridgell August 2008
5
6  */
7
8 #include "hacksm.h"
9
10 static struct {
11         dm_sessid_t sid;
12 } dmapi = {
13         .sid = DM_NO_SESSION
14 };
15
16 #define SESSION_NAME "hacksmd"
17
18 static void hsm_term_handler(int signal)
19 {
20         printf("Got signal %d - exiting\n", signal);
21         exit(1);
22 }
23
24
25 static void hsm_init(const char *path)
26 {
27         char *dmapi_version = NULL;
28         dm_eventset_t eventSet;
29         int ret;
30
31         ret = dm_init_service(&dmapi_version);
32         if (ret != 0) {
33                 printf("Failed to init dmapi\n");
34                 exit(1);
35         }
36
37         printf("Initialised DMAPI version '%s'\n", dmapi_version);      
38
39         hsm_recover_session(SESSION_NAME, &dmapi.sid);
40
41         /* we want mount events only initially */
42         DMEV_ZERO(eventSet);
43         DMEV_SET(DM_EVENT_MOUNT, eventSet);
44         ret = dm_set_disp(dmapi.sid, DM_GLOBAL_HANP, DM_GLOBAL_HLEN, DM_NO_TOKEN,
45                           &eventSet, DM_EVENT_MAX);
46         if (ret != 0) {
47                 printf("Failed to setup events\n");
48                 exit(1);
49         }
50 }
51
52
53 static void hsm_handle_mount(dm_eventmsg_t *msg)
54 {
55         dm_mount_event_t *mount;
56         void *hand1;
57         size_t hand1len;
58         dm_eventset_t eventSet;
59         int ret;
60         
61         mount = DM_GET_VALUE(msg, ev_data, dm_mount_event_t*);
62         hand1 = DM_GET_VALUE(mount , me_handle1, void *);
63         hand1len = DM_GET_LEN(mount, me_handle1);
64         
65         DMEV_ZERO(eventSet);
66         DMEV_SET(DM_EVENT_READ, eventSet);
67         DMEV_SET(DM_EVENT_WRITE, eventSet);
68         DMEV_SET(DM_EVENT_TRUNCATE, eventSet);
69         DMEV_SET(DM_EVENT_DESTROY, eventSet);
70         ret = dm_set_eventlist(dmapi.sid, hand1, hand1len,
71                                DM_NO_TOKEN, &eventSet, DM_EVENT_MAX);
72         if (ret != 0) {
73                 printf("Failed to setup all event handler\n");
74                 exit(1);
75         }
76         
77         ret = dm_set_disp(dmapi.sid, hand1, hand1len, DM_NO_TOKEN,
78                           &eventSet, DM_EVENT_MAX);
79         if (ret != 0) {
80                 printf("Failed to setup disposition for all events\n");
81                 exit(1);
82         }
83         
84         ret = dm_respond_event(dmapi.sid, msg->ev_token, 
85                                DM_RESP_CONTINUE, 0, 0, NULL);
86         if (ret != 0) {
87                 printf("Failed to respond to mount event\n");
88                 exit(1);
89         }
90 }
91
92
93 static void hsm_handle_recall(dm_eventmsg_t *msg)
94 {
95         dm_data_event_t *ev;
96         void *hanp;
97         size_t hlen, rlen;
98         int ret;
99         dm_attrname_t attrname;
100         dm_token_t token = msg->ev_token;
101         struct hsm_attr h;
102         int retcode = -1;
103         dm_boolean_t exactFlag;
104         int fd;
105         char buf[0x10000];
106         off_t ofs;
107         int have_right = 0;
108
109         ev = DM_GET_VALUE(msg, ev_data, dm_data_event_t *);
110         hanp = DM_GET_VALUE(ev, de_handle, void *);
111         hlen = DM_GET_LEN(ev, de_handle);
112
113         memset(attrname.an_chars, 0, DM_ATTR_NAME_SIZE);
114         strncpy((char*)attrname.an_chars, HSM_ATTRNAME, DM_ATTR_NAME_SIZE);
115
116         ret = dm_request_right(dmapi.sid, hanp, hlen, token, DM_RR_WAIT, DM_RIGHT_EXCL);
117         if (ret != 0) {
118                 printf("dm_request_right failed - %s\n", strerror(errno));
119                 goto done;
120         }
121
122         have_right = 1;
123
124         ret = dm_get_dmattr(dmapi.sid, hanp, hlen, token, &attrname, 
125                             sizeof(h), &h, &rlen);
126         if (ret != 0) {
127                 if (errno == ENOENT) {
128                         printf("File already recalled (no attribute)\n");
129                         goto done;
130                 }
131                 printf("dm_get_dmattr failed - %s\n", strerror(errno));
132                 goto done;
133         }
134
135         if (rlen != sizeof(h)) {
136                 printf("hsm_handle_read - bad attribute size %d\n", (int)rlen);
137                 goto done;
138         }
139
140         if (strncmp(h.magic, HSM_MAGIC, sizeof(h.magic)) != 0) {
141                 printf("Bad magic '%*.*s'\n", (int)sizeof(h.magic), (int)sizeof(h.magic), h.magic);
142                 goto done;
143         }
144
145         h.state = HSM_STATE_RECALL;
146         ret = dm_set_dmattr(dmapi.sid, hanp, hlen, token, &attrname, 0, sizeof(h), (void*)&h);
147         if (ret != 0) {
148                 printf("dm_set_dmattr failed - %s\n", strerror(errno));
149                 goto done;
150         }
151
152         fd = hsm_store_open(h.device, h.inode, O_RDONLY);
153         if (fd == -1) {
154                 printf("Failed to open store file for file 0x%llx:0x%llx\n",
155                        (unsigned long long)h.device, (unsigned long long)h.inode);
156                 goto done;
157         }
158
159         printf("Recalling file %llx:%llx of size %d\n", 
160                (unsigned long long)h.device, (unsigned long long)h.inode,
161                (int)h.size);
162
163         ofs = 0;
164         while ((ret = read(fd, buf, sizeof(buf))) > 0) {
165                 int ret2 = dm_write_invis(dmapi.sid, hanp, hlen, token, DM_WRITE_SYNC, ofs, ret, buf);
166                 if (ret2 != ret) {
167                         printf("dm_write_invis failed - %s\n", strerror(errno));
168                         goto done;
169                 }
170                 ofs += ret;
171         }
172         close(fd);
173
174         ret = dm_remove_dmattr(dmapi.sid, hanp, hlen, token, 0, &attrname);
175         if (ret != 0) {
176                 printf("dm_remove_dmattr failed - %s\n", strerror(errno));
177                 goto done;
178         }
179
180         ret = hsm_store_unlink(h.device, h.inode);
181         if (ret != 0) {
182                 printf("Failed to unlink store file\n");
183                 goto done;
184         }
185
186         ret = dm_set_region(dmapi.sid, hanp, hlen, token, 0, NULL, &exactFlag);
187         if (ret == -1) {
188                 printf("failed dm_set_region - %s\n", strerror(errno));
189                 exit(1);
190         }
191
192 done:
193         if (have_right) {
194                 ret = dm_release_right(dmapi.sid, hanp, hlen, token);
195                 if (ret == -1) {
196                         printf("failed dm_release_right on %s\n", strerror(errno));
197                 }
198         }
199
200         ret = dm_respond_event(dmapi.sid, msg->ev_token, 
201                                DM_RESP_CONTINUE, retcode, 0, NULL);
202         if (ret != 0) {
203                 printf("Failed to respond to read event\n");
204                 exit(1);
205         }
206 }
207
208
209 static void hsm_handle_destroy(dm_eventmsg_t *msg)
210 {
211         dm_destroy_event_t *ev;
212         void *hanp;
213         size_t hlen, rlen;
214         int ret;
215         dm_attrname_t attrname;
216         dm_token_t token = msg->ev_token;
217         struct hsm_attr h;
218         int retcode = -1;
219         dm_boolean_t exactFlag;
220
221         ev = DM_GET_VALUE(msg, ev_data, dm_destroy_event_t *);
222         hanp = DM_GET_VALUE(ev, ds_handle, void *);
223         hlen = DM_GET_LEN(ev, ds_handle);
224
225         if (DM_TOKEN_EQ(token, DM_INVALID_TOKEN)) {
226                 goto done;
227         }
228
229         memset(attrname.an_chars, 0, DM_ATTR_NAME_SIZE);
230         strncpy((char*)attrname.an_chars, HSM_ATTRNAME, DM_ATTR_NAME_SIZE);
231
232         ret = dm_get_dmattr(dmapi.sid, hanp, hlen, token, &attrname, 
233                             sizeof(h), &h, &rlen);
234         if (ret != 0) {
235                 printf("dm_get_dmattr failed - %s\n", strerror(errno));
236                 goto done;
237         }
238
239         if (rlen != sizeof(h)) {
240                 printf("hsm_handle_read - bad attribute size %d\n", (int)rlen);
241                 goto done;
242         }
243
244         if (strncmp(h.magic, HSM_MAGIC, sizeof(h.magic)) != 0) {
245                 printf("Bad magic '%*.*s'\n", (int)sizeof(h.magic), (int)sizeof(h.magic), h.magic);
246                 goto done;
247         }
248
249         ret = hsm_store_unlink(h.device, h.inode);
250         if (ret == -1) {
251                 printf("Failed to unlink store file for file 0x%llx:0x%llx\n",
252                        (unsigned long long)h.device, (unsigned long long)h.inode);
253                 goto done;
254         }
255
256         ret = hsm_store_unlink(h.device, h.inode);
257         if (ret != 0) {
258                 printf("Failed to unlink store file\n");
259                 goto done;
260         }
261
262         ret = dm_remove_dmattr(dmapi.sid, hanp, hlen, token, 0, &attrname);
263         if (ret != 0) {
264                 printf("dm_remove_dmattr failed - %s\n", strerror(errno));
265                 goto done;
266         }
267
268         ret = dm_set_region(dmapi.sid, hanp, hlen, token, 0, NULL, &exactFlag);
269         if (ret == -1) {
270                 printf("failed dm_set_region - %s\n", strerror(errno));
271                 exit(1);
272         }
273
274 done:
275         if (!DM_TOKEN_EQ(msg->ev_token,DM_NO_TOKEN) &&
276             !DM_TOKEN_EQ(msg->ev_token, DM_INVALID_TOKEN)) {
277                 ret = dm_respond_event(dmapi.sid, msg->ev_token, 
278                                        DM_RESP_CONTINUE, retcode, 0, NULL);
279                 if (ret != 0) {
280                         printf("Failed to respond to destroy event\n");
281                         exit(1);
282                 }
283         }
284 }
285
286
287 static void hsm_handle_message(dm_eventmsg_t *msg)
288 {
289         printf("Got event %s from node 0x%x\n",
290                dmapi_event_string(msg->ev_type), msg->ev_nodeid);
291
292         switch (msg->ev_type) {
293         case DM_EVENT_MOUNT:
294                 hsm_handle_mount(msg);
295                 break;
296         case DM_EVENT_READ:
297         case DM_EVENT_WRITE:
298                 hsm_handle_recall(msg);
299                 break;
300         case DM_EVENT_DESTROY:
301                 hsm_handle_destroy(msg);
302                 break;
303         default:
304                 if (!DM_TOKEN_EQ(msg->ev_token,DM_NO_TOKEN) &&
305                     !DM_TOKEN_EQ(msg->ev_token, DM_INVALID_TOKEN)) {
306                         printf("Giving default response\n");
307                         int ret = dm_respond_event(dmapi.sid, msg->ev_token, 
308                                                DM_RESP_CONTINUE, 0, 0, NULL);
309                         if (ret != 0) {
310                                 printf("Failed to respond to mount event\n");
311                                 exit(1);
312                         }
313                 }
314                 break;
315         }
316 }
317
318 static void hsm_cleanup_tokens(void)
319 {
320         dm_token_t tok[10];
321         u_int n;
322         int ret, i;
323
324         while ((ret = dm_getall_tokens(dmapi.sid, 10, tok, &n)) == 0 && n > 0) {
325                 printf("Cleaning up %u tokens\n", n);
326                 for (i=0;i<n;i++) {
327                         dm_respond_event(dmapi.sid, tok[i], 
328                                          DM_RESP_CONTINUE, 0, 0, NULL);
329                 }
330         }
331 }
332
333 static void hsm_wait_events(void)
334 {
335         int ret;
336         char buf[0x10000];
337         size_t rlen;
338
339         printf("Waiting for events\n");
340         
341         while (1) {
342                 dm_eventmsg_t *msg;
343                 /* we don't use DM_RR_WAIT to ensure that the daemon can be killed */
344                 msleep(10);
345                 ret = dm_get_events(dmapi.sid, 0, 0, sizeof(buf), buf, &rlen);
346                 if (ret < 0) {
347                         if (errno == EAGAIN) continue;
348                         printf("Failed to get event (%s)\n", strerror(errno));
349                         exit(1);
350                 }
351                 
352                 for (msg=(dm_eventmsg_t *)buf; msg; msg = DM_STEP_TO_NEXT(msg, dm_eventmsg_t *)) {
353                         hsm_handle_message(msg);
354                 }
355         }
356 }
357
358 static void usage(void)
359 {
360         printf("Usage: hacksmd PATH\n");
361         exit(0);
362 }
363
364 int main(int argc, char * const argv[])
365 {
366         int opt;
367         char *fspath;   
368         /* parse command-line options */
369         while ((opt = getopt(argc, argv, "h")) != -1) {
370                 switch (opt) {
371                 case 'h':
372                 default:
373                         usage();
374                         break;
375                 }
376         }
377
378         setlinebuf(stdout);     
379
380         argv += optind;
381         argc -= optind;
382
383         if (argc == 0) {
384                 usage();
385         }
386
387         fspath = argv[0];
388
389         signal(SIGTERM, hsm_term_handler);
390         signal(SIGINT, hsm_term_handler);
391
392         hsm_init(fspath);
393
394         hsm_cleanup_tokens();
395
396         hsm_wait_events();
397
398         return 0;
399 }