4 * Copyright (c) 2001 Michal Moskal <malekith@pld-linux.org>.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
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*
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.
48 * Written for PLD Linux (http://www.pld-linux.org/) setup package.
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
56 * The idea of this program comes from Lukasz Dobrek <dobrek@pld-linux.org>.
60 #include <sys/types.h>
72 #define FILE1 "/etc/passwd"
73 #define FILE2 "/etc/shadow"
74 #define FILE3 "/etc/group"
75 #define FILE4 "/etc/gshadow"
77 /* #define OLD_LOCK */
79 #define LOCK_FILE "/etc/.pwd.lock"
81 #define SUFFIX ".rpmnew"
82 /* maybe "-" or sth? */
87 void eputs(const char *fmt, ...)
92 vfprintf(stderr, fmt, args);
96 void fatal(const char *fmt, ...)
108 char *map_file(const char *name, int *sz)
114 fd = open(name, O_RDONLY);
117 if (fstat(fd, &st) < 0)
120 ptr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
121 if (ptr == MAP_FAILED)
127 int exist(char *id, int id_len, char *ptr, int sz)
131 for (i = 0; i < sz; ) {
132 if (sz - i > id_len && memcmp(id, ptr + i, id_len + 1) == 0)
134 while (i < sz && ptr[i] != '\n')
142 void itoa(char *buf, long i)
156 for (p = tmp; i; i /= 10)
157 *p++ = (i % 10) + '0';
171 int try_lock(const char *name)
174 char file[strlen(name) + 32], lock[strlen(name) + 32];
181 strcat(lock, ".lock");
182 itoa(buf, (long)getpid());
186 fd = open(file, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600);
189 if (write(fd, buf, strlen(buf)) < 0) {
196 if (link(file, lock) == 0) {
201 fd = open(lock, O_RDONLY);
204 memset(buf, 0, sizeof(buf));
205 read(fd, buf, sizeof(buf));
207 if (pid == 0 || kill(pid, 0) != 0) {
212 return try_lock(name);
223 lock_fd = open(LOCK_FILE, O_RDWR|O_CREAT, 0600);
226 signal(SIGALRM, noop);
228 memset(&fl, 0, sizeof(fl));
230 fl.l_whence = SEEK_SET;
231 if (fcntl(lock_fd, F_SETLKW, &fl) != 0) {
243 void unlock(const char *name)
246 char lock[strlen(name) + 32];
249 strcat(lock, ".lock");
258 void lock(const char *name)
264 if (try_lock(name) == 0)
266 eputs("waiting for lock...\n");
269 fatal("cannot get lock");
272 int join(const char *old_name, const char *new_name, const char *backup_name)
274 char *old, *new, *id;
278 new = map_file(new_name, &new_sz);
283 old = map_file(old_name, &old_sz);
285 fatal("cannot mmap file `%s': %m", old_name);
287 fd = open(backup_name, O_WRONLY|O_CREAT|O_TRUNC, 0600);
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);
293 fatal("syncing backup file `%s' failed: %m", backup_name);
295 fatal("closing backup file `%s' failed: %m", backup_name);
298 eputs("merging content of `");
305 fd = open(old_name, O_WRONLY|O_APPEND);
307 fatal("cannot open old file `%s': %m", old_name);
309 for (i = 0; i < new_sz; ) {
311 while (i < new_sz && new[i] != ':' && new[i] != '\n')
313 if (i < new_sz && new[i] == ':') {
314 int id_len, line_len;
316 id_len = i - (id - new);
317 while (i < new_sz && new[i] != '\n')
321 line_len = i - (id - new);
323 if (!exist(id, id_len, old, old_sz)) {
327 write(2, id, id_len);
330 if (write(fd, id, line_len) < 0)
331 fatal("writting line to `%s' failed, check backup file: %m", old_name);
333 } else if (i < new_sz)
340 /* user may want to exime this file... */
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);
356 join("test", "test.new", "test.old");