]> git.ipfire.org Git - thirdparty/glibc.git/blob - login/utmp_file.c
bea63644f76442e2d7c0772df944ce67648ce9f4
[thirdparty/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 <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 /* If we appended a new record this is only partially written.
395 Remove it. */
396 && found < 0)
397 {
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 }