Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / nvi / common / recover.c
blob3138522cac160bc067e592d10f1fc7e789c801aa
1 /* $NetBSD: recover.c,v 1.2 2008/12/05 22:51:42 christos Exp $ */
3 /*-
4 * Copyright (c) 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 * Copyright (c) 1993, 1994, 1995, 1996
7 * Keith Bostic. All rights reserved.
9 * See the LICENSE file for redistribution information.
12 #include "config.h"
14 #ifndef lint
15 static const char sccsid[] = "Id: recover.c,v 10.31 2001/11/01 15:24:44 skimo Exp (Berkeley) Date: 2001/11/01 15:24:44";
16 #endif /* not lint */
18 #include <sys/param.h>
19 #include <sys/types.h> /* XXX: param.h may not have included types.h */
20 #include <sys/queue.h>
21 #include <sys/stat.h>
24 * We include <sys/file.h>, because the open #defines were found there
25 * on historical systems. We also include <fcntl.h> because the open(2)
26 * #defines are found there on newer systems.
28 #include <sys/file.h>
30 #include <bitstring.h>
31 #include <dirent.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <limits.h>
35 #include <pwd.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40 #include <unistd.h>
42 #include "common.h"
43 #include "pathnames.h"
46 * Recovery code.
48 * The basic scheme is as follows. In the EXF structure, we maintain full
49 * paths of a b+tree file and a mail recovery file. The former is the file
50 * used as backing store by the DB package. The latter is the file that
51 * contains an email message to be sent to the user if we crash. The two
52 * simple states of recovery are:
54 * + first starting the edit session:
55 * the b+tree file exists and is mode 700, the mail recovery
56 * file doesn't exist.
57 * + after the file has been modified:
58 * the b+tree file exists and is mode 600, the mail recovery
59 * file exists, and is exclusively locked.
61 * In the EXF structure we maintain a file descriptor that is the locked
62 * file descriptor for the mail recovery file. NOTE: we sometimes have to
63 * do locking with fcntl(2). This is a problem because if you close(2) any
64 * file descriptor associated with the file, ALL of the locks go away. Be
65 * sure to remember that if you have to modify the recovery code. (It has
66 * been rhetorically asked of what the designers could have been thinking
67 * when they did that interface. The answer is simple: they weren't.)
69 * To find out if a recovery file/backing file pair are in use, try to get
70 * a lock on the recovery file.
72 * To find out if a backing file can be deleted at boot time, check for an
73 * owner execute bit. (Yes, I know it's ugly, but it's either that or put
74 * special stuff into the backing file itself, or correlate the files at
75 * boot time, neither of which looks like fun.) Note also that there's a
76 * window between when the file is created and the X bit is set. It's small,
77 * but it's there. To fix the window, check for 0 length files as well.
79 * To find out if a file can be recovered, check the F_RCV_ON bit. Note,
80 * this DOES NOT mean that any initialization has been done, only that we
81 * haven't yet failed at setting up or doing recovery.
83 * To preserve a recovery file/backing file pair, set the F_RCV_NORM bit.
84 * If that bit is not set when ending a file session:
85 * If the EXF structure paths (rcv_path and rcv_mpath) are not NULL,
86 * they are unlink(2)'d, and free(3)'d.
87 * If the EXF file descriptor (rcv_fd) is not -1, it is closed.
89 * The backing b+tree file is set up when a file is first edited, so that
90 * the DB package can use it for on-disk caching and/or to snapshot the
91 * file. When the file is first modified, the mail recovery file is created,
92 * the backing file permissions are updated, the file is sync(2)'d to disk,
93 * and the timer is started. Then, at RCV_PERIOD second intervals, the
94 * b+tree file is synced to disk. RCV_PERIOD is measured using SIGALRM, which
95 * means that the data structures (SCR, EXF, the underlying tree structures)
96 * must be consistent when the signal arrives.
98 * The recovery mail file contains normal mail headers, with two additions,
99 * which occur in THIS order, as the FIRST TWO headers:
101 * X-vi-recover-file: file_name
102 * X-vi-recover-path: recover_path
104 * Since newlines delimit the headers, this means that file names cannot have
105 * newlines in them, but that's probably okay. As these files aren't intended
106 * to be long-lived, changing their format won't be too painful.
108 * Btree files are named "vi.XXXX" and recovery files are named "recover.XXXX".
111 #define VI_FHEADER "X-vi-recover-file: "
112 #define VI_PHEADER "X-vi-recover-path: "
114 static int rcv_copy __P((SCR *, int, char *));
115 static void rcv_email __P((SCR *, char *));
116 static char *rcv_gets __P((char *, size_t, int));
117 static int rcv_mailfile __P((SCR *, int, char *));
118 static int rcv_mktemp __P((SCR *, char *, const char *, int));
121 * rcv_tmp --
122 * Build a file name that will be used as the recovery file.
124 * PUBLIC: int rcv_tmp __P((SCR *, EXF *, char *));
127 rcv_tmp(SCR *sp, EXF *ep, char *name)
129 struct stat sb;
130 int fd;
131 char path[MAXPATHLEN];
132 const char *dp;
135 * !!!
136 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
139 * If the recovery directory doesn't exist, try and create it. As
140 * the recovery files are themselves protected from reading/writing
141 * by other than the owner, the worst that can happen is that a user
142 * would have permission to remove other user's recovery files. If
143 * the sticky bit has the BSD semantics, that too will be impossible.
145 if (opts_empty(sp, O_RECDIR, 0))
146 goto err;
147 dp = O_STR(sp, O_RECDIR);
148 if (stat(dp, &sb)) {
149 if (errno != ENOENT || mkdir(dp, 0)) {
150 msgq(sp, M_SYSERR, "%s", dp);
151 goto err;
153 (void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX);
156 /* Newlines delimit the mail messages. */
157 if (strchr(name, '\n')) {
158 msgq(sp, M_ERR,
159 "055|Files with newlines in the name are unrecoverable");
160 goto err;
163 (void)snprintf(path, sizeof(path), "%s/vi.XXXXXX", dp);
164 if ((fd = rcv_mktemp(sp, path, dp, S_IRWXU)) == -1)
165 goto err;
166 (void)close(fd);
168 if ((ep->rcv_path = strdup(path)) == NULL) {
169 msgq(sp, M_SYSERR, NULL);
170 (void)unlink(path);
171 err: msgq(sp, M_ERR,
172 "056|Modifications not recoverable if the session fails");
173 return (1);
176 /* We believe the file is recoverable. */
177 F_SET(ep, F_RCV_ON);
178 return (0);
182 * rcv_init --
183 * Force the file to be snapshotted for recovery.
185 * PUBLIC: int rcv_init __P((SCR *));
188 rcv_init(SCR *sp)
190 EXF *ep;
191 db_recno_t lno;
193 ep = sp->ep;
195 /* Only do this once. */
196 F_CLR(ep, F_FIRSTMODIFY);
198 /* If we already know the file isn't recoverable, we're done. */
199 if (!F_ISSET(ep, F_RCV_ON))
200 return (0);
202 /* Turn off recoverability until we figure out if this will work. */
203 F_CLR(ep, F_RCV_ON);
205 /* Test if we're recovering a file, not editing one. */
206 if (ep->rcv_mpath == NULL) {
207 /* Build a file to mail to the user. */
208 if (rcv_mailfile(sp, 0, NULL))
209 goto err;
211 /* Force a read of the entire file. */
212 if (db_last(sp, &lno))
213 goto err;
215 /* Turn on a busy message, and sync it to backing store. */
216 sp->gp->scr_busy(sp,
217 "057|Copying file for recovery...", BUSY_ON);
218 if (ep->db->sync(ep->db, 0)) {
219 msgq_str(sp, M_SYSERR, ep->rcv_path,
220 "058|Preservation failed: %s");
221 sp->gp->scr_busy(sp, NULL, BUSY_OFF);
222 goto err;
224 sp->gp->scr_busy(sp, NULL, BUSY_OFF);
227 /* Turn off the owner execute bit. */
228 (void)chmod(ep->rcv_path, S_IRUSR | S_IWUSR);
230 /* We believe the file is recoverable. */
231 F_SET(ep, F_RCV_ON);
232 return (0);
234 err: msgq(sp, M_ERR,
235 "059|Modifications not recoverable if the session fails");
236 return (1);
240 * rcv_sync --
241 * Sync the file, optionally:
242 * flagging the backup file to be preserved
243 * snapshotting the backup file and send email to the user
244 * sending email to the user if the file was modified
245 * ending the file session
247 * PUBLIC: int rcv_sync __P((SCR *, u_int));
250 rcv_sync(SCR *sp, u_int flags)
252 EXF *ep;
253 int fd, rval;
254 char buf[1024];
255 const char *dp;
257 /* Make sure that there's something to recover/sync. */
258 ep = sp->ep;
259 if (ep == NULL || !F_ISSET(ep, F_RCV_ON))
260 return (0);
262 /* Sync the file if it's been modified. */
263 if (F_ISSET(ep, F_MODIFIED)) {
264 if (ep->db->sync(ep->db, 0)) {
265 F_CLR(ep, F_RCV_ON | F_RCV_NORM);
266 msgq_str(sp, M_SYSERR,
267 ep->rcv_path, "060|File backup failed: %s");
268 return (1);
271 /* REQUEST: don't remove backing file on exit. */
272 if (LF_ISSET(RCV_PRESERVE))
273 F_SET(ep, F_RCV_NORM);
275 /* REQUEST: send email. */
276 if (LF_ISSET(RCV_EMAIL))
277 rcv_email(sp, ep->rcv_mpath);
281 * !!!
282 * Each time the user exec's :preserve, we have to snapshot all of
283 * the recovery information, i.e. it's like the user re-edited the
284 * file. We copy the DB(3) backing file, and then create a new mail
285 * recovery file, it's simpler than exiting and reopening all of the
286 * underlying files.
288 * REQUEST: snapshot the file.
290 rval = 0;
291 if (LF_ISSET(RCV_SNAPSHOT)) {
292 if (opts_empty(sp, O_RECDIR, 0))
293 goto err;
294 dp = O_STR(sp, O_RECDIR);
295 (void)snprintf(buf, sizeof(buf), "%s/vi.XXXXXX", dp);
296 if ((fd = rcv_mktemp(sp, buf, dp, S_IRUSR | S_IWUSR)) == -1)
297 goto err;
298 sp->gp->scr_busy(sp,
299 "061|Copying file for recovery...", BUSY_ON);
300 if (rcv_copy(sp, fd, ep->rcv_path) ||
301 close(fd) || rcv_mailfile(sp, 1, buf)) {
302 (void)unlink(buf);
303 (void)close(fd);
304 rval = 1;
306 sp->gp->scr_busy(sp, NULL, BUSY_OFF);
308 if (0) {
309 err: rval = 1;
312 /* REQUEST: end the file session. */
313 if (LF_ISSET(RCV_ENDSESSION) && file_end(sp, NULL, 1))
314 rval = 1;
316 return (rval);
320 * rcv_mailfile --
321 * Build the file to mail to the user.
323 static int
324 rcv_mailfile(SCR *sp, int issync, char *cp_path)
326 EXF *ep;
327 GS *gp;
328 struct passwd *pw;
329 size_t len;
330 time_t now;
331 uid_t uid;
332 int fd;
333 char *p, *t, buf[4096], mpath[MAXPATHLEN];
334 const char *dp;
335 char *t1, *t2, *t3;
338 * XXX
339 * MAXHOSTNAMELEN is in various places on various systems, including
340 * <netdb.h> and <sys/socket.h>. If not found, use a large default.
342 #ifndef MAXHOSTNAMELEN
343 #define MAXHOSTNAMELEN 1024
344 #endif
345 char host[MAXHOSTNAMELEN];
347 gp = sp->gp;
348 if ((pw = getpwuid(uid = getuid())) == NULL) {
349 msgq(sp, M_ERR,
350 "062|Information on user id %u not found", uid);
351 return (1);
354 if (opts_empty(sp, O_RECDIR, 0))
355 return (1);
356 dp = O_STR(sp, O_RECDIR);
357 (void)snprintf(mpath, sizeof(mpath), "%s/recover.XXXXXX", dp);
358 if ((fd = rcv_mktemp(sp, mpath, dp, S_IRUSR | S_IWUSR)) == -1)
359 return (1);
362 * XXX
363 * We keep an open lock on the file so that the recover option can
364 * distinguish between files that are live and those that need to
365 * be recovered. There's an obvious window between the mkstemp call
366 * and the lock, but it's pretty small.
368 ep = sp->ep;
369 if (file_lock(sp, NULL, NULL, fd, 1) != LOCK_SUCCESS)
370 msgq(sp, M_SYSERR, "063|Unable to lock recovery file");
371 if (!issync) {
372 /* Save the recover file descriptor, and mail path. */
373 ep->rcv_fd = fd;
374 if ((ep->rcv_mpath = strdup(mpath)) == NULL) {
375 msgq(sp, M_SYSERR, NULL);
376 goto err;
378 cp_path = ep->rcv_path;
382 * XXX
383 * We can't use stdio(3) here. The problem is that we may be using
384 * fcntl(2), so if ANY file descriptor into the file is closed, the
385 * lock is lost. So, we could never close the FILE *, even if we
386 * dup'd the fd first.
388 t = sp->frp->name;
389 if ((p = strrchr(t, '/')) == NULL)
390 p = t;
391 else
392 ++p;
393 (void)time(&now);
394 (void)gethostname(host, sizeof(host));
395 len = snprintf(buf, sizeof(buf),
396 "%s%s\n%s%s\n%s\n%s\n%s%s\n%s%s\n%s\n\n",
397 VI_FHEADER, t, /* Non-standard. */
398 VI_PHEADER, cp_path, /* Non-standard. */
399 "Reply-To: root",
400 "From: root (Nvi recovery program)",
401 "To: ", pw->pw_name,
402 "Subject: Nvi saved the file ", p,
403 "Precedence: bulk"); /* For vacation(1). */
404 if (len > sizeof(buf) - 1)
405 goto lerr;
406 if ((size_t)write(fd, buf, len) != len)
407 goto werr;
409 len = snprintf(buf, sizeof(buf),
410 "%s%.24s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n\n",
411 "On ", ctime(&now), ", the user ", pw->pw_name,
412 " was editing a file named ", t, " on the machine ",
413 host, ", when it was saved for recovery. ",
414 "You can recover most, if not all, of the changes ",
415 "to this file using the -r option to ", gp->progname, ":\n\n\t",
416 gp->progname, " -r ", t);
417 if (len > sizeof(buf) - 1) {
418 lerr: msgq(sp, M_ERR, "064|Recovery file buffer overrun");
419 goto err;
423 * Format the message. (Yes, I know it's silly.)
424 * Requires that the message end in a <newline>.
426 #define FMTCOLS 60
427 for (t1 = buf; len > 0; len -= t2 - t1, t1 = t2) {
428 /* Check for a short length. */
429 if (len <= FMTCOLS) {
430 t2 = t1 + (len - 1);
431 goto wout;
434 /* Check for a required <newline>. */
435 t2 = strchr(t1, '\n');
436 if (t2 - t1 <= FMTCOLS)
437 goto wout;
439 /* Find the closest space, if any. */
440 for (t3 = t2; t2 > t1; --t2)
441 if (*t2 == ' ') {
442 if (t2 - t1 <= FMTCOLS)
443 goto wout;
444 t3 = t2;
446 t2 = t3;
448 /* t2 points to the last character to display. */
449 wout: *t2++ = '\n';
451 /* t2 points one after the last character to display. */
452 if (write(fd, t1, t2 - t1) != t2 - t1)
453 goto werr;
456 if (issync) {
457 rcv_email(sp, mpath);
458 if (close(fd)) {
459 werr: msgq(sp, M_SYSERR, "065|Recovery file");
460 goto err;
463 return (0);
465 err: if (!issync)
466 ep->rcv_fd = -1;
467 if (fd != -1)
468 (void)close(fd);
469 return (1);
473 * people making love
474 * never exactly the same
475 * just like a snowflake
477 * rcv_list --
478 * List the files that can be recovered by this user.
480 * PUBLIC: int rcv_list __P((SCR *));
483 rcv_list(SCR *sp)
485 struct dirent *dp;
486 struct stat sb;
487 DIR *dirp;
488 FILE *fp;
489 int found;
490 char *p, *t;
491 const char *d;
492 char file[MAXPATHLEN], path[MAXPATHLEN];
494 /* Open the recovery directory for reading. */
495 if (opts_empty(sp, O_RECDIR, 0))
496 return (1);
497 d = O_STR(sp, O_RECDIR);
498 if (chdir(d) || (dirp = opendir(".")) == NULL) {
499 msgq_str(sp, M_SYSERR, d, "recdir: %s");
500 return (1);
503 /* Read the directory. */
504 for (found = 0; (dp = readdir(dirp)) != NULL;) {
505 if (strncmp(dp->d_name, "recover.", 8))
506 continue;
509 * If it's readable, it's recoverable.
511 * XXX
512 * Should be "r", we don't want to write the file. However,
513 * if we're using fcntl(2), there's no way to lock a file
514 * descriptor that's not open for writing.
516 if ((fp = fopen(dp->d_name, "r+")) == NULL)
517 continue;
519 switch (file_lock(sp, NULL, NULL, fileno(fp), 1)) {
520 case LOCK_FAILED:
522 * XXX
523 * Assume that a lock can't be acquired, but that we
524 * should permit recovery anyway. If this is wrong,
525 * and someone else is using the file, we're going to
526 * die horribly.
528 break;
529 case LOCK_SUCCESS:
530 break;
531 case LOCK_UNAVAIL:
532 /* If it's locked, it's live. */
533 (void)fclose(fp);
534 continue;
537 /* Check the headers. */
538 if (fgets(file, sizeof(file), fp) == NULL ||
539 strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) ||
540 (p = strchr(file, '\n')) == NULL ||
541 fgets(path, sizeof(path), fp) == NULL ||
542 strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) ||
543 (t = strchr(path, '\n')) == NULL) {
544 msgq_str(sp, M_ERR, dp->d_name,
545 "066|%s: malformed recovery file");
546 goto next;
548 *p = *t = '\0';
551 * If the file doesn't exist, it's an orphaned recovery file,
552 * toss it.
554 * XXX
555 * This can occur if the backup file was deleted and we crashed
556 * before deleting the email file.
558 errno = 0;
559 if (stat(path + sizeof(VI_PHEADER) - 1, &sb) &&
560 errno == ENOENT) {
561 (void)unlink(dp->d_name);
562 goto next;
565 /* Get the last modification time and display. */
566 (void)fstat(fileno(fp), &sb);
567 (void)printf("%.24s: %s\n",
568 ctime(&sb.st_mtime), file + sizeof(VI_FHEADER) - 1);
569 found = 1;
571 /* Close, discarding lock. */
572 next: (void)fclose(fp);
574 if (found == 0)
575 (void)printf("vi: no files to recover.\n");
576 (void)closedir(dirp);
577 return (0);
581 * rcv_read --
582 * Start a recovered file as the file to edit.
584 * PUBLIC: int rcv_read __P((SCR *, FREF *));
587 rcv_read(SCR *sp, FREF *frp)
589 struct dirent *dp;
590 struct stat sb;
591 DIR *dirp;
592 EXF *ep;
593 time_t rec_mtime;
594 int fd, found, locked = 0, requested, sv_fd;
595 char *name, *p, *t, *recp, *pathp;
596 const char *rp;
597 char file[MAXPATHLEN], path[MAXPATHLEN], recpath[MAXPATHLEN];
599 if (opts_empty(sp, O_RECDIR, 0))
600 return (1);
601 rp = O_STR(sp, O_RECDIR);
602 if ((dirp = opendir(rp)) == NULL) {
603 msgq_str(sp, M_ERR, rp, "%s");
604 return (1);
607 name = frp->name;
608 sv_fd = -1;
609 rec_mtime = 0;
610 recp = pathp = NULL;
611 for (found = requested = 0; (dp = readdir(dirp)) != NULL;) {
612 if (strncmp(dp->d_name, "recover.", 8))
613 continue;
614 (void)snprintf(recpath,
615 sizeof(recpath), "%s/%s", rp, dp->d_name);
618 * If it's readable, it's recoverable. It would be very
619 * nice to use stdio(3), but, we can't because that would
620 * require closing and then reopening the file so that we
621 * could have a lock and still close the FP. Another tip
622 * of the hat to fcntl(2).
624 * XXX
625 * Should be O_RDONLY, we don't want to write it. However,
626 * if we're using fcntl(2), there's no way to lock a file
627 * descriptor that's not open for writing.
629 if ((fd = open(recpath, O_RDWR, 0)) == -1)
630 continue;
632 switch (file_lock(sp, NULL, NULL, fd, 1)) {
633 case LOCK_FAILED:
635 * XXX
636 * Assume that a lock can't be acquired, but that we
637 * should permit recovery anyway. If this is wrong,
638 * and someone else is using the file, we're going to
639 * die horribly.
641 locked = 0;
642 break;
643 case LOCK_SUCCESS:
644 locked = 1;
645 break;
646 case LOCK_UNAVAIL:
647 /* If it's locked, it's live. */
648 (void)close(fd);
649 continue;
652 /* Check the headers. */
653 if (rcv_gets(file, sizeof(file), fd) == NULL ||
654 strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) ||
655 (p = strchr(file, '\n')) == NULL ||
656 rcv_gets(path, sizeof(path), fd) == NULL ||
657 strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) ||
658 (t = strchr(path, '\n')) == NULL) {
659 msgq_str(sp, M_ERR, recpath,
660 "067|%s: malformed recovery file");
661 goto next;
663 *p = *t = '\0';
664 ++found;
667 * If the file doesn't exist, it's an orphaned recovery file,
668 * toss it.
670 * XXX
671 * This can occur if the backup file was deleted and we crashed
672 * before deleting the email file.
674 errno = 0;
675 if (stat(path + sizeof(VI_PHEADER) - 1, &sb) &&
676 errno == ENOENT) {
677 (void)unlink(dp->d_name);
678 goto next;
681 /* Check the file name. */
682 if (strcmp(file + sizeof(VI_FHEADER) - 1, name))
683 goto next;
685 ++requested;
688 * If we've found more than one, take the most recent.
690 * XXX
691 * Since we're using st_mtime, for portability reasons,
692 * we only get a single second granularity, instead of
693 * getting it right.
695 (void)fstat(fd, &sb);
696 if (recp == NULL || rec_mtime < sb.st_mtime) {
697 p = recp;
698 t = pathp;
699 if ((recp = strdup(recpath)) == NULL) {
700 msgq(sp, M_SYSERR, NULL);
701 recp = p;
702 goto next;
704 if ((pathp = strdup(path)) == NULL) {
705 msgq(sp, M_SYSERR, NULL);
706 free(recp);
707 recp = p;
708 pathp = t;
709 goto next;
711 if (p != NULL) {
712 free(p);
713 free(t);
715 rec_mtime = sb.st_mtime;
716 if (sv_fd != -1)
717 (void)close(sv_fd);
718 sv_fd = fd;
719 } else
720 next: (void)close(fd);
722 (void)closedir(dirp);
724 if (recp == NULL) {
725 msgq_str(sp, M_INFO, name,
726 "068|No files named %s, readable by you, to recover");
727 return (1);
729 if (found) {
730 if (requested > 1)
731 msgq(sp, M_INFO,
732 "069|There are older versions of this file for you to recover");
733 if (found > requested)
734 msgq(sp, M_INFO,
735 "070|There are other files for you to recover");
739 * Create the FREF structure, start the btree file.
741 * XXX
742 * file_init() is going to set ep->rcv_path.
744 if (file_init(sp, frp, pathp + sizeof(VI_PHEADER) - 1, 0)) {
745 free(recp);
746 free(pathp);
747 (void)close(sv_fd);
748 return (1);
752 * We keep an open lock on the file so that the recover option can
753 * distinguish between files that are live and those that need to
754 * be recovered. The lock is already acquired, just copy it.
756 ep = sp->ep;
757 ep->rcv_mpath = recp;
758 ep->rcv_fd = sv_fd;
759 if (!locked)
760 F_SET(frp, FR_UNLOCKED);
762 /* We believe the file is recoverable. */
763 F_SET(ep, F_RCV_ON);
764 return (0);
768 * rcv_copy --
769 * Copy a recovery file.
771 static int
772 rcv_copy(SCR *sp, int wfd, char *fname)
774 int nr, nw, off, rfd;
775 char buf[8 * 1024];
777 if ((rfd = open(fname, O_RDONLY, 0)) == -1)
778 goto err;
779 while ((nr = read(rfd, buf, sizeof(buf))) > 0)
780 for (off = 0; nr; nr -= nw, off += nw)
781 if ((nw = write(wfd, buf + off, nr)) < 0)
782 goto err;
783 if (nr == 0)
784 return (0);
786 err: msgq_str(sp, M_SYSERR, fname, "%s");
787 return (1);
791 * rcv_gets --
792 * Fgets(3) for a file descriptor.
794 static char *
795 rcv_gets(char *buf, size_t len, int fd)
797 int nr;
798 char *p;
800 if ((nr = read(fd, buf, len - 1)) == -1)
801 return (NULL);
802 if ((p = strchr(buf, '\n')) == NULL)
803 return (NULL);
804 (void)lseek(fd, (off_t)((p - buf) + 1), SEEK_SET);
805 return (buf);
809 * rcv_mktemp --
810 * Paranoid make temporary file routine.
812 static int
813 rcv_mktemp(SCR *sp, char *path, const char *dname, int perms)
815 int fd;
818 * !!!
819 * We expect mkstemp(3) to set the permissions correctly. On
820 * historic System V systems, mkstemp didn't. Do it here, on
821 * GP's.
823 * XXX
824 * The variable perms should really be a mode_t, and it would
825 * be nice to use fchmod(2) instead of chmod(2), here.
827 if ((fd = mkstemp(path)) == -1)
828 msgq_str(sp, M_SYSERR, dname, "%s");
829 else
830 (void)chmod(path, perms);
831 return (fd);
835 * rcv_email --
836 * Send email.
838 static void
839 rcv_email(SCR *sp, char *fname)
841 struct stat sb;
842 char buf[MAXPATHLEN * 2 + 20];
844 if (_PATH_SENDMAIL[0] != '/' || stat(_PATH_SENDMAIL, &sb))
845 msgq_str(sp, M_SYSERR,
846 _PATH_SENDMAIL, "071|not sending email: %s");
847 else {
849 * !!!
850 * If you need to port this to a system that doesn't have
851 * sendmail, the -t flag causes sendmail to read the message
852 * for the recipients instead of specifying them some other
853 * way.
855 (void)snprintf(buf, sizeof(buf),
856 "%s -t < %s", _PATH_SENDMAIL, fname);
857 (void)system(buf);