Update.
[glibc.git] / login / programs / database.c
blob3138ae605c21a98ed91b086e15317cd723d0ed0d
1 /* Copyright (C) 1997 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
20 #include <assert.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <sys/stat.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include <utmp.h>
31 #include "utmpd-private.h"
32 #include "xtmp.h"
35 /* Prototypes for the local functions. */
36 static int initialize_database (utmp_database *database);
37 static int store_state_entry (utmp_database *database, int old_position,
38 const struct utmp *old_entry);
39 static int store_process_entry (utmp_database *database, int old_position,
40 const struct utmp *old_entry);
41 static int replace_entry (utmp_database *database, int old_position,
42 int new_position, const struct utmp *entry);
43 static int store_entry (utmp_database *database, int position,
44 const struct utmp *entry);
45 static int get_mtime (const char *file, time_t *timer);
48 /* Open the database specified by FILE and merge it with the
49 contents of the old format file specified by OLD_FILE. Returns a
50 pointer to a newly allocated structure describing the database, or
51 NULL on error. */
52 utmp_database *
53 open_database (const char *file, const char *old_file)
55 utmp_database *database;
57 /* Allocate memory. */
58 database = (utmp_database *) malloc (sizeof (utmp_database));
59 if (database == NULL)
60 return NULL;
62 memset (database, 0, sizeof (utmp_database));
64 /* Open database. */
65 database->fd = open (file, O_RDWR);
66 if (database->fd < 0)
67 goto fail;
69 database->old_fd = open (old_file, O_RDWR);
70 if (database->old_fd < 0)
71 goto fail;
73 if ((file && !(database->file = strdup (file)))
74 || (old_file && !(database->old_file = strdup (old_file))))
75 goto fail;
77 if (initialize_database (database) < 0
78 || synchronize_database (database) < 0)
79 goto fail;
81 return database;
83 fail:
84 close_database (database);
85 return NULL;
88 /* Synchronize DATABASE. */
89 int
90 synchronize_database (utmp_database *database)
92 assert (database);
94 /* Check if there is a file in the old format, that we have to
95 synchronize with. */
96 if (database->old_file)
98 time_t curtime;
99 time_t mtime;
101 curtime = time (NULL);
103 if (get_mtime (database->old_file, &mtime) < 0)
104 return -1;
106 if (mtime >= database->mtime)
108 int position = 0;
109 struct utmp entry;
110 struct utmp old_entry;
112 while (1)
114 if (read_old_entry (database, position, &old_entry) < 0)
115 break;
117 if (read_entry (database, position, &entry) < 0
118 || !compare_entry (&old_entry, &entry))
120 if (write_entry (database, position, &old_entry) < 0)
121 return -1;
124 position++;
127 database->mtime = curtime;
132 return 0;
136 /* Close DATABASE. */
137 void
138 close_database (utmp_database *database)
140 assert (database);
142 if (database->fd >= 0)
143 close (database->fd);
145 if (database->old_fd >= 0)
146 close (database->old_fd);
148 /* Free allocated memory. */
149 if (database->file)
150 free (database->file);
151 if (database->old_file)
152 free (database->old_file);
153 free (database);
157 /* Read the entry at POSITION in DATABASE and store the result in
158 ENTRY. Returns 0 if successful, -1 if not. */
160 read_entry (utmp_database *database, int position, struct utmp *entry)
162 ssize_t nbytes;
163 off_t offset;
165 offset = position * sizeof (struct utmp);
166 if (lseek (database->fd, offset, SEEK_SET) < 0)
167 return -1;
169 nbytes = read (database->fd, entry, sizeof (struct utmp));
170 if (nbytes != sizeof (struct utmp))
171 return -1;
173 return 0;
177 /* Write ENTRY at POSITION in DATABASE. Returns 0 if successful, -1
178 on error. */
180 write_entry (utmp_database *database, int position,
181 const struct utmp *entry)
183 int result = -1;
184 struct flock fl;
185 ssize_t nbytes;
186 off_t offset;
188 /* Try to lock the file. */
189 memset (&fl, 0, sizeof (struct flock));
190 fl.l_type = F_WRLCK;
191 fl.l_whence = SEEK_SET;
192 fcntl (database->fd, F_SETLKW, &fl);
194 offset = position * sizeof (struct utmp);
195 if (lseek (database->fd, offset, SEEK_SET) < 0)
196 goto fail;
198 nbytes = write (database->fd, entry, sizeof (struct utmp));
199 if (nbytes != sizeof (struct utmp))
201 ftruncate (database->fd, offset);
202 goto fail;
205 result = 0;
207 fail:
208 /* And unlock the file. */
209 fl.l_type = F_UNLCK;
210 fcntl (database->fd, F_SETLKW, &fl);
212 return result;
216 /* Append ENTRY to DATABASE. Returns the position of the appended
217 entry if successful, or -1 on error. */
219 append_entry (utmp_database *database, const struct utmp *entry)
221 int result = -1;
222 struct flock fl;
223 ssize_t nbytes;
224 off_t offset;
226 /* Try to lock the file. */
227 memset (&fl, 0, sizeof (struct flock));
228 fl.l_type = F_WRLCK;
229 fl.l_whence = SEEK_SET;
230 fcntl (database->fd, F_SETLKW, &fl);
232 offset = lseek (database->fd, 0, SEEK_END);
233 if (offset % sizeof (struct utmp) != 0)
235 offset -= offset % sizeof (struct utmp);
236 ftruncate (database->fd, offset);
238 if (lseek (database->fd, 0, SEEK_END) < 0)
239 goto fail;
242 nbytes = write (database->fd, entry, sizeof (struct utmp));
243 if (nbytes != sizeof (struct utmp))
245 ftruncate (database->fd, offset);
246 goto fail;
249 result = offset / sizeof (struct utmp);
251 fail:
252 /* And unlock the file. */
253 fl.l_type = F_UNLCK;
254 fcntl (database->fd, F_SETLKW, &fl);
256 return result;
261 read_old_entry (utmp_database *database, int position,
262 struct utmp *entry)
264 struct xtmp old_entry;
265 ssize_t nbytes;
266 off_t offset;
268 offset = position * sizeof (struct xtmp);
269 if (lseek (database->old_fd, offset, SEEK_SET) < 0)
270 return -1;
272 nbytes = read (database->old_fd, &old_entry, sizeof (struct xtmp));
273 if (nbytes != sizeof (struct xtmp))
274 return -1;
276 xtmp_to_utmp (&old_entry, entry);
277 return 0;
282 write_old_entry (utmp_database *database, int position,
283 const struct utmp *entry)
285 struct xtmp old_entry;
286 ssize_t nbytes;
287 off_t offset;
289 utmp_to_xtmp (entry, &old_entry);
291 offset = position * sizeof (struct xtmp);
292 if (lseek (database->old_fd, offset, SEEK_SET) < 0)
293 return -1;
295 nbytes = write (database->old_fd, &old_entry, sizeof (struct xtmp));
296 if (nbytes != sizeof (struct xtmp))
297 return -1;
299 return 0;
303 /* Initialize DATABASE. */
304 static int
305 initialize_database (utmp_database *database)
307 struct utmp entry;
308 int position = 0;
310 assert (database);
312 /* Check if there is a file in the old format to read. */
313 if (database->old_file)
315 while (1)
317 if (read_old_entry (database, position, &entry) < 0)
318 break;
320 #if _HAVE_UT_TYPE - 0
321 /* If the login type is one of RUN_LVL, BOOT_TIME, OLD_TIME or
322 NEW_TIME, search for an entry of the same type in the
323 database, and replace it if the entry in the file is newer. */
324 if (entry.ut_type == RUN_LVL || entry.ut_type == BOOT_TIME
325 || entry.ut_type == OLD_TIME || entry.ut_type == NEW_TIME)
327 if (store_state_entry (database, position, &entry) < 0)
328 return -1;
330 else
331 #endif
333 if (store_process_entry (database, position, &entry) < 0)
334 return -1;
337 /* Update position. */
338 position++;
341 while (1)
343 if (read_entry (database, position, &entry) < 0)
344 break;
346 if (write_old_entry (database, position, &entry) < 0)
347 return -1;
349 /* Update position. */
350 position++;
354 return 0;
358 static int
359 store_state_entry (utmp_database *database, int old_position,
360 const struct utmp *old_entry)
362 struct utmp new_entry;
363 int new_position = 0;
364 int found = 0;
366 assert (old_entry->ut_type == RUN_LVL
367 || old_entry->ut_type == BOOT_TIME
368 || old_entry->ut_type == OLD_TIME
369 || old_entry->ut_type == NEW_TIME);
371 while (!found)
373 /* Read the next entry. */
374 if (read_entry (database, new_position, &new_entry) < 0)
375 break;
377 if (old_entry->ut_type == new_entry.ut_type)
379 found = 1;
380 continue;
383 /* Update position. */
384 new_position++;
387 if (found)
389 const struct utmp *entry;
391 if (old_entry->ut_time > new_entry.ut_time)
392 entry = old_entry;
393 else
394 entry = &new_entry;
396 return replace_entry (database, old_position, new_position, entry);
399 return store_entry (database, old_position, old_entry);
403 static int
404 store_process_entry (utmp_database *database, int old_position,
405 const struct utmp *old_entry)
407 struct utmp new_entry;
408 int new_position = 0;
409 int found = 0;
411 while (!found)
413 /* Read the next entry. */
414 if (read_entry (database, new_position, &new_entry) < 0)
415 break;
417 if (proc_utmp_eq (old_entry, &new_entry))
419 found = 1;
420 continue;
423 /* Update position. */
424 new_position++;
427 if (found)
429 const struct utmp *entry;
431 if (old_entry->ut_time > new_entry.ut_time)
432 entry = old_entry;
433 else
434 entry = &new_entry;
436 return replace_entry (database, old_position, new_position, entry);
439 return store_entry (database, old_position, old_entry);
443 static int
444 replace_entry (utmp_database *database, int old_position, int new_position,
445 const struct utmp *entry)
447 struct utmp tmp;
449 if (read_entry (database, old_position, &tmp) < 0
450 || write_entry (database, old_position, entry) < 0
451 || write_entry (database, new_position, &tmp) < 0)
452 return -1;
454 return 0;
458 static int
459 store_entry (utmp_database *database, int position,
460 const struct utmp *entry)
462 struct utmp tmp;
464 if (read_entry (database, position, &tmp) < 0)
465 return write_entry (database, position, entry);
467 if (write_entry (database, position, entry) < 0
468 || append_entry (database, &tmp) < 0)
469 return -1;
471 return 0;
475 /* Get modification time of FILE and put it in TIMER. returns 0 if
476 successful, -1 if not. */
477 static int
478 get_mtime (const char *file, time_t *timer)
480 struct stat st;
482 if (stat (file, &st) < 0)
483 return -1;
485 *timer = st.st_mtime;
487 return 0;