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