Minor style fix in access.c
[kopensolaris-gnu/glibc.git] / login / utmp_file.c
1 /* Copyright (C) 1996-2002, 2003, 2004, 2007 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 Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the 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    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    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 #include <not-cancel.h>
30 #include <kernel-features.h>
31
32 #include "utmp-private.h"
33 #include "utmp-equal.h"
34
35
36 /* Descriptor for the file and position.  */
37 static int file_fd = -1;
38 static off64_t file_offset;
39
40 /* Cache for the last read entry.  */
41 static struct utmp last_entry;
42
43
44 /* Locking timeout.  */
45 #ifndef TIMEOUT
46 # define TIMEOUT 1
47 #endif
48
49 /* Do-nothing handler for locking timeout.  */
50 static void timeout_handler (int signum) {};
51
52 /* LOCK_FILE(fd, type) failure_statement
53      attempts to get a lock on the utmp file referenced by FD.  If it fails,
54      the failure_statement is executed, otherwise it is skipped.
55    LOCKING_FAILED()
56      jumps into the UNLOCK_FILE macro and ensures cleanup of LOCK_FILE.
57    UNLOCK_FILE(fd)
58      unlocks the utmp file referenced by FD and performs the cleanup of
59      LOCK_FILE.
60  */
61 #define LOCK_FILE(fd, type) \
62 {                                                                             \
63   struct flock fl;                                                            \
64   struct sigaction action, old_action;                                        \
65   unsigned int old_timeout;                                                   \
66                                                                               \
67   /* Cancel any existing alarm.  */                                           \
68   old_timeout = alarm (0);                                                    \
69                                                                               \
70   /* Establish signal handler.  */                                            \
71   action.sa_handler = timeout_handler;                                        \
72   __sigemptyset (&action.sa_mask);                                            \
73   action.sa_flags = 0;                                                        \
74   __sigaction (SIGALRM, &action, &old_action);                                \
75                                                                               \
76   alarm (TIMEOUT);                                                            \
77                                                                               \
78   /* Try to get the lock.  */                                                 \
79   memset (&fl, '\0', sizeof (struct flock));                                  \
80   fl.l_type = (type);                                                         \
81   fl.l_whence = SEEK_SET;                                                     \
82   if (fcntl_not_cancel ((fd), F_SETLKW, &fl) < 0)
83
84 #define LOCKING_FAILED() \
85   goto unalarm_return
86
87 #define UNLOCK_FILE(fd) \
88   /* Unlock the file.  */                                                     \
89   fl.l_type = F_UNLCK;                                                        \
90   fcntl_not_cancel ((fd), F_SETLKW, &fl);                                     \
91                                                                               \
92  unalarm_return:                                                              \
93   /* Reset the signal handler and alarm.  We must reset the alarm             \
94      before resetting the handler so our alarm does not generate a            \
95      spurious SIGALRM seen by the user.  However, we cannot just set          \
96      the user's old alarm before restoring the handler, because then          \
97      it's possible our handler could catch the user alarm's SIGARLM           \
98      and then the user would never see the signal he expected.  */            \
99   alarm (0);                                                                  \
100   __sigaction (SIGALRM, &old_action, NULL);                                   \
101   if (old_timeout != 0)                                                       \
102     alarm (old_timeout);                                                      \
103 } while (0)
104
105
106 /* Functions defined here.  */
107 static int setutent_file (void);
108 static int getutent_r_file (struct utmp *buffer, struct utmp **result);
109 static int getutid_r_file (const struct utmp *key, struct utmp *buffer,
110                            struct utmp **result);
111 static int getutline_r_file (const struct utmp *key, struct utmp *buffer,
112                              struct utmp **result);
113 static struct utmp *pututline_file (const struct utmp *data);
114 static void endutent_file (void);
115 static int updwtmp_file (const char *file, const struct utmp *utmp);
116
117 /* Jump table for file functions.  */
118 const struct utfuncs __libc_utmp_file_functions =
119 {
120   setutent_file,
121   getutent_r_file,
122   getutid_r_file,
123   getutline_r_file,
124   pututline_file,
125   endutent_file,
126   updwtmp_file
127 };
128
129
130 #ifndef TRANSFORM_UTMP_FILE_NAME
131 # define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
132 #endif
133
134 static int
135 setutent_file (void)
136 {
137   if (file_fd < 0)
138     {
139       const char *file_name;
140       int result;
141
142       file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
143
144 #ifdef O_CLOEXEC
145 # define O_flags O_LARGEFILE | O_CLOEXEC
146 #else
147 # define O_flags O_LARGEFILE
148 #endif
149       file_fd = open_not_cancel_2 (file_name, O_RDWR | O_flags);
150       if (file_fd == -1)
151         {
152           /* Hhm, read-write access did not work.  Try read-only.  */
153           file_fd = open_not_cancel_2 (file_name, O_RDONLY | O_flags);
154           if (file_fd == -1)
155             return 0;
156         }
157
158 #ifndef __ASSUME_O_CLOEXEC
159 # ifdef O_CLOEXEC
160       if (__have_o_cloexec <= 0)
161 # endif
162         {
163           /* We have to make sure the file is `closed on exec'.  */
164           result = fcntl_not_cancel (file_fd, F_GETFD, 0);
165           if (result >= 0)
166             {
167 # ifdef O_CLOEXEC
168               if (__have_o_cloexec == 0)
169                 __have_o_cloexec = (result & FD_CLOEXEC) ? 1 : -1;
170 # endif
171
172               result = fcntl_not_cancel (file_fd, F_SETFD,
173                                          result | FD_CLOEXEC);
174             }
175
176           if (result == -1)
177             {
178               close_not_cancel_no_status (file_fd);
179               return 0;
180             }
181         }
182 #endif
183     }
184
185   __lseek64 (file_fd, 0, SEEK_SET);
186   file_offset = 0;
187
188   /* Make sure the entry won't match.  */
189 #if _HAVE_UT_TYPE - 0
190   last_entry.ut_type = -1;
191 #else
192   last_entry.ut_line[0] = '\177';
193 # if _HAVE_UT_ID - 0
194   last_entry.ut_id[0] = '\0';
195 # endif
196 #endif
197
198   return 1;
199 }
200
201
202 static int
203 getutent_r_file (struct utmp *buffer, struct utmp **result)
204 {
205   ssize_t nbytes;
206
207   assert (file_fd >= 0);
208
209   if (file_offset == -1l)
210     {
211       /* Not available.  */
212       *result = NULL;
213       return -1;
214     }
215
216   LOCK_FILE (file_fd, F_RDLCK)
217     {
218       nbytes = 0;
219       LOCKING_FAILED ();
220     }
221
222   /* Read the next entry.  */
223   nbytes = read_not_cancel (file_fd, &last_entry, sizeof (struct utmp));
224
225   UNLOCK_FILE (file_fd);
226
227   if (nbytes != sizeof (struct utmp))
228     {
229       if (nbytes != 0)
230         file_offset = -1l;
231       *result = NULL;
232       return -1;
233     }
234
235   /* Update position pointer.  */
236   file_offset += sizeof (struct utmp);
237
238   memcpy (buffer, &last_entry, sizeof (struct utmp));
239   *result = buffer;
240
241   return 0;
242 }
243
244
245 static int
246 internal_getut_r (const struct utmp *id, struct utmp *buffer)
247 {
248   int result = -1;
249
250   LOCK_FILE (file_fd, F_RDLCK)
251     LOCKING_FAILED ();
252
253 #if _HAVE_UT_TYPE - 0
254   if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
255       || id->ut_type == OLD_TIME || id->ut_type == NEW_TIME)
256     {
257       /* Search for next entry with type RUN_LVL, BOOT_TIME,
258          OLD_TIME, or NEW_TIME.  */
259
260       while (1)
261         {
262           /* Read the next entry.  */
263           if (read_not_cancel (file_fd, buffer, sizeof (struct utmp))
264               != sizeof (struct utmp))
265             {
266               __set_errno (ESRCH);
267               file_offset = -1l;
268               goto unlock_return;
269             }
270           file_offset += sizeof (struct utmp);
271
272           if (id->ut_type == buffer->ut_type)
273             break;
274         }
275     }
276   else
277 #endif /* _HAVE_UT_TYPE */
278     {
279       /* Search for the next entry with the specified ID and with type
280          INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, or DEAD_PROCESS.  */
281
282       while (1)
283         {
284           /* Read the next entry.  */
285           if (read_not_cancel (file_fd, buffer, sizeof (struct utmp))
286               != sizeof (struct utmp))
287             {
288               __set_errno (ESRCH);
289               file_offset = -1l;
290               goto unlock_return;
291             }
292           file_offset += sizeof (struct utmp);
293
294           if (__utmp_equal (buffer, id))
295             break;
296         }
297     }
298
299   result = 0;
300
301 unlock_return:
302   UNLOCK_FILE (file_fd);
303
304   return result;
305 }
306
307
308 /* For implementing this function we don't use the getutent_r function
309    because we can avoid the reposition on every new entry this way.  */
310 static int
311 getutid_r_file (const struct utmp *id, struct utmp *buffer,
312                 struct utmp **result)
313 {
314   assert (file_fd >= 0);
315
316   if (file_offset == -1l)
317     {
318       *result = NULL;
319       return -1;
320     }
321
322   if (internal_getut_r (id, &last_entry) < 0)
323     {
324       *result = NULL;
325       return -1;
326     }
327
328   memcpy (buffer, &last_entry, sizeof (struct utmp));
329   *result = buffer;
330
331   return 0;
332 }
333
334
335 /* For implementing this function we don't use the getutent_r function
336    because we can avoid the reposition on every new entry this way.  */
337 static int
338 getutline_r_file (const struct utmp *line, struct utmp *buffer,
339                   struct utmp **result)
340 {
341   assert (file_fd >= 0);
342
343   if (file_offset == -1l)
344     {
345       *result = NULL;
346       return -1;
347     }
348
349   LOCK_FILE (file_fd, F_RDLCK)
350     {
351       *result = NULL;
352       LOCKING_FAILED ();
353     }
354
355   while (1)
356     {
357       /* Read the next entry.  */
358       if (read_not_cancel (file_fd, &last_entry, sizeof (struct utmp))
359           != sizeof (struct utmp))
360         {
361           __set_errno (ESRCH);
362           file_offset = -1l;
363           *result = NULL;
364           goto unlock_return;
365         }
366       file_offset += sizeof (struct utmp);
367
368       /* Stop if we found a user or login entry.  */
369       if (
370 #if _HAVE_UT_TYPE - 0
371           (last_entry.ut_type == USER_PROCESS
372            || last_entry.ut_type == LOGIN_PROCESS)
373           &&
374 #endif
375           !strncmp (line->ut_line, last_entry.ut_line, sizeof line->ut_line))
376         break;
377     }
378
379   memcpy (buffer, &last_entry, sizeof (struct utmp));
380   *result = buffer;
381
382 unlock_return:
383   UNLOCK_FILE (file_fd);
384
385   return ((*result == NULL) ? -1 : 0);
386 }
387
388
389 static struct utmp *
390 pututline_file (const struct utmp *data)
391 {
392   struct utmp buffer;
393   struct utmp *pbuf;
394   int found;
395
396   assert (file_fd >= 0);
397
398   /* Find the correct place to insert the data.  */
399   if (file_offset > 0
400       && (
401 #if _HAVE_UT_TYPE - 0
402           (last_entry.ut_type == data->ut_type
403            && (last_entry.ut_type == RUN_LVL
404                || last_entry.ut_type == BOOT_TIME
405                || last_entry.ut_type == OLD_TIME
406                || last_entry.ut_type == NEW_TIME))
407           ||
408 #endif
409           __utmp_equal (&last_entry, data)))
410     found = 1;
411   else
412     found = internal_getut_r (data, &buffer);
413
414   LOCK_FILE (file_fd, F_WRLCK)
415     {
416       pbuf = NULL;
417       LOCKING_FAILED ();
418     }
419
420   if (found < 0)
421     {
422       /* We append the next entry.  */
423       file_offset = __lseek64 (file_fd, 0, SEEK_END);
424       if (file_offset % sizeof (struct utmp) != 0)
425         {
426           file_offset -= file_offset % sizeof (struct utmp);
427           __ftruncate64 (file_fd, file_offset);
428
429           if (__lseek64 (file_fd, 0, SEEK_END) < 0)
430             {
431               pbuf = NULL;
432               goto unlock_return;
433             }
434         }
435     }
436   else
437     {
438       /* We replace the just read entry.  */
439       file_offset -= sizeof (struct utmp);
440       __lseek64 (file_fd, file_offset, SEEK_SET);
441     }
442
443   /* Write the new data.  */
444   if (write_not_cancel (file_fd, data, sizeof (struct utmp))
445       != sizeof (struct utmp))
446     {
447       /* If we appended a new record this is only partially written.
448          Remove it.  */
449       if (found < 0)
450         (void) __ftruncate64 (file_fd, file_offset);
451       pbuf = NULL;
452     }
453   else
454     {
455       file_offset += sizeof (struct utmp);
456       pbuf = (struct utmp *) data;
457     }
458
459  unlock_return:
460   UNLOCK_FILE (file_fd);
461
462   return pbuf;
463 }
464
465
466 static void
467 endutent_file (void)
468 {
469   assert (file_fd >= 0);
470
471   close_not_cancel_no_status (file_fd);
472   file_fd = -1;
473 }
474
475
476 static int
477 updwtmp_file (const char *file, const struct utmp *utmp)
478 {
479   int result = -1;
480   off64_t offset;
481   int fd;
482
483   /* Open WTMP file.  */
484   fd = open_not_cancel_2 (file, O_WRONLY | O_LARGEFILE);
485   if (fd < 0)
486     return -1;
487
488   LOCK_FILE (fd, F_WRLCK)
489     LOCKING_FAILED ();
490
491   /* Remember original size of log file.  */
492   offset = __lseek64 (fd, 0, SEEK_END);
493   if (offset % sizeof (struct utmp) != 0)
494     {
495       offset -= offset % sizeof (struct utmp);
496       __ftruncate64 (fd, offset);
497
498       if (__lseek64 (fd, 0, SEEK_END) < 0)
499         goto unlock_return;
500     }
501
502   /* Write the entry.  If we can't write all the bytes, reset the file
503      size back to the original size.  That way, no partial entries
504      will remain.  */
505   if (write_not_cancel (fd, utmp, sizeof (struct utmp))
506       != sizeof (struct utmp))
507     {
508       __ftruncate64 (fd, offset);
509       goto unlock_return;
510     }
511
512   result = 0;
513
514 unlock_return:
515   UNLOCK_FILE (fd);
516
517   /* Close WTMP file.  */
518   close_not_cancel_no_status (fd);
519
520   return result;
521 }