74e91fb94802d8b296ff0b11b126b9e4bdacae0d
[kopensolaris-gnu/glibc.git] / sysdeps / generic / utmp_file.c
1 /* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@cygnus.com>
4    and Paul Janzen <pcj@primenet.com>, 1996.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <signal.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <utmp.h>
29
30 #include "utmp-private.h"
31
32
33 /* Descriptor for the file and position.  */
34 static int file_fd = -1;
35 static off_t file_offset;
36
37 /* Cache for the last read entry.  */
38 static struct utmp last_entry;
39
40
41 /* Locking timeout.  */
42 #ifndef TIMEOUT
43 # define TIMEOUT 1
44 #endif
45
46 /* Do-nothing handler for locking timeout.  */
47 static void timeout_handler (int signum) {};
48
49 #define LOCK_FILE(fd, type) \
50 {                                                                       \
51   struct flock fl;                                                      \
52   struct sigaction action, old_action;                                  \
53   unsigned int old_timeout;                                             \
54                                                                         \
55   /* Cancel any existing alarm.  */                                     \
56   old_timeout = alarm (0);                                              \
57                                                                         \
58   /* Establish signal handler.  */                                      \
59   action.sa_handler = timeout_handler;                                  \
60   sigemptyset (&action.sa_mask);                                        \
61   action.sa_flags = 0;                                                  \
62   __sigaction (SIGALRM, &action, &old_action);                          \
63                                                                         \
64   alarm (TIMEOUT);                                                      \
65                                                                         \
66   /* Try to get the lock.  */                                           \
67   memset (&fl, '\0', sizeof (struct flock));                            \
68   fl.l_type = (type);                                                   \
69   fl.l_whence = SEEK_SET;                                               \
70   __fcntl ((fd), F_SETLKW, &fl)
71
72 #define UNLOCK_FILE(fd) \
73   /* Unlock the file.  */                                               \
74   fl.l_type = F_UNLCK;                                                  \
75   __fcntl ((fd), F_SETLKW, &fl);                                        \
76                                                                         \
77   /* Reset the signal handler and alarm.  */                            \
78   __sigaction (SIGALRM, &old_action, NULL);                             \
79   alarm (old_timeout);                                                  \
80 } while (0)
81
82
83 /* Functions defined here.  */
84 static int setutent_file (void);
85 static int getutent_r_file (struct utmp *buffer, struct utmp **result);
86 static int getutid_r_file (const struct utmp *key, struct utmp *buffer,
87                            struct utmp **result);
88 static int getutline_r_file (const struct utmp *key, struct utmp *buffer,
89                              struct utmp **result);
90 static struct utmp *pututline_file (const struct utmp *data);
91 static void endutent_file (void);
92 static int updwtmp_file (const char *file, const struct utmp *utmp);
93
94 /* Jump table for file functions.  */
95 struct utfuncs __libc_utmp_file_functions =
96 {
97   setutent_file,
98   getutent_r_file,
99   getutid_r_file,
100   getutline_r_file,
101   pututline_file,
102   endutent_file,
103   updwtmp_file
104 };
105
106
107 #ifndef TRANSFORM_UTMP_FILE_NAME
108 # define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
109 #endif
110
111 static int
112 setutent_file (void)
113 {
114   if (file_fd < 0)
115     {
116       const char *file_name;
117       int result;
118
119       file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
120
121       file_fd = __open (file_name, O_RDWR);
122       if (file_fd == -1)
123         {
124           /* Hhm, read-write access did not work.  Try read-only.  */
125           file_fd = __open (file_name, O_RDONLY);
126           if (file_fd == -1)
127             return 0;
128         }
129
130       /* We have to make sure the file is `closed on exec'.  */
131       result = __fcntl (file_fd, F_GETFD, 0);
132       if (result >= 0)
133         result = __fcntl (file_fd, F_SETFD, result | FD_CLOEXEC);
134       if (result == -1)
135         {
136           close (file_fd);
137           return 0;
138         }
139     }
140
141   __lseek (file_fd, 0, SEEK_SET);
142   file_offset = 0;
143
144 #if _HAVE_UT_TYPE - 0
145   /* Make sure the entry won't match.  */
146   last_entry.ut_type = -1;
147 #endif
148
149   return 1;
150 }
151
152
153 static int
154 getutent_r_file (struct utmp *buffer, struct utmp **result)
155 {
156   ssize_t nbytes;
157
158   assert (file_fd >= 0);
159
160   if (file_offset == -1l)
161     {
162       /* Not available.  */
163       *result = NULL;
164       return -1;
165     }
166
167   LOCK_FILE (file_fd, F_RDLCK);
168
169   /* Read the next entry.  */
170   nbytes = __read (file_fd, &last_entry, sizeof (struct utmp));
171
172   UNLOCK_FILE (file_fd);
173
174   if (nbytes != sizeof (struct utmp))
175     {
176       file_offset = -1l;
177       *result = NULL;
178       return -1;
179     }
180
181   /* Update position pointer.  */
182   file_offset += sizeof (struct utmp);
183
184   memcpy (buffer, &last_entry, sizeof (struct utmp));
185   *result = buffer;
186
187   return 0;
188 }
189
190
191 static int
192 proc_utmp_eq (const struct utmp *entry, const struct utmp *match)
193 {
194   return
195     (
196 #if _HAVE_UT_TYPE - 0
197      (entry->ut_type == INIT_PROCESS
198       || entry->ut_type == LOGIN_PROCESS
199       || entry->ut_type == USER_PROCESS
200       || entry->ut_type == DEAD_PROCESS)
201      &&
202      (match->ut_type == INIT_PROCESS
203       || match->ut_type == LOGIN_PROCESS
204       || match->ut_type == USER_PROCESS
205       || match->ut_type == DEAD_PROCESS)
206      &&
207 #endif
208 #if _HAVE_UT_ID - 0
209      (entry->ut_id[0] && match->ut_id[0]
210       ? strncmp (entry->ut_id, match->ut_id, sizeof match->ut_id) == 0
211       : strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line) == 0)
212 #else
213      strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line) == 0
214 #endif
215      );
216 }
217
218 static int
219 internal_getut_r (const struct utmp *id, struct utmp *buffer)
220 {
221   int result = -1;
222
223   LOCK_FILE (file_fd, F_RDLCK);
224
225 #if _HAVE_UT_TYPE - 0
226   if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
227       || id->ut_type == OLD_TIME || id->ut_type == NEW_TIME)
228     {
229       /* Search for next entry with type RUN_LVL, BOOT_TIME,
230          OLD_TIME, or NEW_TIME.  */
231
232       while (1)
233         {
234           /* Read the next entry.  */
235           if (__read (file_fd, buffer, sizeof (struct utmp))
236               != sizeof (struct utmp))
237             {
238               __set_errno (ESRCH);
239               file_offset = -1l;
240               goto unlock_return;
241             }
242           file_offset += sizeof (struct utmp);
243
244           if (id->ut_type == buffer->ut_type)
245             break;
246         }
247     }
248   else
249 #endif /* _HAVE_UT_TYPE */
250     {
251       /* Search for the next entry with the specified ID and with type
252          INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, or DEAD_PROCESS.  */
253
254       while (1)
255         {
256           /* Read the next entry.  */
257           if (__read (file_fd, buffer, sizeof (struct utmp))
258               != sizeof (struct utmp))
259             {
260               __set_errno (ESRCH);
261               file_offset = -1l;
262               goto unlock_return;
263             }
264           file_offset += sizeof (struct utmp);
265
266           if (proc_utmp_eq (buffer, id))
267             break;
268         }
269     }
270
271   result = 0;
272
273 unlock_return:
274   UNLOCK_FILE (file_fd);
275
276   return result;
277 }
278
279
280 /* For implementing this function we don't use the getutent_r function
281    because we can avoid the reposition on every new entry this way.  */
282 static int
283 getutid_r_file (const struct utmp *id, struct utmp *buffer,
284                 struct utmp **result)
285 {
286   assert (file_fd >= 0);
287
288   if (file_offset == -1l)
289     {
290       *result = NULL;
291       return -1;
292     }
293
294   if (internal_getut_r (id, &last_entry) < 0)
295     {
296       *result = NULL;
297       return -1;
298     }
299
300   memcpy (buffer, &last_entry, sizeof (struct utmp));
301   *result = buffer;
302
303   return 0;
304 }
305
306
307 /* For implementing this function we don't use the getutent_r function
308    because we can avoid the reposition on every new entry this way.  */
309 static int
310 getutline_r_file (const struct utmp *line, struct utmp *buffer,
311                   struct utmp **result)
312 {
313   assert (file_fd >= 0);
314
315   if (file_offset == -1l)
316     {
317       *result = NULL;
318       return -1;
319     }
320
321   LOCK_FILE (file_fd, F_RDLCK);
322
323   while (1)
324     {
325       /* Read the next entry.  */
326       if (__read (file_fd, &last_entry, sizeof (struct utmp))
327           != sizeof (struct utmp))
328         {
329           __set_errno (ESRCH);
330           file_offset = -1l;
331           *result = NULL;
332           goto unlock_return;
333         }
334       file_offset += sizeof (struct utmp);
335
336       /* Stop if we found a user or login entry.  */
337       if (
338 #if _HAVE_UT_TYPE - 0
339           (last_entry.ut_type == USER_PROCESS
340            || last_entry.ut_type == LOGIN_PROCESS)
341           &&
342 #endif
343           !strncmp (line->ut_line, last_entry.ut_line, sizeof line->ut_line))
344         break;
345     }
346
347   memcpy (buffer, &last_entry, sizeof (struct utmp));
348   *result = buffer;
349
350 unlock_return:
351   UNLOCK_FILE (file_fd);
352
353   return ((*result == NULL) ? -1 : 0);
354 }
355
356
357 static struct utmp *
358 pututline_file (const struct utmp *data)
359 {
360   struct utmp buffer;
361   struct utmp *pbuf;
362   int found;
363
364   assert (file_fd >= 0);
365
366   /* Find the correct place to insert the data.  */
367   if (file_offset > 0
368       && (
369 #if _HAVE_UT_TYPE - 0
370           (last_entry.ut_type == data->ut_type
371            && (last_entry.ut_type == RUN_LVL
372                || last_entry.ut_type == BOOT_TIME
373                || last_entry.ut_type == OLD_TIME
374                || last_entry.ut_type == NEW_TIME))
375           ||
376 #endif
377           proc_utmp_eq (&last_entry, data)))
378     found = 1;
379   else
380     found = internal_getut_r (data, &buffer);
381
382   LOCK_FILE (file_fd, F_WRLCK);
383
384   if (found < 0)
385     {
386       /* We append the next entry.  */
387       file_offset = __lseek (file_fd, 0, SEEK_END);
388       if (file_offset % sizeof (struct utmp) != 0)
389         {
390           file_offset -= file_offset % sizeof (struct utmp);
391           __ftruncate (file_fd, file_offset);
392
393           if (__lseek (file_fd, 0, SEEK_END) < 0)
394             {
395               pbuf = NULL;
396               goto unlock_return;
397             }
398         }
399     }
400   else
401     {
402       /* We replace the just read entry.  */
403       file_offset -= sizeof (struct utmp);
404       __lseek (file_fd, file_offset, SEEK_SET);
405     }
406
407   /* Write the new data.  */
408   if (__write (file_fd, data, sizeof (struct utmp)) != sizeof (struct utmp))
409     {
410       /* If we appended a new record this is only partially written.
411          Remove it.  */
412       if (found < 0)
413         (void) __ftruncate (file_fd, file_offset);
414       pbuf = NULL;
415     }
416   else
417     {
418       file_offset += sizeof (struct utmp);
419       pbuf = (struct utmp *) data;
420     }
421
422  unlock_return:
423   UNLOCK_FILE (file_fd);
424
425   return pbuf;
426 }
427
428
429 static void
430 endutent_file (void)
431 {
432   assert (file_fd >= 0);
433
434   __close (file_fd);
435   file_fd = -1;
436 }
437
438
439 static int
440 updwtmp_file (const char *file, const struct utmp *utmp)
441 {
442   int result = -1;
443   off_t offset;
444   int fd;
445
446   /* Open WTMP file.  */
447   fd = __open (file, O_WRONLY);
448   if (fd < 0)
449     return -1;
450
451   LOCK_FILE (fd, F_WRLCK);
452
453   /* Remember original size of log file.  */
454   offset = __lseek (fd, 0, SEEK_END);
455   if (offset % sizeof (struct utmp) != 0)
456     {
457       offset -= offset % sizeof (struct utmp);
458       __ftruncate (fd, offset);
459
460       if (__lseek (fd, 0, SEEK_END) < 0)
461         goto unlock_return;
462     }
463
464   /* Write the entry.  If we can't write all the bytes, reset the file
465      size back to the original size.  That way, no partial entries
466      will remain.  */
467   if (__write (fd, utmp, sizeof (struct utmp)) != sizeof (struct utmp))
468     {
469       __ftruncate (fd, offset);
470       goto unlock_return;
471     }
472
473   result = 0;
474
475 unlock_return:
476   UNLOCK_FILE (fd);
477
478   /* Close WTMP file.  */
479   __close (fd);
480
481   return result;
482 }