]> git.pld-linux.org Git - projects/setup.git/blob - joinpasswd.c
fsync/close after loop.
[projects/setup.git] / joinpasswd.c
1 /*
2  * $Id$
3  * 
4  * Copyright (c) 2001 Michal Moskal <malekith@pld-linux.org>.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Michal Moskal.
18  * 4. Neither the name of the author nor the names of any co-contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY MICHAL MOSKAL AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *
35  * 
36  * USAGE: joinpasswd
37  *
38  * Add entries from freshly created {passwd,shadow,group}.rpmnew to existing
39  * {passwd,shadow,group}. It is usable as part of setup package, during upgrade
40  * where new system user/group is to be added. If entry is already found in 
41  * system database, it is left, otherwise it is added. UIDs/GIDs are *not* 
42  * checked anyhow. 
43  *
44  * For typical sizes of files in setup package, it takes about 1 second per
45  * 20000 users in system database on Pentium class machine. After static link
46  * against uClibc it is under 2k on x86. Stdio hasn't been used intentionally.
47  * 
48  * Written for PLD Linux (http://www.pld-linux.org/) setup package.
49  *
50  * Compilation against uClibc:
51  * UCROOT=/usr/lib/bootdisk/usr
52  * gcc -I$UCROOT/include -nostdlib -O2 joinpasswd.c $UCROOT/lib/crt0.o \
53  *              $UCROOT/lib/libc.a -lgcc -o joinpasswd
54  * strip -R .comment -R .note joinpasswd
55  *
56  * The idea of this program comes from Lukasz Dobrek <dobrek@pld-linux.org>.
57  * 
58  */
59
60 #include <sys/types.h>
61 #include <sys/mman.h>
62 #include <sys/stat.h>
63 #include <stdio.h>
64 #include <errno.h>
65 #include <unistd.h>
66 #include <stdarg.h>
67 #include <fcntl.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <signal.h>
71
72 #define FILE1 "/etc/passwd"
73 #define FILE2 "/etc/shadow"
74 #define FILE3 "/etc/group"
75 #define FILE4 "/etc/gshadow"
76
77 /* #define OLD_LOCK */
78
79 #define LOCK_FILE "/etc/.pwd.lock"
80
81 #define SUFFIX ".rpmnew"
82 /* maybe "-" or sth? */
83 #define BACKUP ".old"
84
85 /* #define SILENT */
86
87 void eputs(const char *fmt, ...)
88 {
89         va_list args;
90
91         va_start(args, fmt);
92         vfprintf(stderr, fmt, args);
93         va_end(args);
94 }
95
96 void fatal(const char *fmt, ...)
97 {
98         va_list args;
99
100         va_start(args, fmt);
101         eputs(fmt, args);
102         va_end(args);
103
104         eputs("\n");
105         exit(1);
106 }
107
108 char *map_file(const char *name, int *sz)
109 {
110         int fd;
111         void *ptr;
112         struct stat st;
113
114         fd = open(name, O_RDONLY);
115         if (fd == -1)
116                 return NULL;
117         if (fstat(fd, &st) < 0)
118                 return NULL;
119         *sz = st.st_size;
120         ptr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
121         if (ptr == MAP_FAILED)
122                 return NULL;
123
124         return ptr;
125 }
126
127 int exist(char *id, int id_len, char *ptr, int sz)
128 {
129         int i;
130
131         for (i = 0; i < sz; ) {
132                 if (sz - i > id_len && memcmp(id, ptr + i, id_len + 1) == 0)
133                         return 1;
134                 while (i < sz && ptr[i] != '\n')
135                         i++;
136                 i++;
137         }
138
139         return 0;
140 }
141
142 void itoa(char *buf, long i)
143 {
144         char tmp[32];
145         char *p;
146
147         if (i < 0) {
148                 strcpy(buf, "-");
149                 buf++;
150                 i = -i;
151         }
152         if (i == 0) {
153                 strcpy(buf, "0"); 
154                 return;
155         }
156         for (p = tmp; i; i /= 10)
157                 *p++ = (i % 10) + '0';
158         while (p > tmp)
159                 *buf++ = *--p;
160         *buf = 0;
161 }
162
163 #ifndef OLD_LOCK
164 int lock_fd = -1;
165 void noop(int x)
166 {
167         (void)x;
168 }
169 #endif
170
171 int try_lock(const char *name)
172 {
173 #ifdef OLD_LOCK
174         char file[strlen(name) + 32], lock[strlen(name) + 32];
175         char buf[32];
176         int fd;
177         long pid;
178
179         strcpy(lock, name);
180         strcpy(file, name);
181         strcat(lock, ".lock");
182         itoa(buf, (long)getpid());
183         strcat(file, ".");
184         strcat(file, buf);
185         
186         fd = open(file, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600);
187         if (fd < 0)
188                 return -1;
189         if (write(fd, buf, strlen(buf)) < 0) {
190                 close(fd);
191                 return -1;
192         }
193         if (close(fd) < 0)
194                 return -1;
195
196         if (link(file, lock) == 0) {
197                 unlink(file);
198                 return 0;
199         }
200
201         fd = open(lock, O_RDONLY);
202         if (fd < 0)
203                 goto oops;
204         memset(buf, 0, sizeof(buf));
205         read(fd, buf, sizeof(buf));
206         pid = atol(buf);
207         if (pid == 0 || kill(pid, 0) != 0) {
208                 /* stale lock */
209                 unlink(file);
210                 unlink(lock);
211                 /* try again */
212                 return try_lock(name);
213         }
214
215 oops:
216         unlink(file);
217         return -1;
218 #else
219         struct flock fl;
220         
221         if (lock_fd != -1)
222                 return -1;
223         lock_fd = open(LOCK_FILE, O_RDWR|O_CREAT, 0600);
224         if (lock_fd == -1)
225                 return -1;
226         signal(SIGALRM, noop);
227         alarm(15);
228         memset(&fl, 0, sizeof(fl));
229         fl.l_type = F_WRLCK;
230         fl.l_whence = SEEK_SET;
231         if (fcntl(lock_fd, F_SETLKW, &fl) != 0) {
232                 alarm(0);
233                 close(lock_fd);
234                 lock_fd = -1;
235                 return -1;
236         }
237         alarm(0);
238         
239         return 0;
240 #endif
241 }
242
243 void unlock(const char *name)
244 {
245 #ifdef OLD_LOCK
246         char lock[strlen(name) + 32];
247
248         strcpy(lock, name);
249         strcat(lock, ".lock");
250         unlink(lock);
251 #else
252         if (lock_fd != -1)
253                 close(lock_fd);
254         lock_fd = -1;
255 #endif
256 }
257
258 void lock(const char *name)
259 {
260         int n;
261
262         n = 5;
263         while (n--) {
264                 if (try_lock(name) == 0)
265                         return;
266                 eputs("waiting for lock...\n");
267                 sleep(1);
268         }
269         fatal("cannot get lock");
270 }
271
272 int join(const char *old_name, const char *new_name, const char *backup_name)
273 {
274         char *old, *new, *id;
275         int i, fd;
276         int old_sz, new_sz;
277
278         new = map_file(new_name, &new_sz);
279         if (new == NULL)
280                 return -1;
281         
282         lock(old_name);
283         old = map_file(old_name, &old_sz);
284         if (old == NULL)
285                 fatal("cannot mmap file `%s': %m", old_name);
286         
287         fd = open(backup_name, O_WRONLY|O_CREAT|O_TRUNC, 0600);
288         if (fd < 0)
289                 fatal("cannot create backup file `%s': %m", backup_name);
290         if (write(fd, old, old_sz) < 0)
291                 fatal("writting to backup file `%s' failed: %m", backup_name);
292         if (fsync(fd) < 0)
293                 fatal("syncing backup file `%s' failed: %m", backup_name);
294         if (close(fd) < 0)
295                 fatal("closing backup file `%s' failed: %m", backup_name);
296         
297 #ifndef SILENT
298         eputs("merging content of `");
299         eputs(old_name);
300         eputs("' with `");
301         eputs(new_name);
302         eputs("'\n");
303 #endif /* SILENT */
304
305         fd = open(old_name, O_WRONLY|O_APPEND);
306         if (fd < 0)
307                 fatal("cannot open old file `%s': %m", old_name);
308         
309         for (i = 0; i < new_sz; ) {
310                 id = new + i;
311                 while (i < new_sz && new[i] != ':' && new[i] != '\n')
312                         i++;
313                 if (i < new_sz && new[i] == ':') {
314                         int id_len, line_len;
315
316                         id_len = i - (id - new);
317                         while (i < new_sz && new[i] != '\n')
318                                 i++;
319                         if (i < new_sz)
320                                 i++;
321                         line_len = i - (id - new);
322                         
323                         if (!exist(id, id_len, old, old_sz)) {
324 #ifndef SILENT
325                                 eputs(old_name);
326                                 eputs(": adding `");
327                                 write(2, id, id_len);
328                                 eputs("'\n");
329 #endif /* SILENT */
330                                 if (write(fd, id, line_len) < 0)
331                                         fatal("writting line to `%s' failed, check backup file: %m", old_name);
332                         }
333                 } else if (i < new_sz)
334                         i++;
335         }
336         fsync(fd);
337         close(fd);
338
339 #if 0
340         /* user may want to exime this file... */
341         unlink(new_name);
342 #endif
343         unlock(old_name);
344
345         return 0;
346 }
347
348 int main()
349 {
350 #if 1
351         join(FILE1, FILE1 SUFFIX, FILE1 BACKUP);
352         join(FILE2, FILE2 SUFFIX, FILE2 BACKUP);
353         join(FILE3, FILE3 SUFFIX, FILE3 BACKUP);
354         join(FILE4, FILE4 SUFFIX, FILE4 BACKUP);
355 #else
356         join("test", "test.new", "test.old");
357 #endif
358         return 0;
359 }
This page took 0.827863 seconds and 3 git commands to generate.