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