PageRenderTime 54ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/tools/joinpasswd.c

https://github.com/freddix/core
C | 359 lines | 251 code | 44 blank | 64 comment | 64 complexity | c15bc3ee171f85a1f67169c776f7ff8c MD5 | raw file
  1. /*
  2. * $Id: joinpasswd.c,v 1.14 2011/12/08 19:25:04 arekm Exp $
  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. #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. #define FILE1 "/etc/passwd"
  71. #define FILE2 "/etc/shadow"
  72. #define FILE3 "/etc/group"
  73. #define FILE4 "/etc/gshadow"
  74. /* #define OLD_LOCK */
  75. #define LOCK_FILE "/etc/.pwd.lock"
  76. #define SUFFIX ".rpmnew"
  77. /* maybe "-" or sth? */
  78. #define BACKUP ".old"
  79. /* #define SILENT */
  80. void eputs(const char *fmt, ...)
  81. {
  82. va_list args;
  83. va_start(args, fmt);
  84. vfprintf(stderr, fmt, args);
  85. va_end(args);
  86. }
  87. void fatal(const char *fmt, ...)
  88. {
  89. va_list args;
  90. va_start(args, fmt);
  91. eputs(fmt, args);
  92. va_end(args);
  93. eputs("\n");
  94. exit(1);
  95. }
  96. char *map_file(const char *name, int *sz)
  97. {
  98. int fd;
  99. void *ptr;
  100. struct stat st;
  101. fd = open(name, O_RDONLY);
  102. if (fd == -1)
  103. return NULL;
  104. if (fstat(fd, &st) < 0)
  105. return NULL;
  106. *sz = st.st_size;
  107. ptr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
  108. if (ptr == MAP_FAILED)
  109. return NULL;
  110. return ptr;
  111. }
  112. int exist(char *id, int id_len, char *ptr, int sz)
  113. {
  114. int i;
  115. for (i = 0; i < sz; ) {
  116. if (sz - i > id_len && memcmp(id, ptr + i, id_len + 1) == 0)
  117. return 1;
  118. while (i < sz && ptr[i] != '\n')
  119. i++;
  120. i++;
  121. }
  122. return 0;
  123. }
  124. void itoa(char *buf, long i)
  125. {
  126. char tmp[32];
  127. char *p;
  128. if (i < 0) {
  129. strcpy(buf, "-");
  130. buf++;
  131. i = -i;
  132. }
  133. if (i == 0) {
  134. strcpy(buf, "0");
  135. return;
  136. }
  137. for (p = tmp; i; i /= 10)
  138. *p++ = (i % 10) + '0';
  139. while (p > tmp)
  140. *buf++ = *--p;
  141. *buf = 0;
  142. }
  143. #ifndef OLD_LOCK
  144. int lock_fd = -1;
  145. void noop(int x)
  146. {
  147. (void)x;
  148. }
  149. #endif
  150. int try_lock(const char *name)
  151. {
  152. #ifdef OLD_LOCK
  153. char file[strlen(name) + 32], lock[strlen(name) + 32];
  154. char buf[32];
  155. int fd;
  156. long pid;
  157. strcpy(lock, name);
  158. strcpy(file, name);
  159. strcat(lock, ".lock");
  160. itoa(buf, (long)getpid());
  161. strcat(file, ".");
  162. strcat(file, buf);
  163. fd = open(file, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600);
  164. if (fd < 0)
  165. return -1;
  166. if (write(fd, buf, strlen(buf)) < 0) {
  167. close(fd);
  168. return -1;
  169. }
  170. if (close(fd) < 0)
  171. return -1;
  172. if (link(file, lock) == 0) {
  173. unlink(file);
  174. return 0;
  175. }
  176. fd = open(lock, O_RDONLY);
  177. if (fd < 0)
  178. goto oops;
  179. memset(buf, 0, sizeof(buf));
  180. read(fd, buf, sizeof(buf));
  181. pid = atol(buf);
  182. if (pid == 0 || kill(pid, 0) != 0) {
  183. /* stale lock */
  184. unlink(file);
  185. unlink(lock);
  186. /* try again */
  187. return try_lock(name);
  188. }
  189. oops:
  190. unlink(file);
  191. return -1;
  192. #else
  193. struct flock fl;
  194. if (lock_fd != -1)
  195. return -1;
  196. lock_fd = open(LOCK_FILE, O_RDWR|O_CREAT, 0600);
  197. if (lock_fd == -1)
  198. return -1;
  199. signal(SIGALRM, noop);
  200. alarm(15);
  201. memset(&fl, 0, sizeof(fl));
  202. fl.l_type = F_WRLCK;
  203. fl.l_whence = SEEK_SET;
  204. if (fcntl(lock_fd, F_SETLKW, &fl) != 0) {
  205. alarm(0);
  206. close(lock_fd);
  207. lock_fd = -1;
  208. return -1;
  209. }
  210. alarm(0);
  211. return 0;
  212. #endif
  213. }
  214. void unlock(const char *name)
  215. {
  216. #ifdef OLD_LOCK
  217. char lock[strlen(name) + 32];
  218. strcpy(lock, name);
  219. strcat(lock, ".lock");
  220. unlink(lock);
  221. #else
  222. if (lock_fd != -1)
  223. close(lock_fd);
  224. lock_fd = -1;
  225. #endif
  226. }
  227. void lock(const char *name)
  228. {
  229. int n;
  230. n = 5;
  231. while (n--) {
  232. if (try_lock(name) == 0)
  233. return;
  234. eputs("waiting for lock...\n");
  235. sleep(1);
  236. }
  237. fatal("cannot get lock");
  238. }
  239. int join(const char *old_name, const char *new_name, const char *backup_name)
  240. {
  241. char *old, *new, *id;
  242. int i, fd;
  243. int old_sz, new_sz;
  244. new = map_file(new_name, &new_sz);
  245. if (new == NULL)
  246. return -1;
  247. lock(old_name);
  248. old = map_file(old_name, &old_sz);
  249. if (old == NULL)
  250. fatal("cannot mmap file `%s': %m", old_name);
  251. fd = open(backup_name, O_WRONLY|O_CREAT|O_TRUNC, 0600);
  252. if (fd < 0)
  253. fatal("cannot create backup file `%s': %m", backup_name);
  254. if (write(fd, old, old_sz) < 0)
  255. fatal("writting to backup file `%s' failed: %m", backup_name);
  256. if (fsync(fd) < 0)
  257. fatal("syncing backup file `%s' failed: %m", backup_name);
  258. if (close(fd) < 0)
  259. fatal("closing backup file `%s' failed: %m", backup_name);
  260. #ifndef SILENT
  261. eputs("merging content of `");
  262. eputs(old_name);
  263. eputs("' with `");
  264. eputs(new_name);
  265. eputs("'\n");
  266. #endif /* SILENT */
  267. fd = open(old_name, O_WRONLY|O_APPEND);
  268. if (fd < 0)
  269. fatal("cannot open old file `%s': %m", old_name);
  270. for (i = 0; i < new_sz; ) {
  271. id = new + i;
  272. while (i < new_sz && new[i] != ':' && new[i] != '\n')
  273. i++;
  274. if (i < new_sz && new[i] == ':') {
  275. int id_len, line_len;
  276. id_len = i - (id - new);
  277. while (i < new_sz && new[i] != '\n')
  278. i++;
  279. if (i < new_sz)
  280. i++;
  281. line_len = i - (id - new);
  282. if (!exist(id, id_len, old, old_sz)) {
  283. #ifndef SILENT
  284. eputs(old_name);
  285. eputs(": adding `");
  286. write(2, id, id_len);
  287. eputs("'\n");
  288. #endif /* SILENT */
  289. if (write(fd, id, line_len) < 0)
  290. fatal("writting line to `%s' failed, check backup file: %m", old_name);
  291. }
  292. } else if (i < new_sz)
  293. i++;
  294. }
  295. fsync(fd);
  296. close(fd);
  297. #if 0
  298. /* user may want to exime this file... */
  299. unlink(new_name);
  300. #endif
  301. unlock(old_name);
  302. return 0;
  303. }
  304. int main()
  305. {
  306. #if 1
  307. join(FILE1, FILE1 SUFFIX, FILE1 BACKUP);
  308. join(FILE2, FILE2 SUFFIX, FILE2 BACKUP);
  309. join(FILE3, FILE3 SUFFIX, FILE3 BACKUP);
  310. join(FILE4, FILE4 SUFFIX, FILE4 BACKUP);
  311. #else
  312. join("test", "test.new", "test.old");
  313. #endif
  314. return 0;
  315. }