Merge branch 'upstream' into stable
[gnulib.git] / lib / readutmp.c
1 /* GNU's read utmp module.
2
3    Copyright (C) 1992-2001, 2003, 2004, 2005, 2006 Free Software
4    Foundation, Inc.
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program 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
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 /* Written by jla; revised by djm */
20
21 #include <config.h>
22
23 #include "readutmp.h"
24
25 #include <errno.h>
26 #include <stdio.h>
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <signal.h>
31 #include <stdbool.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdint.h>
35
36 #include "xalloc.h"
37
38 #if USE_UNLOCKED_IO
39 # include "unlocked-io.h"
40 #endif
41
42 /* Copy UT->ut_name into storage obtained from malloc.  Then remove any
43    trailing spaces from the copy, NUL terminate it, and return the copy.  */
44
45 char *
46 extract_trimmed_name (const STRUCT_UTMP *ut)
47 {
48   char *p, *trimmed_name;
49
50   trimmed_name = xmalloc (sizeof (UT_USER (ut)) + 1);
51   strncpy (trimmed_name, UT_USER (ut), sizeof (UT_USER (ut)));
52   /* Append a trailing NUL.  Some systems pad names shorter than the
53      maximum with spaces, others pad with NULs.  Remove any trailing
54      spaces.  */
55   trimmed_name[sizeof (UT_USER (ut))] = '\0';
56   for (p = trimmed_name + strlen (trimmed_name);
57        trimmed_name < p && p[-1] == ' ';
58        *--p = '\0')
59     continue;
60   return trimmed_name;
61 }
62
63 /* Is the utmp entry U desired by the user who asked for OPTIONS?  */
64
65 static inline bool
66 desirable_utmp_entry (STRUCT_UTMP const *u, int options)
67 {
68   bool user_proc = IS_USER_PROCESS (u);
69   if ((options & READ_UTMP_USER_PROCESS) && !user_proc)
70     return false;
71   if ((options & READ_UTMP_CHECK_PIDS)
72       && user_proc
73       && (UT_PID (u) <= 0
74           || (kill (UT_PID (u), 0) < 0 && errno == ESRCH)))
75     return false;
76   return true;
77 }
78
79 /* Read the utmp entries corresponding to file FILE into freshly-
80    malloc'd storage, set *UTMP_BUF to that pointer, set *N_ENTRIES to
81    the number of entries, and return zero.  If there is any error,
82    return -1, setting errno, and don't modify the parameters.
83    If OPTIONS & READ_UTMP_CHECK_PIDS is nonzero, omit entries whose
84    process-IDs do not currently exist.  */
85
86 #ifdef UTMP_NAME_FUNCTION
87
88 int
89 read_utmp (char const *file, size_t *n_entries, STRUCT_UTMP **utmp_buf,
90            int options)
91 {
92   size_t n_read = 0;
93   size_t n_alloc = 0;
94   STRUCT_UTMP *utmp = NULL;
95   STRUCT_UTMP *u;
96
97   /* Ignore the return value for now.
98      Solaris' utmpname returns 1 upon success -- which is contrary
99      to what the GNU libc version does.  In addition, older GNU libc
100      versions are actually void.   */
101   UTMP_NAME_FUNCTION (file);
102
103   SET_UTMP_ENT ();
104
105   while ((u = GET_UTMP_ENT ()) != NULL)
106     if (desirable_utmp_entry (u, options))
107       {
108         if (n_read == n_alloc)
109           utmp = x2nrealloc (utmp, &n_alloc, sizeof *utmp);
110
111         utmp[n_read++] = *u;
112       }
113
114   END_UTMP_ENT ();
115
116   *n_entries = n_read;
117   *utmp_buf = utmp;
118
119   return 0;
120 }
121
122 #else
123
124 int
125 read_utmp (char const *file, size_t *n_entries, STRUCT_UTMP **utmp_buf,
126            int options)
127 {
128   size_t n_read = 0;
129   size_t n_alloc = 0;
130   STRUCT_UTMP *utmp = NULL;
131   int saved_errno;
132   FILE *f = fopen (file, "r");
133
134   if (! f)
135     return -1;
136
137   for (;;)
138     {
139       if (n_read == n_alloc)
140         utmp = x2nrealloc (utmp, &n_alloc, sizeof *utmp);
141       if (fread (&utmp[n_read], sizeof utmp[n_read], 1, f) == 0)
142         break;
143       n_read += desirable_utmp_entry (&utmp[n_read], options);
144     }
145
146   saved_errno = ferror (f) ? errno : 0;
147   if (fclose (f) != 0)
148     saved_errno = errno;
149   if (saved_errno != 0)
150     {
151       free (utmp);
152       errno = saved_errno;
153       return -1;
154     }
155
156   *n_entries = n_read;
157   *utmp_buf = utmp;
158
159   return 0;
160 }
161
162 #endif