1 | /* Tests for UTMP functions. |
2 | Copyright (C) 1998-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <errno.h> |
20 | #include <error.h> |
21 | #include <stdlib.h> |
22 | #include <string.h> |
23 | #include <sys/types.h> |
24 | #include <time.h> |
25 | |
26 | #ifdef UTMPX |
27 | # include <utmpx.h> |
28 | # define utmp utmpx |
29 | # define utmpname utmpxname |
30 | # define setutent setutxent |
31 | # define getutent getutxent |
32 | # define endutent endutxent |
33 | # define getutline getutxline |
34 | # define getutid getutxid |
35 | # define pututline pututxline |
36 | #else |
37 | # include <utmp.h> |
38 | #endif |
39 | |
40 | |
41 | /* Prototype for our test function. */ |
42 | static int do_test (int argc, char *argv[]); |
43 | |
44 | /* We have a preparation function. */ |
45 | static void do_prepare (int argc, char *argv[]); |
46 | #define PREPARE do_prepare |
47 | |
48 | /* This defines the `main' function and some more. */ |
49 | #include <test-skeleton.c> |
50 | |
51 | |
52 | /* These are for the temporary file we generate. */ |
53 | char *name; |
54 | int fd; |
55 | |
56 | static void |
57 | do_prepare (int argc, char *argv[]) |
58 | { |
59 | size_t name_len; |
60 | |
61 | name_len = strlen (test_dir); |
62 | name = xmalloc (n: name_len + sizeof ("/utmpXXXXXX" )); |
63 | mempcpy (mempcpy (name, test_dir, name_len), |
64 | "/utmpXXXXXX" , sizeof ("/utmpXXXXXX" )); |
65 | |
66 | /* Open our test file. */ |
67 | fd = mkstemp (template: name); |
68 | if (fd == -1) |
69 | error (EXIT_FAILURE, errno, format: "cannot open test file `%s'" , name); |
70 | add_temp_file (name); |
71 | } |
72 | |
73 | struct utmp entry[] = |
74 | { |
75 | #define UT(a) .ut_tv = { .tv_sec = (a)} |
76 | |
77 | { .ut_type = BOOT_TIME, .ut_pid = 1, UT(1000) }, |
78 | { .ut_type = RUN_LVL, .ut_pid = 1, UT(2000) }, |
79 | { .ut_type = INIT_PROCESS, .ut_pid = 5, .ut_id = "si" , UT(3000) }, |
80 | { .ut_type = LOGIN_PROCESS, .ut_pid = 23, .ut_line = "tty1" , .ut_id = "1" , |
81 | .ut_user = "LOGIN" , UT(4000) }, |
82 | { .ut_type = USER_PROCESS, .ut_pid = 24, .ut_line = "tty2" , .ut_id = "2" , |
83 | .ut_user = "albert" , UT(8000) }, |
84 | { .ut_type = USER_PROCESS, .ut_pid = 196, .ut_line = "ttyp0" , .ut_id = "p0" , |
85 | .ut_user = "niels" , UT(10000) }, |
86 | { .ut_type = DEAD_PROCESS, .ut_line = "ttyp1" , .ut_id = "p1" , UT(16000) }, |
87 | { .ut_type = EMPTY }, |
88 | { .ut_type = EMPTY } |
89 | }; |
90 | int num_entries = sizeof entry / sizeof (struct utmp); |
91 | |
92 | time_t entry_time = 20000; |
93 | pid_t entry_pid = 234; |
94 | |
95 | static int |
96 | do_init (void) |
97 | { |
98 | int n; |
99 | |
100 | setutent (); |
101 | |
102 | for (n = 0; n < num_entries; n++) |
103 | { |
104 | if (pututline (utmp_ptr: &entry[n]) == NULL) |
105 | { |
106 | error (status: 0, errno, format: "cannot write UTMP entry" ); |
107 | return 1; |
108 | } |
109 | } |
110 | |
111 | endutent (); |
112 | |
113 | return 0; |
114 | } |
115 | |
116 | |
117 | static int |
118 | do_check (void) |
119 | { |
120 | struct utmp *ut; |
121 | int n; |
122 | |
123 | setutent (); |
124 | |
125 | n = 0; |
126 | while ((ut = getutent ())) |
127 | { |
128 | if (n < num_entries |
129 | && memcmp (ut, &entry[n], sizeof (struct utmp))) |
130 | { |
131 | error (status: 0, errnum: 0, format: "UTMP entry does not match" ); |
132 | return 1; |
133 | } |
134 | |
135 | n++; |
136 | } |
137 | |
138 | if (n != num_entries) |
139 | { |
140 | error (status: 0, errnum: 0, format: "number of UTMP entries is incorrect" ); |
141 | return 1; |
142 | } |
143 | |
144 | endutent (); |
145 | |
146 | return 0; |
147 | } |
148 | |
149 | static int |
150 | simulate_login (const char *line, const char *user) |
151 | { |
152 | int n; |
153 | |
154 | for (n = 0; n < num_entries; n++) |
155 | { |
156 | if (strcmp (line, entry[n].ut_line) == 0 |
157 | || entry[n].ut_type == DEAD_PROCESS) |
158 | { |
159 | if (entry[n].ut_pid == DEAD_PROCESS) |
160 | entry[n].ut_pid = (entry_pid += 27); |
161 | entry[n].ut_type = USER_PROCESS; |
162 | strncpy (entry[n].ut_user, user, sizeof (entry[n].ut_user)); |
163 | entry[n].ut_tv.tv_sec = (entry_time += 1000); |
164 | setutent (); |
165 | |
166 | if (pututline (utmp_ptr: &entry[n]) == NULL) |
167 | { |
168 | error (status: 0, errno, format: "cannot write UTMP entry" ); |
169 | return 1; |
170 | } |
171 | |
172 | endutent (); |
173 | |
174 | return 0; |
175 | } |
176 | } |
177 | |
178 | error (status: 0, errnum: 0, format: "no entries available" ); |
179 | return 1; |
180 | } |
181 | |
182 | static int |
183 | simulate_logout (const char *line) |
184 | { |
185 | int n; |
186 | |
187 | for (n = 0; n < num_entries; n++) |
188 | { |
189 | if (strcmp (line, entry[n].ut_line) == 0) |
190 | { |
191 | entry[n].ut_type = DEAD_PROCESS; |
192 | strncpy (entry[n].ut_user, "" , sizeof (entry[n].ut_user)); |
193 | entry[n].ut_tv.tv_sec = (entry_time += 1000); |
194 | setutent (); |
195 | |
196 | if (pututline (utmp_ptr: &entry[n]) == NULL) |
197 | { |
198 | error (status: 0, errno, format: "cannot write UTMP entry" ); |
199 | return 1; |
200 | } |
201 | |
202 | endutent (); |
203 | |
204 | return 0; |
205 | } |
206 | } |
207 | |
208 | error (status: 0, errnum: 0, format: "no entry found for `%s'" , line); |
209 | return 1; |
210 | } |
211 | |
212 | static int |
213 | check_login (const char *line) |
214 | { |
215 | struct utmp *up; |
216 | struct utmp ut; |
217 | int n; |
218 | |
219 | setutent (); |
220 | |
221 | strcpy (ut.ut_line, line); |
222 | up = getutline (line: &ut); |
223 | if (up == NULL) |
224 | { |
225 | error (status: 0, errno, format: "cannot get entry for line `%s'" , line); |
226 | return 1; |
227 | } |
228 | |
229 | endutent (); |
230 | |
231 | for (n = 0; n < num_entries; n++) |
232 | { |
233 | if (strcmp (line, entry[n].ut_line) == 0) |
234 | { |
235 | if (memcmp (up, &entry[n], sizeof (struct utmp))) |
236 | { |
237 | error (status: 0, errnum: 0, format: "UTMP entry does not match" ); |
238 | return 1; |
239 | } |
240 | |
241 | return 0; |
242 | } |
243 | } |
244 | |
245 | error (status: 0, errnum: 0, format: "bogus entry for line `%s'" , line); |
246 | return 1; |
247 | } |
248 | |
249 | static int |
250 | check_logout (const char *line) |
251 | { |
252 | struct utmp ut; |
253 | |
254 | setutent (); |
255 | |
256 | strcpy (ut.ut_line, line); |
257 | if (getutline (line: &ut) != NULL) |
258 | { |
259 | error (status: 0, errnum: 0, format: "bogus login entry for `%s'" , line); |
260 | return 1; |
261 | } |
262 | |
263 | endutent (); |
264 | |
265 | return 0; |
266 | } |
267 | |
268 | static int |
269 | check_id (const char *id) |
270 | { |
271 | struct utmp *up; |
272 | struct utmp ut; |
273 | int n; |
274 | |
275 | setutent (); |
276 | |
277 | ut.ut_type = USER_PROCESS; |
278 | strcpy (ut.ut_id, id); |
279 | up = getutid (id: &ut); |
280 | if (up == NULL) |
281 | { |
282 | error (status: 0, errno, format: "cannot get entry for ID `%s'" , id); |
283 | return 1; |
284 | } |
285 | |
286 | endutent (); |
287 | |
288 | for (n = 0; n < num_entries; n++) |
289 | { |
290 | if (strcmp (id, entry[n].ut_id) == 0) |
291 | { |
292 | if (memcmp (up, &entry[n], sizeof (struct utmp))) |
293 | { |
294 | error (status: 0, errnum: 0, format: "UTMP entry does not match" ); |
295 | return 1; |
296 | } |
297 | |
298 | return 0; |
299 | } |
300 | } |
301 | |
302 | error (status: 0, errnum: 0, format: "bogus entry for ID `%s'" , id); |
303 | return 1; |
304 | } |
305 | |
306 | static int |
307 | check_type (int type) |
308 | { |
309 | struct utmp *up; |
310 | struct utmp ut; |
311 | int n; |
312 | |
313 | setutent (); |
314 | |
315 | ut.ut_type = type; |
316 | up = getutid (id: &ut); |
317 | if (up == NULL) |
318 | { |
319 | error (status: 0, errno, format: "cannot get entry for type `%d'" , type); |
320 | return 1; |
321 | } |
322 | |
323 | endutent (); |
324 | |
325 | for (n = 0; n < num_entries; n++) |
326 | { |
327 | if (type == entry[n].ut_type) |
328 | { |
329 | if (memcmp (up, &entry[n], sizeof (struct utmp))) |
330 | { |
331 | error (status: 0, errnum: 0, format: "UTMP entry does not match" ); |
332 | return 1; |
333 | } |
334 | |
335 | return 0; |
336 | } |
337 | } |
338 | |
339 | error (status: 0, errnum: 0, format: "bogus entry for type `%d'" , type); |
340 | return 1; |
341 | } |
342 | |
343 | static int |
344 | do_test (int argc, char *argv[]) |
345 | { |
346 | int result = 0; |
347 | |
348 | utmpname (file: name); |
349 | |
350 | result |= do_init (); |
351 | result |= do_check (); |
352 | |
353 | result |= simulate_login (line: "tty1" , user: "erwin" ); |
354 | result |= do_check (); |
355 | |
356 | result |= simulate_login (line: "ttyp1" , user: "paul" ); |
357 | result |= do_check (); |
358 | |
359 | result |= simulate_logout (line: "tty2" ); |
360 | result |= do_check (); |
361 | |
362 | result |= simulate_logout (line: "ttyp0" ); |
363 | result |= do_check (); |
364 | |
365 | result |= simulate_login (line: "ttyp2" , user: "richard" ); |
366 | result |= do_check (); |
367 | |
368 | result |= check_login (line: "tty1" ); |
369 | result |= check_logout (line: "ttyp0" ); |
370 | result |= check_id (id: "p1" ); |
371 | result |= check_id (id: "2" ); |
372 | result |= check_id (id: "si" ); |
373 | result |= check_type (BOOT_TIME); |
374 | result |= check_type (RUN_LVL); |
375 | |
376 | return result; |
377 | } |
378 | |