cmd/xfsprogs/libdm/dmapi_tests/README 1.1 Renamed to cmd/xfstests/dmapi/README
[xfstests-dev.git] / dmapi / src / sample_hsm / migin.c
1 /*
2  * Master migration daemon
3  *
4  * The master migration daemon waits for events on a file and
5  * spawns a child process to handle the event
6  *
7  * This code was written by Peter Lawthers, and placed in the public
8  * domain for use by DMAPI implementors and app writers.
9  *
10  * Standard disclaimer:
11  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
12  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
14  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
15  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
16  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
17  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
19  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
20  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
21  * SUCH DAMAGE.
22  */
23
24 #include <sys/types.h>
25 #include <sys/time.h>
26 #include <sys/resource.h>
27 #include <sys/wait.h>
28
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <signal.h>
33
34 #include <lib/hsm.h>
35
36 extern char     *optarg;
37 extern int       optind, optopt, opterr;
38 extern int       errno;
39 char            *Progname;
40 int              Verbose;
41
42 extern int       setup_dmapi(dm_sessid_t *);
43 extern void      err_msg(char *, ...);
44 extern void      errno_msg(char *, ...);
45
46 void             event_loop(dm_sessid_t);
47 int              set_events(dm_sessid_t, void *, size_t);
48 int              mk_daemon(char *);
49 void             spawn_kid(dm_sessid_t, dm_token_t, char *);
50 void             migin_exit(void);
51 void             usage(char *);
52
53
54 void
55 usage(
56         char *prog)
57 {
58         fprintf(stderr, "Usage: %s ", prog);
59         fprintf(stderr, " <-v verbose> ");
60         fprintf(stderr, " <-l logfile> ");
61         fprintf(stderr, "filesystem \n");
62 }
63
64
65 int
66 main(
67         int     argc, 
68         char    *argv[])
69 {
70         
71         int              c;
72         int              error;
73         char            *fsname, *logfile;
74         dm_sessid_t      sid;
75         void            *fs_hanp;
76         size_t           fs_hlen;
77
78
79         Progname  = argv[0];
80         fsname  = NULL;
81         logfile = NULL;
82
83         while ((c = getopt(argc, argv, "vl:")) != EOF) {
84                 switch (c) {
85                 case 'v':
86                         Verbose = 1;
87                         break;
88                 case 'l':
89                         logfile = optarg;
90                         break;
91                 case '?':
92                 default:
93                         usage(Progname);
94                         exit(1);
95                 }
96         }
97         if (optind >= argc) {
98                 usage(Progname);
99                 exit(1);
100         }
101         fsname = argv[optind];
102         if (fsname == NULL) {
103                 usage(Progname);
104                 exit(1);
105         }
106         /*
107          * If no logfile name is specified, we'll just send
108          * all output to some file in /tmp
109          */
110         if (logfile == NULL)
111                 logfile = LOG_DEFAULT;
112          
113
114         /*
115          * Now we have our filesystem name and possibly a size threshold
116          * to look for. Init the dmapi, and get a filesystem handle so
117          * we can set up our events
118          */
119         error = setup_dmapi(&sid);
120         if (error) 
121                 exit(1);
122         
123         if (dm_path_to_fshandle(fsname, &fs_hanp, &fs_hlen) == -1) {
124                 errno_msg("Can't get filesystem handle");
125                 exit(1);
126         }
127
128         /*
129          * Turn ourselves into a daemon
130          */
131         error = mk_daemon(logfile);
132         if (error) 
133                 exit(1);
134         
135
136         /*
137          * Set the event disposition so that our session will receive 
138          * the managed region events (read, write, and truncate)
139          */
140         error = set_events(sid, fs_hanp, fs_hlen);
141         if (error) 
142                 exit(1);
143         
144
145         /*
146          * Now wait forever for messages, spawning kids to
147          * do the actual work
148          */
149         event_loop(sid);
150         return(0);
151 }
152
153 /*
154  * Main event loop processing
155  */
156 void
157 event_loop(
158         dm_sessid_t     sid)
159 {
160         void            *msgbuf;
161         size_t           bufsize, rlen;
162         int              error;
163         dm_eventmsg_t   *msg;
164
165         /*
166          * We take a swag at a buffer size. If it's wrong, we can
167          * always resize it
168          */
169         bufsize = sizeof(dm_eventmsg_t) + sizeof(dm_data_event_t) + HANDLE_LEN;
170         bufsize *= 16;
171         msgbuf  = (void *)malloc(bufsize);
172         if (msgbuf == NULL) {
173                 err_msg("Can't allocate memory for buffer");
174                 goto out;
175         }
176
177         for (;;) {      
178                 error = dm_get_events(sid, ALL_AVAIL_MSGS, DM_EV_WAIT, bufsize,
179                                         msgbuf, &rlen);
180                 if (error == -1) {
181                         if (errno == E2BIG) {
182                                 free(msgbuf);
183                                 msgbuf = (void *)malloc(rlen);
184                                 if (msgbuf == NULL) {
185                                         err_msg("Can't resize msg buffer");
186                                         goto out;
187                                 }
188                                 continue;
189                         }
190                         errno_msg("Error getting events from DMAPI");
191                         goto out;
192                 }
193
194                 /*
195                  * Walk thru the message buffer, pull out each individual
196                  * message, and dispatch the messages to child processes
197                  * with the sid, token, and data. The children will
198                  * respond to the events.
199                  */
200                 msg = (dm_eventmsg_t *)msgbuf;
201                 while (msg != NULL ) {
202                         if (Verbose) {
203                                 fprintf(stderr, "Received %s, token %d\n",
204                                     (msg->ev_type == DM_EVENT_READ ? "read" : 
205                                     (msg->ev_type == DM_EVENT_WRITE ? "write" : "trunc")), msg->ev_token);
206                         }
207                         switch (msg->ev_type) {
208
209                         case DM_EVENT_READ:
210                                 spawn_kid(sid, msg->ev_token, RESTORE_FILE);
211                                 break;
212
213                         case DM_EVENT_WRITE:
214                         case DM_EVENT_TRUNCATE:
215                                 spawn_kid(sid, msg->ev_token, INVAL_FILE);
216                                 break;
217
218                         default:
219                                 err_msg("Invalid msg type %d\n", msg->ev_type);
220                                 break;
221                         }
222                         msg = DM_STEP_TO_NEXT(msg, dm_eventmsg_t *);
223                 }
224         }
225 out:
226         if (msgbuf != NULL)
227                 free(msgbuf);
228
229         migin_exit();
230 }
231
232 /*
233  * Fork and exec our worker bee to work on  the file. If 
234  * there is any error in fork/exec'ing the file, we have to
235  * supply the error return to the event. Once the child gets
236  * started, he/she/it will respond to the event for us.
237  */
238 void
239 spawn_kid(
240         dm_sessid_t      sid,
241         dm_token_t       token,
242         char            *action)
243 {
244         pid_t   pid;
245         char    sidbuf[sizeof(dm_sessid_t)];
246         char    tokenbuf[sizeof(dm_token_t)];
247
248         pid = fork();
249         if (pid == 0) {
250                 /*
251                  * We're in the child. Try and exec the worker bee
252                  */
253                 sprintf(sidbuf, "%d", sid);
254                 sprintf(tokenbuf, "%d", token);
255                 if (Verbose) {
256                         fprintf(stderr, "execl(%s, %s, %s, -s, %s, -t, xs, 0)\n",
257                                 WORKER_BEE, WORKER_BEE, action, sidbuf, tokenbuf);
258                 }
259                 if (execl(WORKER_BEE, WORKER_BEE, action, "-s", sidbuf, 
260                         "-t", tokenbuf, NULL)) 
261                 {
262                         (void)dm_respond_event(sid, token, DM_RESP_ABORT, 
263                                                 errno, 0, 0);
264                         exit(1);
265                 }
266         }
267
268         if (pid < 0) {
269                 err_msg("Can't fork worker bee");
270                 (void)dm_respond_event(sid, token, DM_RESP_ABORT, errno,
271                                         0, 0);
272                 return;
273         }
274         return;
275
276 }
277
278
279 /*
280  * Set the event disposition of the managed region events
281  */
282 int
283 set_events(
284         dm_sessid_t      sid,
285         void            *fs_hanp,
286         size_t           fs_hlen)
287 {
288         dm_eventset_t   eventlist;
289
290         DMEV_ZERO(eventlist);
291         DMEV_SET(DM_EVENT_READ, eventlist);
292         DMEV_SET(DM_EVENT_WRITE, eventlist);
293         DMEV_SET(DM_EVENT_TRUNCATE, eventlist);
294
295         if (dm_set_disp(sid, fs_hanp, fs_hlen, DM_NO_TOKEN, &eventlist, 
296                         DM_EVENT_MAX) == -1) 
297         {
298                 errno_msg("Can't set event disposition");
299                 return(1);
300         }
301         return(0);
302 }
303
304
305
306
307 /*
308  * Dissassociate ourselves from our tty, and close all files
309  */
310 int
311 mk_daemon(
312         char    *logfile)
313 {
314         int                     fd, pid;
315         int                     i;
316         struct rlimit           lim;
317         struct sigaction        act;
318
319 #ifdef NOTYET
320         if ((pid = fork()) == -1)
321                 return (-1);
322         if (pid)
323                 exit(0);
324
325         (void) setsid();
326
327         (void) chdir("/");
328
329 #endif /* NOTYET */
330         /*
331          * Determine how many open files we've got and close
332          * then all
333          */
334         if (getrlimit(RLIMIT_NOFILE, &lim) < 0 ) {
335                 errno_msg("Can't determine max number of open files");
336                 return(1);
337         }
338         for (i=0; i<lim.rlim_cur; i++)
339                 (void)close(i);
340
341         /*
342          * For error reporting, we re-direct stdout and stderr to a 
343          * logfile. 
344          */
345         if ((fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC, 0755)) < 0) {
346                 errno_msg("Can't open logfile %s", logfile);
347                 return(1);
348         }
349         (void)dup2(fd, STDOUT_FILENO);
350         (void)dup2(fd, STDERR_FILENO);
351         close(fd);
352
353         /*
354          * Set up signals so that we can wait for spawned children 
355          */
356         act.sa_handler = migin_exit;
357         act.sa_flags   = 0;
358         sigemptyset(&act.sa_mask);
359
360         (void)sigaction(SIGHUP, &act, NULL);
361         (void)sigaction(SIGINT, &act, NULL);
362         (void)sigaction(SIGQUIT, &act, NULL);
363         (void)sigaction(SIGTERM, &act, NULL);
364         (void)sigaction(SIGUSR1, &act, NULL);
365         (void)sigaction(SIGUSR1, &act, NULL);
366         (void)sigaction(SIGUSR2, &act, NULL);
367
368         return(0);
369 }
370
371 void
372 migin_exit(void)
373 {
374         dm_sessid_t     *sidbuf, *sid;
375         void            *infobuf;
376         char            *cp;
377         u_int            nsessions, nret;
378         int              i, found, error;
379         size_t           buflen, retlen;
380
381         sidbuf  = NULL;
382         infobuf = NULL;
383         
384         /*
385          * We could try and kill off all our kids, but we're not
386          * 'Mr. Mean', so we just wait for them to die.
387          */
388         err_msg("%s: waiting for all children to die...", Progname);
389         while (wait3((int *)0, WNOHANG, (struct rusage *)0) > 0)
390                 ;
391
392         fprintf(stdout, "\n");
393
394         /*
395          * Now search for our session and try and shut it down. We
396          * could just as easily make the session ID a global, but
397          * this demonstrates how sessions can be queried
398          */
399         nsessions = 4;
400         sidbuf = (dm_sessid_t *)malloc(nsessions * sizeof(dm_sessid_t));
401         if (sidbuf == NULL) {
402                 err_msg("Can't alloc mem to shut down session");
403                 goto out;
404         }
405         error = dm_getall_sessions(nsessions, sidbuf, &nret);
406         if (error == -1) {
407                 if (errno != E2BIG) {
408                         errno_msg("Can't get list of active sessions");
409                         goto out;
410                 }
411                 free(sidbuf);
412                 nsessions = nret;
413                 sidbuf = (dm_sessid_t *)malloc(nsessions * sizeof(dm_sessid_t));
414                 if (sidbuf == NULL) {
415                         err_msg("Can't alloc mem to shut down session");
416                         goto out;
417                 }
418                 error = dm_getall_sessions(nsessions, sidbuf, &nret);
419                 if (error == -1) {
420                         errno_msg("Can't get list of active sessions");
421                         goto out;
422                 }
423         }
424
425         /*
426          * Now we have all the active sessions in our system.
427          * Query each one until we find ourselves.
428          */
429         sid    = sidbuf;
430         buflen = DM_SESSION_INFO_LEN;
431         infobuf = malloc(buflen);
432         if (infobuf == NULL) {
433                 err_msg("Can't alloc memory for session info buffer");
434                 goto out;
435         }
436
437         /*
438          * When we registered our session, we just hammered the last component
439          * of the path name, so that's all we look for here. This prevents
440          * mismatches when running at ./migin or some other such foo
441          */
442         cp = strrchr(Progname, '/');
443         if (cp)
444                 cp++;
445         else
446                 cp = Progname;
447
448
449         found = 0;
450         for (i=0; i<nret; i++) {
451                 error = dm_query_session(*sid, buflen, infobuf, &retlen);
452                 if (error == -1)
453                         continue;               /* We just punt on errors */
454
455                 if (strstr(infobuf, cp)) {
456                         found = 1;
457                         break;
458                 }
459                 sid++;
460         }
461
462         /*
463          * XXXX         FIXME           XXX
464          * 
465          *      Should do a set_disp to 0 and then drain the session
466          *      queue as well. On the SGI, we'll need to make the
467          *      filesystem handle global so that we can get at it
468          */
469
470         if (!found) {
471                 err_msg("Can't find session to shut down");
472                 goto out;
473         }
474         error = dm_destroy_session(*sid);
475         if (error == -1) {
476                 errno_msg("Can't shut down session");
477         }
478
479
480 out:
481         if (infobuf)
482                 free(infobuf);
483         if (sidbuf)
484                 free(sidbuf);
485
486         exit(0);
487 }
488