/* $Id: vidb.C,v 1.1 2001/08/20 04:43:24 dm Exp $ */ /* * * Copyright (C) 2001 David Mazieres (dm@uun.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "authdb.h" #include "sfsmisc.h" #include "init.h" str db; mode_t dbmode; str editor; ptr dblock; str dottmp; str tmppath; EXITFN (cleanup); static void cleanup () { if (tmppath) unlink (tmppath); } static void lockit (str file, bool wait) { dblock = lockfile::alloc (file << ".lock"); if (!dblock) { if (!wait) fatal << file << ": could not lock file\n"; warn << ": waiting for lock on " << file << "..."; dblock = lockfile::alloc (file << ".lock", true); warnx << "\n"; } // In case we die but not editor, best NOT to close on exec fcntl (dblock->fd, F_SETFD, 0); dottmp = file << ".tmp"; } static void copyit (str file) { int rfd = open (file, O_RDONLY); struct stat sb; if (rfd < 0 || fstat (rfd, &sb) < 0) fatal << file << ": " << strerror (errno) << "\n"; dbmode = sb.st_mode; int wfd = open (dottmp, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0600 | dbmode & 0777); if (wfd < 0) { if (errno == EEXIST) fatal << dottmp << " exists, please remove file or use -r flag\n"; fatal << dottmp << ": " << strerror (errno) << "\n"; } tmppath = dottmp; char buf[8192]; int n; while ((n = read (rfd, buf, sizeof (buf))) > 0) if (write (wfd, buf, n) != n) fatal << tmppath << ": " << strerror (errno) << "\n"; if (n < 0) fatal << file << ": " << strerror (errno) << "\n"; close (rfd); close (wfd); } static void finish (int status) { if (status) fatal << editor << ": abnormal exit\n"; int wfd = open (tmppath, O_WRONLY); if (wfd < 0 || fsync (wfd) < 0 || close (wfd) < 0 || chmod (tmppath, dbmode) < 0) fatal << tmppath << ": " << strerror (errno) << "\n"; if (!dblock->ok ()) { str target = db << ".bak"; if (rename (dottmp, target) < 0) fatal << dottmp << ": " << strerror (errno) << "\n"; dblock = NULL; tmppath = NULL; fatal << "lock was stolen -- file saved in " << target << "\n"; } if (rename (tmppath, db) < 0) fatal << db << ": " << strerror (errno) << "\n"; exit (0); } static void usage () { warnx << "usage: " << progname << " [-r] [-w] [-e editor] dbfile\n"; exit (1); } int main (int argc, char **argv) { setprogname (argv[0]); editor = getenv ("EDITOR"); bool opt_recover = false, opt_wait = false; int ch; while ((ch = getopt (argc, argv, "rwe:")) != -1) switch (ch) { case 'r': opt_recover = true; break; case 'w': opt_wait = true; break; case 'e': editor = optarg; break; default: usage (); } if (optind + 1 != argc) usage (); db = argv[optind]; if (!editor) editor = "vi"; if (str edpath = find_program (editor)) editor = edpath; else fatal << editor << ": " << strerror (errno) << "\n"; lockit (db, opt_wait); if (opt_recover) { str target = db << ".bak"; if (rename (dottmp, target) == 0) warn << "saved old " << dottmp << " in " << target << "\n"; } copyit (db); const char *av[] = { editor, tmppath, NULL }; pid_t pid = spawn (editor, av); if (pid == -1) fatal << editor << ": " << strerror (errno) << "\n"; int status = -1; waitpid (pid, &status, 0); finish (status); return 0; }