Not needed anymore.
[kopensolaris-gnu/glibc.git] / login / utmp_file.c
1 /* Copyright (C) 1996, 1997 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 <errno.h>
22 #include <fcntl.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <utmp.h>
30
31 #include "utmp-private.h"
32
33
34 /* Descriptor for the file and position.  */
35 static int file_fd = INT_MIN;
36 static off_t file_offset;
37
38 static struct utmp last_entry;
39
40
41 /* Functions defined here.  */
42 static int setutent_file (int reset);
43 static int getutent_r_file (struct utmp *buffer, struct utmp **result);
44 static int getutid_r_file (const struct utmp *key, struct utmp *buffer,
45                            struct utmp **result);
46 static int getutline_r_file (const struct utmp *key, struct utmp *buffer,
47                              struct utmp **result);
48 static struct utmp *pututline_file (const struct utmp *data);
49 static void endutent_file (void);
50 static int updwtmp_file (const char *file, const struct utmp *utmp);
51
52 /* Jump table for file functions.  */
53 struct utfuncs __libc_utmp_file_functions =
54 {
55   setutent_file,
56   getutent_r_file,
57   getutid_r_file,
58   getutline_r_file,
59   pututline_file,
60   endutent_file,
61   updwtmp_file
62 };
63
64
65 static int
66 setutent_file (int reset)
67 {
68   if (file_fd == INT_MIN)
69     {
70       file_fd = open (__libc_utmp_file_name, O_RDWR);
71       if (file_fd == -1)
72         {
73           /* Hhm, read-write access did not work.  Try read-only.  */
74           file_fd = open (__libc_utmp_file_name, O_RDONLY);
75           if (file_fd == -1)
76             {
77               perror (_("while opening UTMP file"));
78               return 0;
79             }
80         }
81       file_offset = 0;
82
83 #if _HAVE_UT_TYPE - 0
84       /* Make sure the entry won't match.  */
85       last_entry.ut_type = -1;
86 #endif
87     }
88   else if (reset)
89     {
90       lseek (file_fd, 0, SEEK_SET);
91
92       /* Remember we are at beginning of file.  */
93       file_offset = 0;
94
95 #if _HAVE_UT_TYPE - 0
96       /* Make sure the entry won't match.  */
97       last_entry.ut_type = -1;
98 #endif
99     }
100
101   return 1;
102 }
103
104
105 static void
106 endutent_file (void)
107 {
108   if (file_fd >= 0)
109     close (file_fd);
110
111   file_fd = INT_MIN;
112 }
113
114
115 static int
116 getutent_r_file (struct utmp *buffer, struct utmp **result)
117 {
118   ssize_t nbytes;
119   struct flock fl;                      /* Information struct for locking.  */
120
121   /* Open utmp file if not already done.  */
122   if (file_fd == INT_MIN)
123     setutent_file (1);
124
125   if (file_fd == -1 || file_offset == -1l)
126     {
127       /* Not available.  */
128       *result = NULL;
129       return -1;
130     }
131
132   /* XXX The following is not perfect.  Instead of locking the file itself
133      Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl> suggests to
134      use an extra locking file.  */
135
136   /* Try to get the lock.  */
137   memset (&fl, '\0', sizeof (struct flock));
138   fl.l_type = F_RDLCK;
139   fl.l_whence = SEEK_SET;
140   fcntl (file_fd, F_SETLKW, &fl);
141
142   /* Read the next entry.  */
143   nbytes = read (file_fd, &last_entry, sizeof (struct utmp));
144
145   /* And unlock the file.  */
146   fl.l_type = F_UNLCK;
147   fcntl (file_fd, F_SETLKW, &fl);
148
149   if (nbytes != sizeof (struct utmp))
150     {
151       file_offset = -1l;
152       *result = NULL;
153       return -1;
154     }
155
156   /* Update position pointer.  */
157   file_offset += sizeof (struct utmp);
158
159   memcpy (buffer, &last_entry, sizeof (struct utmp));
160   *result = buffer;
161
162   return 0;
163 }
164
165
166 /* For implementing this function we don't use the getutent_r function
167    because we can avoid the reposition on every new entry this way.  */
168 static int
169 getutline_r_file (const struct utmp *line, struct utmp *buffer,
170                   struct utmp **result)
171 {
172   struct flock fl;
173   
174   if (file_fd < 0 || file_offset == -1l)
175     {
176       *result = NULL;
177       return -1;
178     }
179
180   /* Try to get the lock.  */
181   memset (&fl, '\0', sizeof (struct flock));
182   fl.l_type = F_RDLCK;
183   fl.l_whence = SEEK_SET;
184   fcntl (file_fd, F_SETLKW, &fl);
185
186   while (1)
187     {
188       /* Read the next entry.  */
189       if (read (file_fd, &last_entry, sizeof (struct utmp))
190           != sizeof (struct utmp))
191         {
192           __set_errno (ESRCH);
193           file_offset = -1l;
194           *result = NULL;
195           goto unlock_return;
196         }
197       file_offset += sizeof (struct utmp);
198
199       /* Stop if we found a user or login entry.  */
200       if (
201 #if _HAVE_UT_TYPE - 0
202           (last_entry.ut_type == USER_PROCESS
203            || last_entry.ut_type == LOGIN_PROCESS)
204           &&
205 #endif
206           !strncmp (line->ut_line, last_entry.ut_line, sizeof line->ut_line))
207         break;
208     }
209
210   memcpy (buffer, &last_entry, sizeof (struct utmp));
211   *result = buffer;
212
213 unlock_return:
214   /* And unlock the file.  */
215   fl.l_type = F_UNLCK;
216   fcntl (file_fd, F_SETLKW, &fl);
217
218   return ((result == NULL) ? -1 : 0);
219 }
220
221
222 static int
223 proc_utmp_eq (const struct utmp *entry, const struct utmp *match)
224 {
225   return
226     (
227 #if _HAVE_UT_TYPE - 0
228      (entry->ut_type == INIT_PROCESS
229       || entry->ut_type == LOGIN_PROCESS
230       || entry->ut_type == USER_PROCESS
231       || entry->ut_type == DEAD_PROCESS)
232      &&
233      (match->ut_type == INIT_PROCESS
234       || match->ut_type == LOGIN_PROCESS
235       || match->ut_type == USER_PROCESS
236       || match->ut_type == DEAD_PROCESS)
237      &&
238 #endif
239 #if _HAVE_UT_ID - 0
240      (entry->ut_id[0] && match->ut_id[0]
241       ? strncmp (entry->ut_id, match->ut_id, sizeof match->ut_id) == 0
242       : strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line) == 0)
243 #else
244      strncmp (entry->ut_line, match->ut_line, sizeof match->ut_line) == 0
245 #endif
246      );
247 }
248
249 static int
250 internal_getut_r (const struct utmp *id, struct utmp *buffer)
251 {
252   int result = -1;
253   struct flock fl;
254   
255   /* Try to get the lock.  */
256   memset (&fl, '\0', sizeof (struct flock));
257   fl.l_type = F_RDLCK;
258   fl.l_whence = SEEK_SET;
259   fcntl (file_fd, F_SETLKW, &fl);
260
261 #if _HAVE_UT_TYPE - 0
262   if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
263       || id->ut_type == OLD_TIME || id->ut_type == NEW_TIME)
264     {
265       /* Search for next entry with type RUN_LVL, BOOT_TIME,
266          OLD_TIME, or NEW_TIME.  */
267
268       while (1)
269         {
270           /* Read the next entry.  */
271           if (read (file_fd, buffer, sizeof (struct utmp))
272               != sizeof (struct utmp))
273             {
274               __set_errno (ESRCH);
275               file_offset = -1l;
276               goto unlock_return;
277             }
278           file_offset += sizeof (struct utmp);
279
280           if (id->ut_type == buffer->ut_type)
281             break;
282         }
283     }
284   else
285 #endif /* _HAVE_UT_TYPE */
286     {
287       /* Search for the next entry with the specified ID and with type
288          INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, or DEAD_PROCESS.  */
289
290       while (1)
291         {
292           /* Read the next entry.  */
293           if (read (file_fd, buffer, sizeof (struct utmp))
294               != sizeof (struct utmp))
295             {
296               __set_errno (ESRCH);
297               file_offset = -1l;
298               goto unlock_return;
299             }
300           file_offset += sizeof (struct utmp);
301
302           if (proc_utmp_eq (buffer, id))
303             break;
304         }
305     }
306
307   result = 0;
308
309 unlock_return:
310   /* And unlock the file.  */
311   fl.l_type = F_UNLCK;
312   fcntl (file_fd, F_SETLKW, &fl);
313
314   return result;
315 }
316
317
318 /* For implementing this function we don't use the getutent_r function
319    because we can avoid the reposition on every new entry this way.  */
320 static int
321 getutid_r_file (const struct utmp *id, struct utmp *buffer,
322                 struct utmp **result)
323 {
324   if (file_fd < 0 || file_offset == -1l)
325     {
326       *result = NULL;
327       return -1;
328     }
329
330   if (internal_getut_r (id, &last_entry) < 0)
331     {
332       *result = NULL;
333       return -1;
334     }
335
336   memcpy (buffer, &last_entry, sizeof (struct utmp));
337   *result = buffer;
338
339   return 0;
340 }
341
342
343 static struct utmp *
344 pututline_file (const struct utmp *data)
345 {
346   struct flock fl;                      /* Information struct for locking.  */
347   struct utmp buffer;
348   struct utmp *pbuf;
349   int found;
350
351   if (file_fd < 0)
352     /* Something went wrong.  */
353     return NULL;
354
355   if (file_fd == INT_MIN)
356     /* The file is closed.  Open it again.  */
357     setutent_file (0);
358
359   /* Find the correct place to insert the data.  */
360   if (file_offset > 0
361       && (
362 #if _HAVE_UT_TYPE - 0
363           (last_entry.ut_type == data->ut_type
364            && (last_entry.ut_type == RUN_LVL
365                || last_entry.ut_type == BOOT_TIME
366                || last_entry.ut_type == OLD_TIME
367                || last_entry.ut_type == NEW_TIME))
368           ||
369 #endif
370           proc_utmp_eq (&last_entry, data)))
371     found = 1;
372   else
373     found = internal_getut_r (data, &buffer);
374
375   /* Try to lock the file.  */
376   memset (&fl, '\0', sizeof (struct flock));
377   fl.l_type = F_WRLCK;
378   fl.l_whence = SEEK_SET;
379   fcntl (file_fd, F_SETLKW, &fl);
380
381   if (found < 0)
382     {
383       /* We append the next entry.  */
384       file_offset = lseek (file_fd, 0, SEEK_END);
385       if (file_offset % sizeof (struct utmp) != 0)
386         {
387           file_offset -= file_offset % sizeof (struct utmp);
388           ftruncate (file_fd, file_offset);
389
390           if (lseek (file_fd, 0, SEEK_END) < 0)
391             {
392               pbuf = NULL;
393               goto unlock_return;
394             }
395         }
396     }
397   else
398     {
399       /* We replace the just read entry.  */
400       file_offset -= sizeof (struct utmp);
401       lseek (file_fd, file_offset, SEEK_SET);
402     }
403
404   /* Write the new data.  */
405   if (write (file_fd, data, sizeof (struct utmp)) != sizeof (struct utmp)
406       /* If we appended a new record this is only partially written.
407          Remove it.  */
408       && found < 0)
409     {
410       (void) ftruncate (file_fd, file_offset);
411       pbuf = NULL;
412     }
413   else
414     {
415       file_offset += sizeof (struct utmp);
416       pbuf = (struct utmp *) data;
417     }
418
419  unlock_return:
420    /* And unlock the file.  */
421   fl.l_type = F_UNLCK;
422   fcntl (file_fd, F_SETLKW, &fl);
423
424   return pbuf;
425 }
426
427
428 static int
429 updwtmp_file (const char *file, const struct utmp *utmp)
430 {
431   int result = -1;
432   struct flock fl;
433   off_t offset;
434   int fd;
435   
436   /* Open WTMP file.  */
437   fd = open (file, O_WRONLY);
438   if (fd < 0)
439     return -1;
440
441   /* Try to get the lock.  */
442   memset (&fl, '\0', sizeof (struct flock));
443   fl.l_type = F_WRLCK;
444   fl.l_whence = SEEK_SET;
445   fcntl (fd, F_SETLKW, &fl);
446   
447   /* Remember original size of log file.  */
448   offset = lseek (fd, 0, SEEK_END);
449   if (offset % sizeof (struct utmp) != 0)
450     {
451       offset -= offset % sizeof (struct utmp);
452       ftruncate (fd, offset);
453
454       if (lseek (fd, 0, SEEK_END) < 0)
455         goto unlock_return;
456     }
457
458   /* Write the entry.  If we can't write all the bytes, reset the file
459      size back to the original size.  That way, no partial entries
460      will remain.  */
461   if (write (fd, utmp, sizeof (struct utmp)) != sizeof (struct utmp))
462     {
463       ftruncate (fd, offset);
464       goto unlock_return;
465     }
466
467   result = 0;
468   
469 unlock_return:
470   /* And unlock the file.  */
471   fl.l_type = F_UNLCK;
472   fcntl (fd, F_SETLKW, &fl);
473
474   /* Close WTMP file.  */
475   close (fd);
476
477   return result;
478 }