955274: DMAPI qa test fixes
[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 dm_sessid_t      sid = DM_NO_SESSION;
42
43 extern int       setup_dmapi(dm_sessid_t *);
44 extern void      err_msg(char *, ...);
45 extern void      errno_msg(char *, ...);
46
47 void             event_loop(dm_sessid_t);
48 int              set_events(dm_sessid_t, void *, size_t);
49 int              mk_daemon(char *);
50 void             spawn_kid(dm_sessid_t, dm_token_t, char *);
51 void             migin_exit(int);
52 void             usage(char *);
53 void             setup_signals();
54
55
56 void
57 usage(
58         char *prog)
59 {
60         fprintf(stderr, "Usage: %s ", prog);
61         fprintf(stderr, " <-v verbose> ");
62         fprintf(stderr, " <-l logfile> ");
63         fprintf(stderr, "filesystem \n");
64 }
65
66
67 int
68 main(
69         int     argc, 
70         char    *argv[])
71 {
72         
73         int              c;
74         int              error;
75         char            *fsname, *logfile;
76         void            *fs_hanp;
77         size_t           fs_hlen;
78
79
80         Progname  = argv[0];
81         fsname  = NULL;
82         logfile = NULL;
83
84         while ((c = getopt(argc, argv, "vl:")) != EOF) {
85                 switch (c) {
86                 case 'v':
87                         Verbose = 1;
88                         break;
89                 case 'l':
90                         logfile = optarg;
91                         break;
92                 case '?':
93                 default:
94                         usage(Progname);
95                         exit(1);
96                 }
97         }
98         if (optind >= argc) {
99                 usage(Progname);
100                 exit(1);
101         }
102         fsname = argv[optind];
103         if (fsname == NULL) {
104                 usage(Progname);
105                 exit(1);
106         }
107         /*
108          * If no logfile name is specified, we'll just send
109          * all output to some file in /tmp
110          */
111         if (logfile == NULL)
112                 logfile = LOG_DEFAULT;
113          
114
115         /*
116          * Turn ourselves into a daemon
117          */
118         if (!Verbose){
119                 error = mk_daemon(logfile);
120                 if (error) 
121                         exit(1);
122         }
123         setup_signals();
124         
125         /*
126          * Now we have our filesystem name and possibly a size threshold
127          * to look for. Init the dmapi, and get a filesystem handle so
128          * we can set up our events
129          */
130         error = setup_dmapi(&sid);
131         if (error) 
132                 exit(1);
133         
134         if (dm_path_to_fshandle(fsname, &fs_hanp, &fs_hlen) == -1) {
135                 errno_msg("Can't get filesystem handle");
136                 exit(1);
137         }
138
139
140         /*
141          * Set the event disposition so that our session will receive 
142          * the managed region events (read, write, and truncate)
143          */
144         error = set_events(sid, fs_hanp, fs_hlen);
145         if (error) 
146                 exit(1);
147         
148
149         /*
150          * Now wait forever for messages, spawning kids to
151          * do the actual work
152          */
153         event_loop(sid);
154         return(0);
155 }
156
157 /*
158  * Main event loop processing
159  */
160 void
161 event_loop(
162         dm_sessid_t     sid)
163 {
164         void            *msgbuf;
165         size_t           bufsize, rlen;
166         int              error;
167         dm_eventmsg_t   *msg;
168
169         /*
170          * We take a swag at a buffer size. If it's wrong, we can
171          * always resize it
172          */
173         bufsize = sizeof(dm_eventmsg_t) + sizeof(dm_data_event_t) + HANDLE_LEN;
174         bufsize *= 16;
175         msgbuf  = (void *)malloc(bufsize);
176         if (msgbuf == NULL) {
177                 err_msg("Can't allocate memory for buffer\n");
178                 goto out;
179         }
180
181         for (;;) {      
182                 error = dm_get_events(sid, ALL_AVAIL_MSGS, DM_EV_WAIT, bufsize,
183                                         msgbuf, &rlen);
184                 if (error == -1) {
185                         if (errno == E2BIG) {
186                                 free(msgbuf);
187                                 msgbuf = (void *)malloc(rlen);
188                                 if (msgbuf == NULL) {
189                                         err_msg("Can't resize msg buffer\n");
190                                         goto out;
191                                 }
192                                 continue;
193                         }
194                         errno_msg("Error getting events from DMAPI");
195                         goto out;
196                 }
197
198                 /*
199                  * Walk thru the message buffer, pull out each individual
200                  * message, and dispatch the messages to child processes
201                  * with the sid, token, and data. The children will
202                  * respond to the events.
203                  */
204                 msg = (dm_eventmsg_t *)msgbuf;
205                 while (msg != NULL ) {
206                         if (Verbose) {
207                                 fprintf(stderr, "Received %s, token %d\n",
208                                     (msg->ev_type == DM_EVENT_READ ? "read" : 
209                                     (msg->ev_type == DM_EVENT_WRITE ? "write" : "trunc")), msg->ev_token);
210                         }
211                         switch (msg->ev_type) {
212
213                         case DM_EVENT_READ:
214                                 spawn_kid(sid, msg->ev_token, RESTORE_FILE);
215                                 break;
216
217                         case DM_EVENT_WRITE:
218                         case DM_EVENT_TRUNCATE:
219                                 spawn_kid(sid, msg->ev_token, INVAL_FILE);
220                                 break;
221
222                         default:
223                                 err_msg("Invalid msg type %d\n", msg->ev_type);
224                                 break;
225                         }
226                         msg = DM_STEP_TO_NEXT(msg, dm_eventmsg_t *);
227                 }
228         }
229 out:
230         if (msgbuf != NULL)
231                 free(msgbuf);
232
233         migin_exit(0);
234 }
235
236 /*
237  * Fork and exec our worker bee to work on  the file. If 
238  * there is any error in fork/exec'ing the file, we have to
239  * supply the error return to the event. Once the child gets
240  * started, they will respond to the event for us.
241  */
242 void
243 spawn_kid(
244         dm_sessid_t      sid,
245         dm_token_t       token,
246         char            *action)
247 {
248         pid_t   pid;
249         char    sidbuf[10];
250         char    tokenbuf[10];
251
252         pid = fork();
253         if (pid == 0) {
254                 /*
255                  * We're in the child. Try and exec the worker bee
256                  */
257                 sprintf(sidbuf, "%d", sid);
258                 sprintf(tokenbuf, "%d", token);
259                 if (Verbose) {
260                         fprintf(stderr, "execl(%s, %s, %s, -s, %s, -t, %s, 0)\n",
261                                 WORKER_BEE, WORKER_BEE, action, sidbuf,
262                                 tokenbuf);
263                 }
264                 execlp("./"WORKER_BEE, WORKER_BEE, action, "-s", sidbuf, 
265                         "-t", tokenbuf, NULL);
266                 errno_msg("execlp of worker bee failed");
267                 (void)dm_respond_event(sid, token, DM_RESP_ABORT, 
268                                         errno, 0, 0);
269                 exit(1);
270         }
271
272         if (pid < 0) {
273                 err_msg("Can't fork worker bee\n");
274                 (void)dm_respond_event(sid, token, DM_RESP_ABORT, errno,
275                                         0, 0);
276                 return;
277         }
278         return;
279
280 }
281
282
283 /*
284  * Set the event disposition of the managed region events
285  */
286 int
287 set_events(
288         dm_sessid_t      sid,
289         void            *fs_hanp,
290         size_t           fs_hlen)
291 {
292         dm_eventset_t   eventlist;
293
294         DMEV_ZERO(eventlist);
295         DMEV_SET(DM_EVENT_READ, eventlist);
296         DMEV_SET(DM_EVENT_WRITE, eventlist);
297         DMEV_SET(DM_EVENT_TRUNCATE, eventlist);
298
299         if (dm_set_disp(sid, fs_hanp, fs_hlen, DM_NO_TOKEN, &eventlist, 
300                         DM_EVENT_MAX) == -1) 
301         {
302                 errno_msg("Can't set event disposition");
303                 return(1);
304         }
305         return(0);
306 }
307
308
309
310
311 /*
312  * Dissassociate ourselves from our tty, and close all files
313  */
314 int
315 mk_daemon(
316         char    *logfile)
317 {
318         int                     fd;
319         int                     i;
320         struct rlimit           lim;
321         pid_t                   pid;
322
323         if ((pid = fork()) == -1)
324                 return (-1);
325         if (pid)
326                 exit(0);
327
328         (void) setsid();
329
330         (void) chdir("/");
331
332         /*
333          * Determine how many open files we've got and close
334          * then all
335          */
336         if (getrlimit(RLIMIT_NOFILE, &lim) < 0 ) {
337                 errno_msg("Can't determine max number of open files");
338                 return(1);
339         }
340         for (i=0; i<lim.rlim_cur; i++)
341                 (void)close(i);
342
343         /*
344          * For error reporting, we re-direct stdout and stderr to a 
345          * logfile. 
346          */
347         if ((fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC, 0755)) < 0) {
348                 errno_msg("Can't open logfile %s", logfile);
349                 return(1);
350         }
351         (void)dup2(fd, STDOUT_FILENO);
352         (void)dup2(fd, STDERR_FILENO);
353         close(fd);
354
355         return(0);
356 }
357
358
359 void
360 setup_signals()
361 {
362         struct sigaction        act;
363
364         /*
365          * Set up signals so that we can wait for spawned children 
366          */
367         act.sa_handler = migin_exit;
368         act.sa_flags   = 0;
369         sigemptyset(&act.sa_mask);
370
371         (void)sigaction(SIGHUP, &act, NULL);
372         (void)sigaction(SIGINT, &act, NULL);
373         (void)sigaction(SIGQUIT, &act, NULL);
374         (void)sigaction(SIGTERM, &act, NULL);
375         (void)sigaction(SIGUSR1, &act, NULL);
376         (void)sigaction(SIGUSR1, &act, NULL);
377         (void)sigaction(SIGUSR2, &act, NULL);
378 }
379
380 void
381 migin_exit(int x)
382 {
383         int              error;
384
385         /*
386          * We could try and kill off all our kids, but we're not
387          * 'Mr. Mean', so we just wait for them to die.
388          */
389         err_msg("%s: waiting for all children to die...\n", Progname);
390         while (wait3((int *)0, WNOHANG, (struct rusage *)0) > 0)
391                 ;
392
393         fprintf(stdout, "\n");
394
395
396         /*
397          * XXXX         FIXME           XXX
398          * 
399          *      Should do a set_disp to 0 and then drain the session
400          *      queue as well. On the SGI, we'll need to make the
401          *      filesystem handle global so that we can get at it
402          */
403
404         error = dm_destroy_session(sid);
405         if (error == -1) {
406                 errno_msg("Can't shut down session\n");
407         }
408
409         exit(0);
410 }
411