]> granicus.if.org Git - shadow/blob - src/vipw.c
[svn-upgrade] Integrating new upstream version, shadow (20000826)
[shadow] / src / vipw.c
1 /*
2   vipw, vigr  edit the password or group file
3   with -s will edit shadow or gshadow file
4  
5   Copyright (C) 1997 Guy Maor <maor@ece.utexas.edu>
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21   */
22
23 #include <config.h>
24
25 #include "rcsid.h"
26 RCSID(PKG_VER "$Id: vipw.c,v 1.2 2000/08/26 18:27:19 marekm Exp $")
27
28 #include "defines.h"
29
30 #include <errno.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <signal.h>
37 #include <utime.h>
38 #include "prototypes.h"
39 #include "pwio.h"
40 #include "shadowio.h"
41 #include "groupio.h"
42 #include "sgroupio.h"
43
44
45 static const char *progname, *filename, *fileeditname;
46 static int filelocked = 0, createedit = 0;
47 static int (*unlock)(void);
48
49 /* local function prototypes */
50 static int create_backup_file(FILE *, const char *, struct stat *);
51 static void vipwexit(const char *, int, int);
52 static void vipwedit(const char *, int (*)(void), int (*)(void));
53
54 static int
55 create_backup_file(FILE *fp, const char *backup, struct stat *sb)
56 {
57   struct utimbuf ub;
58   FILE *bkfp;
59   int c;
60   mode_t mask;
61
62   mask = umask(077);
63   bkfp = fopen(backup, "w");
64   umask(mask);
65   if (!bkfp) return -1;
66
67   rewind(fp);
68   while ((c = getc(fp)) != EOF) {
69     if (putc(c, bkfp) == EOF) break;
70   }
71
72   if (c != EOF || fflush(bkfp)) {
73     fclose(bkfp);
74     unlink(backup);
75     return -1;
76   }
77   if (fclose(bkfp)) {
78     unlink(backup);
79     return -1;
80   }
81
82   ub.actime = sb->st_atime;
83   ub.modtime = sb->st_mtime;
84   if (utime(backup, &ub) ||
85       chmod(backup, sb->st_mode) ||
86       chown(backup, sb->st_uid, sb->st_gid)) {
87     unlink(backup);
88     return -1;
89   }
90   return 0;
91 }
92
93
94 static void
95 vipwexit(const char *msg, int syserr, int ret)
96 {
97   int err = errno;
98   if (filelocked) (*unlock)();
99   if (createedit) unlink(fileeditname);
100   if (msg) fprintf(stderr, "%s: %s", progname, msg);
101   if (syserr) fprintf(stderr, ": %s", strerror(err));
102   fprintf(stderr, _("\n%s: %s is unchanged\n"), progname, filename);
103   exit(ret);
104 }
105
106 #ifndef DEFAULT_EDITOR
107 #define DEFAULT_EDITOR "vi"
108 #endif
109
110 static void
111 vipwedit(const char *file, int (*file_lock)(void), int (*file_unlock)(void))
112 {
113   const char *editor;
114   pid_t pid;
115   struct stat st1, st2;
116   int status;
117   FILE *f;
118   char filebackup[1024], fileedit[1024];
119
120   snprintf(filebackup, sizeof filebackup, "%s-", file);
121   snprintf(fileedit, sizeof fileedit, "%s.edit", file);
122   unlock = file_unlock;
123   filename = file;
124   fileeditname = fileedit;
125   
126   if (access(file, F_OK)) vipwexit(file, 1, 1);
127   if (!file_lock()) vipwexit(_("Couldn't lock file"), errno, 5);
128   filelocked = 1;
129
130   /* edited copy has same owners, perm */
131   if (stat(file, &st1)) vipwexit(file, 1, 1);
132   if (!(f = fopen(file, "r"))) vipwexit(file, 1, 1);
133   if (create_backup_file(f, fileedit, &st1))
134     vipwexit(_("Couldn't make backup"), errno, 1);
135   createedit = 1;
136   
137   editor = getenv("VISUAL");
138   if (!editor)
139     editor = getenv("EDITOR");
140   if (!editor)
141     editor = DEFAULT_EDITOR;
142   
143   if ((pid = fork()) == -1) vipwexit("fork", 1, 1);
144   else if (!pid) {
145 #if 0
146     execlp(editor, editor, fileedit, (char *) 0);
147     fprintf(stderr, "%s: %s: %s\n", progname, editor, strerror(errno));
148     exit(1);
149 #else
150     /* use the system() call to invoke the editor so that it accepts
151        command line args in the EDITOR and VISUAL environment vars */
152     char *buf;
153     buf = (char *) malloc (strlen(editor) + strlen(fileedit) + 2);
154     snprintf(buf, strlen(editor) + strlen(fileedit) + 2, "%s %s",
155              editor, fileedit);
156     if (system(buf) != 0) {
157        fprintf(stderr, "%s: %s: %s\n", progname, editor, strerror(errno));
158        exit(1);
159     } else
160        exit(0);
161 #endif
162   }
163
164   for (;;) {
165     pid = waitpid(pid, &status, WUNTRACED);
166     if (WIFSTOPPED(status)) {
167       kill(getpid(), SIGSTOP);
168       kill(getpid(), SIGCONT);
169     }
170     else break;
171   }
172
173   if (pid == -1 || !WIFEXITED(status) || WEXITSTATUS(status))
174     vipwexit(editor, 1, 1);
175
176   if (stat(fileedit, &st2)) vipwexit(fileedit, 1, 1);
177   if (st1.st_mtime == st2.st_mtime) vipwexit(0, 0, 0);
178
179   /* XXX - here we should check fileedit for errors; if there are any,
180      ask the user what to do (edit again, save changes anyway, or quit
181      without saving).  Use pwck or grpck to do the check.  --marekm */
182
183   createedit = 0;
184   unlink(filebackup);
185   link(file, filebackup);
186   if (rename(fileedit, file) == -1) {
187     fprintf(stderr, _("%s: can't restore %s: %s (your changes are in %s)\n"),
188             progname, file, strerror(errno), fileedit);
189     vipwexit(0,0,1);
190   }
191
192   (*file_unlock)();
193 }
194
195
196 int
197 main(int argc, char **argv)
198 {
199   int flag;
200   int editshadow = 0;
201   char *c;
202   int e = 1;
203   int do_vipw;
204
205   setlocale(LC_ALL, "");
206   bindtextdomain(PACKAGE, LOCALEDIR);
207   textdomain(PACKAGE);
208
209   progname = ((c = strrchr(*argv, '/')) ? c+1 : *argv);
210   do_vipw = (strcmp(progname, "vigr") != 0);
211
212   while ((flag = getopt(argc, argv, "ghps")) != EOF) {
213     switch (flag) {
214     case 'p':
215       do_vipw = 1;
216       break;
217     case 'g':
218       do_vipw = 0;
219       break;
220     case 's':
221       editshadow = 1;
222       break;
223     case 'h':
224       e = 0;
225     default:
226       printf(_("Usage:\n\
227 `vipw' edits /etc/passwd        `vipw -s' edits /etc/shadow\n\
228 `vigr' edits /etc/group         `vigr -s' edits /etc/gshadow\n\
229 "));
230       exit(e);
231     }
232   }
233
234   if (do_vipw) {
235 #ifdef SHADOWPWD
236     if (editshadow)
237       vipwedit(SHADOW_FILE, spw_lock, spw_unlock);
238     else
239 #endif
240       vipwedit(PASSWD_FILE, pw_lock, pw_unlock);
241   }
242   else {
243 #ifdef SHADOWGRP
244     if (editshadow)
245       vipwedit(SGROUP_FILE, sgr_lock, sgr_unlock);
246     else
247 #endif
248       vipwedit(GROUP_FILE, gr_lock, gr_unlock);
249   }
250
251   return 0;
252 }