2 * Master migration daemon
4 * The master migration daemon waits for events on a file and
5 * spawns a child process to handle the event
7 * This code was written by Peter Lawthers, and placed in the public
8 * domain for use by DMAPI implementors and app writers.
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
24 #include <sys/types.h>
26 #include <sys/resource.h>
37 extern int optind, optopt, opterr;
42 extern int setup_dmapi(dm_sessid_t *);
43 extern void err_msg(char *, ...);
44 extern void errno_msg(char *, ...);
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 *);
58 fprintf(stderr, "Usage: %s ", prog);
59 fprintf(stderr, " <-v verbose> ");
60 fprintf(stderr, " <-l logfile> ");
61 fprintf(stderr, "filesystem \n");
73 char *fsname, *logfile;
83 while ((c = getopt(argc, argv, "vl:")) != EOF) {
101 fsname = argv[optind];
102 if (fsname == NULL) {
107 * If no logfile name is specified, we'll just send
108 * all output to some file in /tmp
111 logfile = LOG_DEFAULT;
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
119 error = setup_dmapi(&sid);
123 if (dm_path_to_fshandle(fsname, &fs_hanp, &fs_hlen) == -1) {
124 errno_msg("Can't get filesystem handle");
129 * Turn ourselves into a daemon
131 error = mk_daemon(logfile);
137 * Set the event disposition so that our session will receive
138 * the managed region events (read, write, and truncate)
140 error = set_events(sid, fs_hanp, fs_hlen);
146 * Now wait forever for messages, spawning kids to
154 * Main event loop processing
161 size_t bufsize, rlen;
166 * We take a swag at a buffer size. If it's wrong, we can
169 bufsize = sizeof(dm_eventmsg_t) + sizeof(dm_data_event_t) + HANDLE_LEN;
171 msgbuf = (void *)malloc(bufsize);
172 if (msgbuf == NULL) {
173 err_msg("Can't allocate memory for buffer");
178 error = dm_get_events(sid, ALL_AVAIL_MSGS, DM_EV_WAIT, bufsize,
181 if (errno == E2BIG) {
183 msgbuf = (void *)malloc(rlen);
184 if (msgbuf == NULL) {
185 err_msg("Can't resize msg buffer");
190 errno_msg("Error getting events from DMAPI");
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.
200 msg = (dm_eventmsg_t *)msgbuf;
201 while (msg != NULL ) {
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);
207 switch (msg->ev_type) {
210 spawn_kid(sid, msg->ev_token, RESTORE_FILE);
214 case DM_EVENT_TRUNCATE:
215 spawn_kid(sid, msg->ev_token, INVAL_FILE);
219 err_msg("Invalid msg type %d\n", msg->ev_type);
222 msg = DM_STEP_TO_NEXT(msg, dm_eventmsg_t *);
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.
245 char sidbuf[sizeof(dm_sessid_t)];
246 char tokenbuf[sizeof(dm_token_t)];
251 * We're in the child. Try and exec the worker bee
253 sprintf(sidbuf, "%d", sid);
254 sprintf(tokenbuf, "%d", token);
256 fprintf(stderr, "execl(%s, %s, %s, -s, %s, -t, %s, 0)\n",
257 WORKER_BEE, WORKER_BEE, action, sidbuf,
260 if (execl(WORKER_BEE, WORKER_BEE, action, "-s", sidbuf,
261 "-t", tokenbuf, NULL))
263 (void)dm_respond_event(sid, token, DM_RESP_ABORT,
270 err_msg("Can't fork worker bee");
271 (void)dm_respond_event(sid, token, DM_RESP_ABORT, errno,
281 * Set the event disposition of the managed region events
289 dm_eventset_t eventlist;
291 DMEV_ZERO(eventlist);
292 DMEV_SET(DM_EVENT_READ, eventlist);
293 DMEV_SET(DM_EVENT_WRITE, eventlist);
294 DMEV_SET(DM_EVENT_TRUNCATE, eventlist);
296 if (dm_set_disp(sid, fs_hanp, fs_hlen, DM_NO_TOKEN, &eventlist,
299 errno_msg("Can't set event disposition");
309 * Dissassociate ourselves from our tty, and close all files
318 struct sigaction act;
321 if ((pid = fork()) == -1)
332 * Determine how many open files we've got and close
335 if (getrlimit(RLIMIT_NOFILE, &lim) < 0 ) {
336 errno_msg("Can't determine max number of open files");
339 for (i=0; i<lim.rlim_cur; i++)
343 * For error reporting, we re-direct stdout and stderr to a
346 if ((fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC, 0755)) < 0) {
347 errno_msg("Can't open logfile %s", logfile);
350 (void)dup2(fd, STDOUT_FILENO);
351 (void)dup2(fd, STDERR_FILENO);
355 * Set up signals so that we can wait for spawned children
357 act.sa_handler = migin_exit;
359 sigemptyset(&act.sa_mask);
361 (void)sigaction(SIGHUP, &act, NULL);
362 (void)sigaction(SIGINT, &act, NULL);
363 (void)sigaction(SIGQUIT, &act, NULL);
364 (void)sigaction(SIGTERM, &act, NULL);
365 (void)sigaction(SIGUSR1, &act, NULL);
366 (void)sigaction(SIGUSR1, &act, NULL);
367 (void)sigaction(SIGUSR2, &act, NULL);
375 dm_sessid_t *sidbuf, *sid;
378 u_int nsessions, nret;
380 size_t buflen, retlen;
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.
389 err_msg("%s: waiting for all children to die...", Progname);
390 while (wait3((int *)0, WNOHANG, (struct rusage *)0) > 0)
393 fprintf(stdout, "\n");
396 * Now search for our session and try and shut it down. We
397 * could just as easily make the session ID a global, but
398 * this demonstrates how sessions can be queried
401 sidbuf = (dm_sessid_t *)malloc(nsessions * sizeof(dm_sessid_t));
402 if (sidbuf == NULL) {
403 err_msg("Can't alloc mem to shut down session");
406 error = dm_getall_sessions(nsessions, sidbuf, &nret);
408 if (errno != E2BIG) {
409 errno_msg("Can't get list of active sessions");
414 sidbuf = (dm_sessid_t *)malloc(nsessions * sizeof(dm_sessid_t));
415 if (sidbuf == NULL) {
416 err_msg("Can't alloc mem to shut down session");
419 error = dm_getall_sessions(nsessions, sidbuf, &nret);
421 errno_msg("Can't get list of active sessions");
427 * Now we have all the active sessions in our system.
428 * Query each one until we find ourselves.
431 buflen = DM_SESSION_INFO_LEN;
432 infobuf = malloc(buflen);
433 if (infobuf == NULL) {
434 err_msg("Can't alloc memory for session info buffer");
439 * When we registered our session, we just hammered the last component
440 * of the path name, so that's all we look for here. This prevents
441 * mismatches when running at ./migin or some other such foo
443 cp = strrchr(Progname, '/');
451 for (i=0; i<nret; i++) {
452 error = dm_query_session(*sid, buflen, infobuf, &retlen);
454 continue; /* We just punt on errors */
456 if (strstr(infobuf, cp)) {
466 * Should do a set_disp to 0 and then drain the session
467 * queue as well. On the SGI, we'll need to make the
468 * filesystem handle global so that we can get at it
472 err_msg("Can't find session to shut down");
475 error = dm_destroy_session(*sid);
477 errno_msg("Can't shut down session");