shadow/src/vipw.c

336 lines
7.5 KiB
C
Raw Normal View History

/*
vipw, vigr edit the password or group file
with -s will edit shadow or gshadow file
Copyright (C) 1997 Guy Maor <maor@ece.utexas.edu>
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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <config.h>
#ident "$Id$"
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <utime.h>
#include "defines.h"
#include "exitcodes.h"
#include "groupio.h"
#include "nscd.h"
#include "prototypes.h"
#include "pwio.h"
#include "sgroupio.h"
#include "shadowio.h"
#define MSG_WARN_EDIT_OTHER_FILE _( \
"You have modified %s.\n"\
"You may need to modify %s for consistency.\n"\
"Please use the command `%s' to do so.\n")
/*
* Global variables
*/
static const char *progname, *filename, *fileeditname;
static int filelocked = 0, createedit = 0;
static int (*unlock) (void);
static int quiet = 0;
/* local function prototypes */
static void usage (void);
static int create_backup_file (FILE *, const char *, struct stat *);
static void vipwexit (const char *, int, int);
static void vipwedit (const char *, int (*)(void), int (*)(void));
/*
* usage - display usage message and exit
*/
static void usage (void)
{
fprintf (stderr, _("Usage: vipw [options]\n"
"\n"
"Options:\n"
" -g, --group edit group database\n"
" -h, --help display this help message and exit\n"
" -p, --passwd edit passwd database\n"
" -q, --quiet quiet mode\n"
" -s, --shadow edit shadow or gshadow database\n"
"\n"));
exit (E_USAGE);
}
/*
*
*/
static int create_backup_file (FILE * fp, const char *backup, struct stat *sb)
{
struct utimbuf ub;
FILE *bkfp;
int c;
mode_t mask;
mask = umask (077);
bkfp = fopen (backup, "w");
umask (mask);
if (!bkfp)
return -1;
c = 0;
if (fseeko (fp, 0, SEEK_SET) == 0)
while ((c = getc (fp)) != EOF) {
if (putc (c, bkfp) == EOF)
break;
}
if (c != EOF || ferror (fp) || fflush (bkfp)) {
fclose (bkfp);
unlink (backup);
return -1;
}
if (fclose (bkfp)) {
unlink (backup);
return -1;
}
ub.actime = sb->st_atime;
ub.modtime = sb->st_mtime;
if (utime (backup, &ub) ||
chmod (backup, sb->st_mode) ||
chown (backup, sb->st_uid, sb->st_gid)) {
unlink (backup);
return -1;
}
return 0;
}
/*
*
*/
static void vipwexit (const char *msg, int syserr, int ret)
{
int err = errno;
if (createedit)
unlink (fileeditname);
if (filelocked)
(*unlock) ();
if (msg)
fprintf (stderr, "%s: %s", progname, msg);
if (syserr)
fprintf (stderr, ": %s", strerror (err));
if (!quiet)
fprintf (stdout, _("\n%s: %s is unchanged\n"), progname,
filename);
exit (ret);
}
#ifndef DEFAULT_EDITOR
#define DEFAULT_EDITOR "vi"
#endif
/*
*
*/
static void
vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void))
{
const char *editor;
pid_t pid;
struct stat st1, st2;
int status;
FILE *f;
char filebackup[1024], fileedit[1024];
snprintf (filebackup, sizeof filebackup, "%s-", file);
snprintf (fileedit, sizeof fileedit, "%s.edit", file);
unlock = file_unlock;
filename = file;
fileeditname = fileedit;
if (access (file, F_OK))
vipwexit (file, 1, 1);
if (!file_lock ())
vipwexit (_("Couldn't lock file"), errno, 5);
filelocked = 1;
/* edited copy has same owners, perm */
if (stat (file, &st1))
vipwexit (file, 1, 1);
if (!(f = fopen (file, "r")))
vipwexit (file, 1, 1);
if (create_backup_file (f, fileedit, &st1))
vipwexit (_("Couldn't make backup"), errno, 1);
createedit = 1;
editor = getenv ("VISUAL");
if (!editor)
editor = getenv ("EDITOR");
if (!editor)
editor = DEFAULT_EDITOR;
if ((pid = fork ()) == -1)
vipwexit ("fork", 1, 1);
else if (!pid) {
/* use the system() call to invoke the editor so that it accepts
command line args in the EDITOR and VISUAL environment vars */
char *buf;
buf = (char *) malloc (strlen (editor) + strlen (fileedit) + 2);
snprintf (buf, strlen (editor) + strlen (fileedit) + 2,
"%s %s", editor, fileedit);
if (system (buf) != 0) {
fprintf (stderr, "%s: %s: %s\n", progname, editor,
strerror (errno));
exit (1);
} else
exit (0);
}
for (;;) {
pid = waitpid (pid, &status, WUNTRACED);
if (WIFSTOPPED (status)) {
kill (getpid (), SIGSTOP);
kill (getpid (), SIGCONT);
} else
break;
}
if (pid == -1 || !WIFEXITED (status) || WEXITSTATUS (status))
vipwexit (editor, 1, 1);
if (stat (fileedit, &st2))
vipwexit (fileedit, 1, 1);
if (st1.st_mtime == st2.st_mtime)
vipwexit (0, 0, 0);
/*
* XXX - here we should check fileedit for errors; if there are any,
* ask the user what to do (edit again, save changes anyway, or quit
* without saving). Use pwck or grpck to do the check. --marekm
*/
createedit = 0;
unlink (filebackup);
link (file, filebackup);
if (rename (fileedit, file) == -1) {
fprintf (stderr,
_
("%s: can't restore %s: %s (your changes are in %s)\n"),
progname, file, strerror (errno), fileedit);
vipwexit (0, 0, 1);
}
(*file_unlock) ();
}
int main (int argc, char **argv)
{
int editshadow = 0;
char *a;
int do_vipw;
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
progname = ((a = strrchr (*argv, '/')) ? a + 1 : *argv);
do_vipw = (strcmp (progname, "vigr") != 0);
{
/*
* Parse the command line options.
*/
int c;
static struct option long_options[] = {
{"group", no_argument, NULL, 'g'},
{"help", no_argument, NULL, 'h'},
{"passwd", no_argument, NULL, 'p'},
{"quiet", no_argument, NULL, 'q'},
{"shadow", no_argument, NULL, 's'},
};
while ((c =
getopt_long (argc, argv, "ghpqs",
long_options, NULL)) != -1) {
switch (c) {
case 'g':
do_vipw = 0;
break;
case 'h':
usage ();
break;
case 'p':
do_vipw = 1;
break;
case 'q':
quiet = 1;
break;
case 's':
editshadow = 1;
break;
default:
usage ();
}
}
}
if (do_vipw) {
if (editshadow) {
vipwedit (SHADOW_FILE, spw_lock, spw_unlock);
printf (MSG_WARN_EDIT_OTHER_FILE,
SHADOW_FILE,
PASSWD_FILE,
"vipw");
} else {
vipwedit (PASSWD_FILE, pw_lock, pw_unlock);
if (spw_file_present ()) {
printf (MSG_WARN_EDIT_OTHER_FILE,
PASSWD_FILE,
SHADOW_FILE,
"vipw -s");
}
}
} else {
#ifdef SHADOWGRP
if (editshadow) {
vipwedit (SGROUP_FILE, sgr_lock, sgr_unlock);
printf (MSG_WARN_EDIT_OTHER_FILE,
SGROUP_FILE,
GROUP_FILE,
"vigr");
} else {
#endif
vipwedit (GROUP_FILE, gr_lock, gr_unlock);
#ifdef SHADOWGRP
if (sgr_file_present ()) {
printf (MSG_WARN_EDIT_OTHER_FILE,
GROUP_FILE,
SGROUP_FILE,
"vigr -s");
}
}
#endif
}
nscd_flush_cache ("passwd");
nscd_flush_cache ("group");
exit (E_SUCCESS);
}