update from main archive 961209
[kopensolaris-gnu/glibc.git] / login / utmp_file.c
1 /* Copyright (C) 1996 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 <unistd.h>
28 #include <utmp.h>
29 #include <sys/file.h>
30 #include <sys/stat.h>
31
32 #include "utmp-private.h"
33
34
35 /* This is the default name.  */
36 static const char default_file_name[] = _PATH_UTMP;
37
38 /* Current file name.  */
39 static const char *file_name = (const char *) default_file_name;
40
41 /* Descriptor for the file and position.  */
42 static int file_fd = INT_MIN;
43 static off_t file_offset;
44
45 static struct utmp last_entry;
46
47 /* Functions defined here.  */
48 static int setutent_file (int reset);
49 static int getutent_r_file (struct utmp *buffer, struct utmp **result);
50 static int getutid_r_file (const struct utmp *key, struct utmp *buffer,
51                            struct utmp **result);
52 static int getutline_r_file (const struct utmp *key, struct utmp *buffer,
53                              struct utmp **result);
54 static struct utmp *pututline_file (const struct utmp *data);
55 static void endutent_file (void);
56 static int utmpname_file (const char *name);
57
58
59 /* Jump table for file functions.  */
60 struct utfuncs __libc_utmp_file_functions =
61 {
62   setutent_file,
63   getutent_r_file,
64   getutid_r_file,
65   getutline_r_file,
66   pututline_file,
67   endutent_file,
68   utmpname_file
69 };
70
71
72 static int
73 setutent_file (int reset)
74 {
75   if (file_fd == INT_MIN)
76     {
77       file_fd = open (file_name, O_RDWR);
78       if (file_fd == -1)
79         {
80           /* Hhm, read-write access did not work.  Try read-only.  */
81           file_fd = open (file_name, O_RDONLY);
82           if (file_fd == -1)
83             {
84               perror (_("while opening UTMP file"));
85               return 0;
86             }
87         }
88       file_offset = 0;
89
90       /* Make sure the entry won't match.  */
91       last_entry.ut_type = -1;
92     }
93   else if (reset)
94     {
95       /* Remember we are at beginning of file.  */
96       file_offset = 0;
97
98       /* Make sure the entry won't match.  */
99       last_entry.ut_type = -1;
100     }
101
102   return 1;
103 }
104
105
106 static void
107 endutent_file (void)
108 {
109   if (file_fd >= 0)
110     close (file_fd);
111
112   file_fd = INT_MIN;
113 }
114
115
116 static int
117 getutent_r_file (struct utmp *buffer, struct utmp **result)
118 {
119   int nbytes;
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   /* Read the next entry.  */
133   flock (file_fd, LOCK_SH);
134   nbytes = read (file_fd, &last_entry, sizeof (struct utmp));
135   flock (file_fd, LOCK_UN);
136
137   if (nbytes != sizeof (struct utmp))
138     {
139       file_offset = -1l;
140       *result = NULL;
141       return -1;
142     }
143
144   /* Update position pointer.  */
145   file_offset += sizeof (struct utmp);
146
147   memcpy (buffer, &last_entry, sizeof (struct utmp));
148   *result = buffer;
149
150   return 0;
151 }
152
153
154 /* For implementing this function we don't use the getutent_r function
155    because we can avoid the reposition on every new entry this way.  */
156 static int
157 getutline_r_file (const struct utmp *line, struct utmp *buffer,
158                   struct utmp **result)
159 {
160   if (file_fd < 0 || file_offset == -1l)
161     {
162       *result = NULL;
163       return -1;
164     }
165
166   while (1)
167     {
168       /* Read the next entry.  */
169       if (read (file_fd, &last_entry, sizeof (struct utmp))
170           != sizeof (struct utmp))
171         {
172           __set_errno (ESRCH);
173           file_offset = -1l;
174           *result = NULL;
175           return -1;
176         }
177       file_offset += sizeof (struct utmp);
178
179       /* Stop if we found a user or login entry.  */
180       if (
181 #if _HAVE_UT_TYPE - 0
182           (last_entry.ut_type == USER_PROCESS
183            || last_entry.ut_type == LOGIN_PROCESS)
184           &&
185 #endif
186           !strncmp (line->ut_line, last_entry.ut_line, sizeof line->ut_line))
187         break;
188     }
189
190   memcpy (buffer, &last_entry, sizeof (struct utmp));
191   *result = buffer;
192
193   return 0;
194 }
195
196
197 static int
198 internal_getutid_r (const struct utmp *id, struct utmp *buffer)
199 {
200   if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
201       || id->ut_type == OLD_TIME || id->ut_type == NEW_TIME)
202     {
203       /* Search for next entry with type RUN_LVL, BOOT_TIME,
204          OLD_TIME, or NEW_TIME.  */
205
206       while (1)
207         {
208           /* Read the next entry.  */
209           if (read (file_fd, buffer, sizeof (struct utmp))
210               != sizeof (struct utmp))
211             {
212               __set_errno (ESRCH);
213               file_offset = -1l;
214               return -1;
215             }
216           file_offset += sizeof (struct utmp);
217
218           if (id->ut_type == buffer->ut_type)
219             break;
220         }
221     }
222   else
223     {
224       /* Search for the next entry with the specified ID and with type
225          INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, or DEAD_PROCESS.  */
226
227       while (1)
228         {
229           /* Read the next entry.  */
230           if (read (file_fd, buffer, sizeof (struct utmp))
231               != sizeof (struct utmp))
232             {
233               __set_errno (ESRCH);
234               file_offset = -1l;
235               return -1;
236             }
237           file_offset += sizeof (struct utmp);
238
239           if ((   buffer->ut_type == INIT_PROCESS
240                || buffer->ut_type == LOGIN_PROCESS
241                || buffer->ut_type == USER_PROCESS
242                || buffer->ut_type == DEAD_PROCESS)
243               && strncmp (buffer->ut_id, id->ut_id, sizeof id->ut_id) == 0)
244             break;
245         }
246     }
247
248   return 0;
249 }
250
251
252 /* For implementing this function we don't use the getutent_r function
253    because we can avoid the reposition on every new entry this way.  */
254 static int
255 getutid_r_file (const struct utmp *id, struct utmp *buffer,
256                 struct utmp **result)
257 {
258   if (file_fd < 0 || file_offset == -1l)
259     {
260       *result = NULL;
261       return -1;
262     }
263
264   if (internal_getutid_r (id, &last_entry) < 0)
265     {
266       *result = NULL;
267       return -1;
268     }
269
270   memcpy (buffer, &last_entry, sizeof (struct utmp));
271   *result = buffer;
272
273   return 0;
274 }
275
276
277 static struct utmp *
278 pututline_file (const struct utmp *data)
279 {
280   struct utmp buffer;
281   struct utmp *pbuf;
282   int found;
283
284   if (file_fd < 0)
285     /* Something went wrong.  */
286     return NULL;
287
288   /* Find the correct place to insert the data.  */
289   if (file_offset > 0)
290     found = 0;
291   else
292     if (   last_entry.ut_type == RUN_LVL
293         || last_entry.ut_type == BOOT_TIME
294         || last_entry.ut_type == OLD_TIME
295         || last_entry.ut_type == NEW_TIME
296         || ((   last_entry.ut_type == INIT_PROCESS
297              || last_entry.ut_type == LOGIN_PROCESS
298              || last_entry.ut_type == USER_PROCESS
299              || last_entry.ut_type == DEAD_PROCESS)
300             && !strncmp (last_entry.ut_id, data->ut_id, sizeof data->ut_id)))
301       found = 1;
302     else
303       found = internal_getutid_r (data, &buffer);
304
305   /* Try to lock the file.  */
306   if (flock (file_fd, LOCK_EX | LOCK_NB) < 0 && errno != ENOSYS)
307     {
308       /* Oh, oh.  The file is already locked.  Wait a bit and try again.  */
309       sleep (1);
310
311       /* This time we ignore the error.  */
312       (void) flock (file_fd, LOCK_EX | LOCK_NB);
313     }
314
315   if (found < 0)
316     {
317       /* We append the next entry.  */
318       file_offset = lseek (file_fd, 0, SEEK_END);
319       if (file_offset % sizeof (struct utmp) != 0)
320         {
321           file_offset -= file_offset % sizeof (struct utmp);
322           ftruncate (file_fd, file_offset);
323
324           if (lseek (file_fd, 0, SEEK_END) < 0)
325             {
326               (void) flock (file_fd, LOCK_UN);
327               return NULL;
328             }
329         }
330     }
331   else
332     {
333       /* We replace the just read entry.  */
334       file_offset -= sizeof (struct utmp);
335       lseek (file_fd, file_offset, SEEK_SET);
336     }
337
338   /* Write the new data.  */
339   if (write (file_fd, data, sizeof (struct utmp)) != sizeof (struct utmp)
340       /* If we appended a new record this is only partially written.
341          Remove it.  */
342       && found < 0)
343     {
344       (void) ftruncate (file_fd, file_offset);
345       pbuf = NULL;
346     }
347   else
348     {
349       file_offset += sizeof (struct utmp);
350       pbuf = (struct utmp *) data;
351     }
352
353    /* And unlock the file.  */
354   (void) flock (file_fd, LOCK_UN);
355
356   return pbuf;
357 }
358
359
360 static int
361 utmpname_file (const char *name)
362 {
363   if (strcmp (name, file_name) != 0)
364     {
365       if (strcmp (name, default_file_name) == 0)
366         {
367           if (file_name != default_file_name)
368             free ((char *) file_name);
369
370           file_name = default_file_name;
371         }
372       else
373         {
374           char *new_name = __strdup (name);
375           if (new_name == NULL)
376             /* Out of memory.  */
377             return -1;
378
379           if (file_name != default_file_name)
380             free ((char *) file_name);
381
382           file_name = new_name;
383         }
384     }
385   return 0;
386 }