Add point for gconv-modules db.
[glibc.git] / login / programs / database.c
blob7044cd8d87ac7d22aea1b370bb98192eeb36c105
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 (int filedes, time_t *timer);
48 /* Open the database specified by FILE and merge it with the contents
49 of the old format file specified by OLD_FILE. Returns a pointer to
50 a newly allocated structure describing the database, or NULL on
51 error. */
52 utmp_database *
53 open_database (const char *file, const char *old_file)
55 mode_t mode = S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH;
56 utmp_database *database;
58 /* Allocate memory. */
59 database = (utmp_database *) malloc (sizeof (utmp_database));
60 if (database == NULL)
62 error (0, 0, _("memory exhausted"));
63 return NULL;
66 memset (database, 0, sizeof (utmp_database));
68 /* Open database, create it if it doesn't exist already. */
69 database->fd = open (file, O_RDWR | O_CREAT, mode);
70 if (database->fd < 0)
72 error (0, errno, "%s", file);
73 goto return_error;
76 database->file = strdup (file);
77 if (database->file == NULL)
79 error (0, 0, _("memory exhausted"));
80 goto return_error;
83 if (old_file)
85 database->old_fd = open (old_file, O_RDWR|O_CREAT, mode);
86 if (database->old_fd < 0)
88 error (0, errno, "%s", old_file);
89 goto return_error;
92 database->old_file = strdup (old_file);
93 if (database->old_file == NULL)
95 error (0, 0, _("memory exhausted"));
96 goto return_error;
100 /* Initialize database. */
101 if (initialize_database (database) < 0)
102 goto return_error;
104 return database;
106 return_error:
107 close_database (database);
109 return NULL;
112 /* Synchronize DATABASE. */
114 synchronize_database (utmp_database *database)
116 assert (database);
118 /* Check if there is a file in the old format, that we have to
119 synchronize with. */
120 if (database->old_file)
122 time_t curtime;
123 time_t mtime;
125 curtime = time (NULL);
127 if (get_mtime (database->old_fd, &mtime) < 0)
129 error (0, errno, _("%s: cannot get modification time"),
130 database->old_file);
131 return -1;
134 if (mtime >= database->mtime)
136 int position = 0;
137 struct utmp entry;
138 struct utmp old_entry;
140 while (1)
142 if (read_old_entry (database, position, &old_entry) < 0)
143 break;
145 if (read_entry (database, position, &entry) < 0
146 || !compare_entry (&old_entry, &entry))
148 if (write_entry (database, position, &old_entry) < 0)
150 error (0, errno, "%s", database->file);
151 return -1;
155 position++;
158 database->mtime = curtime;
163 return 0;
167 /* Close DATABASE. */
168 void
169 close_database (utmp_database *database)
171 assert (database);
173 if (database->fd >= 0)
174 close (database->fd);
176 if (database->old_fd >= 0)
177 close (database->old_fd);
179 /* Free allocated memory. */
180 if (database->file)
181 free (database->file);
182 if (database->old_file)
183 free (database->old_file);
184 free (database);
188 /* Read the entry at POSITION in DATABASE and store the result in
189 ENTRY. Returns 0 if successful, -1 if not. */
191 read_entry (utmp_database *database, int position, struct utmp *entry)
193 ssize_t nbytes;
194 off_t offset;
196 offset = position * sizeof (struct utmp);
197 if (lseek (database->fd, offset, SEEK_SET) < 0)
198 return -1;
200 nbytes = read (database->fd, entry, sizeof (struct utmp));
201 if (nbytes != sizeof (struct utmp))
202 return -1;
204 return 0;
208 /* Write ENTRY at POSITION in DATABASE. Returns 0 if successful, -1
209 on error. */
211 write_entry (utmp_database *database, int position,
212 const struct utmp *entry)
214 int result = -1;
215 struct flock fl;
216 ssize_t nbytes;
217 off_t offset;
219 /* Try to lock the file. */
220 memset (&fl, 0, sizeof (struct flock));
221 fl.l_type = F_WRLCK;
222 fl.l_whence = SEEK_SET;
223 fcntl (database->fd, F_SETLKW, &fl);
225 offset = position * sizeof (struct utmp);
226 if (lseek (database->fd, offset, SEEK_SET) < 0)
227 goto fail;
229 nbytes = write (database->fd, entry, sizeof (struct utmp));
230 if (nbytes != sizeof (struct utmp))
232 ftruncate (database->fd, offset);
233 goto fail;
236 result = 0;
238 fail:
239 /* And unlock the file. */
240 fl.l_type = F_UNLCK;
241 fcntl (database->fd, F_SETLKW, &fl);
243 return result;
247 /* Append ENTRY to DATABASE. Returns the position of the appended
248 entry if successful, or -1 on error. */
250 append_entry (utmp_database *database, const struct utmp *entry)
252 int result = -1;
253 struct flock fl;
254 ssize_t nbytes;
255 off_t offset;
257 /* Try to lock the file. */
258 memset (&fl, 0, sizeof (struct flock));
259 fl.l_type = F_WRLCK;
260 fl.l_whence = SEEK_SET;
261 fcntl (database->fd, F_SETLKW, &fl);
263 offset = lseek (database->fd, 0, SEEK_END);
264 if (offset % sizeof (struct utmp) != 0)
266 offset -= offset % sizeof (struct utmp);
267 ftruncate (database->fd, offset);
269 if (lseek (database->fd, 0, SEEK_END) < 0)
270 goto fail;
273 nbytes = write (database->fd, entry, sizeof (struct utmp));
274 if (nbytes != sizeof (struct utmp))
276 ftruncate (database->fd, offset);
277 goto fail;
280 result = offset / sizeof (struct utmp);
282 fail:
283 /* And unlock the file. */
284 fl.l_type = F_UNLCK;
285 fcntl (database->fd, F_SETLKW, &fl);
287 return result;
292 read_old_entry (utmp_database *database, int position,
293 struct utmp *entry)
295 struct xtmp old_entry;
296 ssize_t nbytes;
297 off_t offset;
299 offset = position * sizeof (struct xtmp);
300 if (lseek (database->old_fd, offset, SEEK_SET) < 0)
301 return -1;
303 nbytes = read (database->old_fd, &old_entry, sizeof (struct xtmp));
304 if (nbytes != sizeof (struct xtmp))
305 return -1;
307 xtmp_to_utmp (&old_entry, entry);
308 return 0;
313 write_old_entry (utmp_database *database, int position,
314 const struct utmp *entry)
316 struct xtmp old_entry;
317 ssize_t nbytes;
318 off_t offset;
320 utmp_to_xtmp (entry, &old_entry);
322 offset = position * sizeof (struct xtmp);
323 if (lseek (database->old_fd, offset, SEEK_SET) < 0)
324 return -1;
326 nbytes = write (database->old_fd, &old_entry, sizeof (struct xtmp));
327 if (nbytes != sizeof (struct xtmp))
328 return -1;
330 return 0;
334 /* Initialize DATABASE. */
335 static int
336 initialize_database (utmp_database *database)
338 struct utmp entry;
339 int position = 0;
341 assert (database);
343 /* Check if there is a file in the old format to read. */
344 if (database->old_file)
346 while (1)
348 if (read_old_entry (database, position, &entry) < 0)
349 break;
351 #if _HAVE_UT_TYPE - 0
352 /* If the login type is one of RUN_LVL, BOOT_TIME, OLD_TIME or
353 NEW_TIME, search for an entry of the same type in the
354 database, and replace it if the entry in the file is newer. */
355 if (entry.ut_type == RUN_LVL || entry.ut_type == BOOT_TIME
356 || entry.ut_type == OLD_TIME || entry.ut_type == NEW_TIME)
358 if (store_state_entry (database, position, &entry) < 0)
360 error (0, errno, "%s", database->file);
361 return -1;
364 else
365 #endif
367 if (store_process_entry (database, position, &entry) < 0)
369 error (0, errno, "%s", database->file);
370 return -1;
374 /* Update position. */
375 position++;
378 while (1)
380 if (read_entry (database, position, &entry) < 0)
381 break;
383 if (write_old_entry (database, position, &entry) < 0)
385 error (0, errno, "%s", database->file);
386 return -1;
389 /* Update position. */
390 position++;
394 return synchronize_database (database);
398 #if _HAVE_UT_TYPE - 0
399 static int
400 store_state_entry (utmp_database *database, int old_position,
401 const struct utmp *old_entry)
403 struct utmp new_entry;
404 int new_position = 0;
405 int found = 0;
407 assert (old_entry->ut_type == RUN_LVL
408 || old_entry->ut_type == BOOT_TIME
409 || old_entry->ut_type == OLD_TIME
410 || old_entry->ut_type == NEW_TIME);
412 while (!found)
414 /* Read the next entry. */
415 if (read_entry (database, new_position, &new_entry) < 0)
416 break;
418 if (old_entry->ut_type == new_entry.ut_type)
420 found = 1;
421 continue;
424 /* Update position. */
425 new_position++;
428 if (found)
430 const struct utmp *entry;
432 if (
433 #if _HAVE_UT_TV - 0
434 old_entry->ut_tv.tv_sec > new_entry.ut_tv.tv_sec
435 #else
436 old_entry->ut_time > new_entry.ut_time
437 #endif
439 entry = old_entry;
440 else
441 entry = &new_entry;
443 return replace_entry (database, old_position, new_position, entry);
446 return store_entry (database, old_position, old_entry);
448 #endif
451 static int
452 store_process_entry (utmp_database *database, int old_position,
453 const struct utmp *old_entry)
455 struct utmp new_entry;
456 int new_position = 0;
457 int found = 0;
459 while (!found)
461 /* Read the next entry. */
462 if (read_entry (database, new_position, &new_entry) < 0)
463 break;
465 if (proc_utmp_eq (old_entry, &new_entry))
467 found = 1;
468 continue;
471 /* Update position. */
472 new_position++;
475 if (found)
477 const struct utmp *entry;
479 if (
480 #if _HAVE_UT_TV - 0
481 old_entry->ut_tv.tv_sec > new_entry.ut_tv.tv_sec
482 #else
483 old_entry->ut_time > new_entry.ut_time
484 #endif
486 entry = old_entry;
487 else
488 entry = &new_entry;
490 return replace_entry (database, old_position, new_position, entry);
493 return store_entry (database, old_position, old_entry);
497 static int
498 replace_entry (utmp_database *database, int old_position, int new_position,
499 const struct utmp *entry)
501 struct utmp tmp;
503 if (read_entry (database, old_position, &tmp) < 0
504 || write_entry (database, old_position, entry) < 0
505 || write_entry (database, new_position, &tmp) < 0)
506 return -1;
508 return 0;
512 static int
513 store_entry (utmp_database *database, int position,
514 const struct utmp *entry)
516 struct utmp tmp;
518 if (read_entry (database, position, &tmp) < 0)
519 return write_entry (database, position, entry);
521 if (write_entry (database, position, entry) < 0
522 || append_entry (database, &tmp) < 0)
523 return -1;
525 return 0;
529 /* Get modification time of the file with file descriptor FILEDES and
530 put it in TIMER. Returns 0 if successful, -1 if not. */
531 static int
532 get_mtime (int filedes, time_t *timer)
534 struct stat st;
536 if (fstat (filedes, &st) < 0)
537 return -1;
539 *timer = st.st_mtime;
541 return 0;