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