2 vipw, vigr edit the password or group file
3 with -s will edit shadow or gshadow file
5 Copyright (c) 1997 , Guy Maor <maor@ece.utexas.edu>
6 Copyright (c) 1999 - 2000, Marek Michałkiewicz
7 Copyright (c) 2002 - 2006, Tomasz Kłoczko
8 Copyright (c) 2007 - 2008, Nicolas François
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 Boston, MA 02110-1301, USA. */
28 #ident "$Id: vipw.c 3006 2009-05-25 19:51:23Z nekral-guest $"
33 #include <selinux/selinux.h>
39 #include <sys/types.h>
45 #include "prototypes.h"
50 #include "exitcodes.h"
52 #define MSG_WARN_EDIT_OTHER_FILE _( \
53 "You have modified %s.\n"\
54 "You may need to modify %s for consistency.\n"\
55 "Please use the command '%s' to do so.\n")
60 static const char *progname, *filename, *fileeditname;
61 static bool filelocked = false;
62 static bool createedit = false;
63 static int (*unlock) (void);
64 static bool quiet = false;
66 /* local function prototypes */
67 static void usage (void);
68 static int create_backup_file (FILE *, const char *, struct stat *);
69 static void vipwexit (const char *msg, int syserr, int ret);
70 static void vipwedit (const char *, int (*)(void), int (*)(void));
73 * usage - display usage message and exit
75 static void usage (void)
78 fputs (_("Usage: vipw [options]\n"
81 " -g, --group edit group database\n"
82 " -h, --help display this help message and exit\n"
83 " -p, --passwd edit passwd database\n"
84 " -q, --quiet quiet mode\n"
85 " -s, --shadow edit shadow or gshadow database\n"
93 static int create_backup_file (FILE * fp, const char *backup, struct stat *sb)
101 bkfp = fopen (backup, "w");
108 if (fseeko (fp, 0, SEEK_SET) == 0)
109 while ((c = getc (fp)) != EOF) {
110 if (putc (c, bkfp) == EOF) {
114 if ((EOF != c) || (ferror (fp) != 0) || (fflush (bkfp) != 0)) {
119 if (fsync (fileno (bkfp)) != 0) {
120 (void) fclose (bkfp);
124 if (fclose (bkfp) != 0) {
129 ub.actime = sb->st_atime;
130 ub.modtime = sb->st_mtime;
131 if ( (utime (backup, &ub) != 0)
132 || (chmod (backup, sb->st_mode) != 0)
133 || (chown (backup, sb->st_uid, sb->st_gid) != 0)) {
143 static void vipwexit (const char *msg, int syserr, int ret)
148 if (unlink (fileeditname) != 0) {
149 fprintf (stderr, _("%s: failed to remove %s\n"), progname, fileeditname);
154 if ((*unlock) () == 0) {
155 fprintf (stderr, _("%s: failed to unlock %s\n"), progname, fileeditname);
156 SYSLOG ((LOG_ERR, "failed to unlock %s", fileeditname));
161 fprintf (stderr, "%s: %s", progname, msg);
164 fprintf (stderr, ": %s", strerror (err));
166 (void) fputs ("\n", stderr);
168 fprintf (stdout, _("%s: %s is unchanged\n"), progname,
174 #ifndef DEFAULT_EDITOR
175 #define DEFAULT_EDITOR "vi"
182 vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void))
186 struct stat st1, st2;
189 char filebackup[1024], fileedit[1024];
191 snprintf (filebackup, sizeof filebackup, "%s-", file);
192 snprintf (fileedit, sizeof fileedit, "%s.edit", file);
193 unlock = file_unlock;
195 fileeditname = fileedit;
197 if (access (file, F_OK) != 0) {
198 vipwexit (file, 1, 1);
201 /* if SE Linux is enabled then set the context of all new files
202 to be the context of the file we are editing */
203 if (is_selinux_enabled ()) {
204 security_context_t passwd_context=NULL;
206 if (getfilecon (file, &passwd_context) < 0) {
207 vipwexit (_("Couldn't get file context"), errno, 1);
209 ret = setfscreatecon (passwd_context);
210 freecon (passwd_context);
212 vipwexit (_("setfscreatecon () failed"), errno, 1);
216 if (file_lock () == 0) {
217 vipwexit (_("Couldn't lock file"), errno, 5);
221 /* edited copy has same owners, perm */
222 if (stat (file, &st1) != 0) {
223 vipwexit (file, 1, 1);
225 f = fopen (file, "r");
227 vipwexit (file, 1, 1);
229 if (create_backup_file (f, fileedit, &st1) != 0) {
230 vipwexit (_("Couldn't make backup"), errno, 1);
235 editor = getenv ("VISUAL");
236 if (NULL == editor) {
237 editor = getenv ("EDITOR");
239 if (NULL == editor) {
240 editor = DEFAULT_EDITOR;
245 vipwexit ("fork", 1, 1);
246 } else if (0 == pid) {
247 /* use the system() call to invoke the editor so that it accepts
248 command line args in the EDITOR and VISUAL environment vars */
251 buf = (char *) malloc (strlen (editor) + strlen (fileedit) + 2);
252 snprintf (buf, strlen (editor) + strlen (fileedit) + 2,
253 "%s %s", editor, fileedit);
254 if (system (buf) != 0) {
255 fprintf (stderr, "%s: %s: %s\n", progname, editor,
264 pid = waitpid (pid, &status, WUNTRACED);
265 if ((pid != -1) && (WIFSTOPPED (status) != 0)) {
266 /* The child (editor) was suspended.
268 kill (getpid (), WSTOPSIG(status));
269 /* wake child when resumed */
277 || (WIFEXITED (status) == 0)
278 || (WEXITSTATUS (status) != 0)) {
279 vipwexit (editor, 1, 1);
282 if (stat (fileedit, &st2) != 0) {
283 vipwexit (fileedit, 1, 1);
285 if (st1.st_mtime == st2.st_mtime) {
289 /* unset the fscreatecon */
290 if (is_selinux_enabled ()) {
291 if (setfscreatecon (NULL)) {
292 vipwexit (_("setfscreatecon () failed"), errno, 1);
298 * XXX - here we should check fileedit for errors; if there are any,
299 * ask the user what to do (edit again, save changes anyway, or quit
300 * without saving). Use pwck or grpck to do the check. --marekm
304 link (file, filebackup);
305 if (rename (fileedit, file) == -1) {
307 _("%s: can't restore %s: %s (your changes are in %s)\n"),
308 progname, file, strerror (errno), fileedit);
312 if ((*file_unlock) () == 0) {
313 fprintf (stderr, _("%s: failed to unlock %s\n"), progname, fileeditname);
314 SYSLOG ((LOG_ERR, "failed to unlock %s", fileeditname));
317 SYSLOG ((LOG_INFO, "file %s edited", fileeditname));
320 int main (int argc, char **argv)
322 bool editshadow = false;
326 (void) setlocale (LC_ALL, "");
327 (void) bindtextdomain (PACKAGE, LOCALEDIR);
328 (void) textdomain (PACKAGE);
330 progname = ((a = strrchr (*argv, '/')) ? a + 1 : *argv);
331 do_vipw = (strcmp (progname, "vigr") != 0);
333 OPENLOG (do_vipw ? "vipw" : "vigr");
337 * Parse the command line options.
340 static struct option long_options[] = {
341 {"group", no_argument, NULL, 'g'},
342 {"help", no_argument, NULL, 'h'},
343 {"passwd", no_argument, NULL, 'p'},
344 {"quiet", no_argument, NULL, 'q'},
345 {"shadow", no_argument, NULL, 's'},
346 {NULL, 0, NULL, '\0'}
349 getopt_long (argc, argv, "ghpqs",
350 long_options, NULL)) != -1) {
375 vipwedit (SHADOW_FILE, spw_lock, spw_unlock);
376 printf (MSG_WARN_EDIT_OTHER_FILE,
381 vipwedit (PASSWD_FILE, pw_lock, pw_unlock);
382 if (spw_file_present ()) {
383 printf (MSG_WARN_EDIT_OTHER_FILE,
392 vipwedit (SGROUP_FILE, sgr_lock, sgr_unlock);
393 printf (MSG_WARN_EDIT_OTHER_FILE,
399 vipwedit (GROUP_FILE, gr_lock, gr_unlock);
401 if (sgr_file_present ()) {
402 printf (MSG_WARN_EDIT_OTHER_FILE,
411 nscd_flush_cache ("passwd");
412 nscd_flush_cache ("group");