]>
Commit | Line | Data |
---|---|---|
61d3db42 | 1 | /* Test the lock upgrade path in tst-pututxline. |
6d7e8eda | 2 | Copyright (C) 2019-2023 Free Software Foundation, Inc. |
61d3db42 FW |
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 License as | |
7 | published by the Free Software Foundation; either version 2.1 of the | |
8 | 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; see the file COPYING.LIB. If | |
5a82c748 | 17 | not, see <https://www.gnu.org/licenses/>. */ |
61d3db42 FW |
18 | |
19 | /* pututxline upgrades the read lock on the file to a write lock. | |
20 | This test verifies that if the lock upgrade fails, the utmp | |
21 | subsystem remains in a consistent state, so that pututxline can be | |
22 | called again. */ | |
23 | ||
24 | #include <errno.h> | |
25 | #include <fcntl.h> | |
26 | #include <stdlib.h> | |
27 | #include <support/check.h> | |
28 | #include <support/namespace.h> | |
29 | #include <support/support.h> | |
30 | #include <support/temp_file.h> | |
31 | #include <support/xthread.h> | |
32 | #include <support/xunistd.h> | |
33 | #include <unistd.h> | |
34 | #include <utmp.h> | |
35 | #include <utmpx.h> | |
36 | ||
37 | /* Path to the temporary utmp file. */ | |
38 | static char *path; | |
39 | ||
40 | /* Used to synchronize the subprocesses. The barrier itself is | |
41 | allocated in shared memory. */ | |
42 | static pthread_barrier_t *barrier; | |
43 | ||
44 | /* Use pututxline to write an entry for PID. */ | |
45 | static struct utmpx * | |
46 | write_entry (pid_t pid) | |
47 | { | |
48 | struct utmpx ut = | |
49 | { | |
50 | .ut_type = LOGIN_PROCESS, | |
51 | .ut_id = "1", | |
52 | .ut_user = "root", | |
53 | .ut_pid = pid, | |
54 | .ut_line = "entry", | |
55 | .ut_host = "localhost", | |
56 | }; | |
57 | return pututxline (&ut); | |
58 | } | |
59 | ||
60 | /* Create the initial entry in a subprocess, so that the utmp | |
61 | subsystem in the original process is not disturbed. */ | |
62 | static void | |
63 | subprocess_create_entry (void *closure) | |
64 | { | |
65 | TEST_COMPARE (utmpname (path), 0); | |
66 | TEST_VERIFY (write_entry (101) != NULL); | |
67 | } | |
68 | ||
69 | /* Acquire an advisory read lock on PATH. */ | |
70 | __attribute__ ((noreturn)) static void | |
71 | subprocess_lock_file (void) | |
72 | { | |
73 | int fd = xopen (path, O_RDONLY, 0); | |
74 | ||
75 | struct flock64 fl = | |
76 | { | |
77 | .l_type = F_RDLCK, | |
78 | fl.l_whence = SEEK_SET, | |
79 | }; | |
80 | TEST_COMPARE (fcntl64 (fd, F_SETLKW, &fl), 0); | |
81 | ||
82 | /* Signal to the main process that the lock has been acquired. */ | |
83 | xpthread_barrier_wait (barrier); | |
84 | ||
85 | /* Wait for the unlock request from the main process. */ | |
86 | xpthread_barrier_wait (barrier); | |
87 | ||
88 | /* Implicitly unlock the file. */ | |
89 | xclose (fd); | |
90 | ||
91 | /* Overwrite the existing entry. */ | |
92 | TEST_COMPARE (utmpname (path), 0); | |
93 | errno = 0; | |
94 | setutxent (); | |
95 | TEST_COMPARE (errno, 0); | |
96 | TEST_VERIFY (write_entry (102) != NULL); | |
97 | errno = 0; | |
98 | endutxent (); | |
99 | TEST_COMPARE (errno, 0); | |
100 | ||
101 | _exit (0); | |
102 | } | |
103 | ||
104 | static int | |
105 | do_test (void) | |
106 | { | |
107 | xclose (create_temp_file ("tst-pututxline-lockfail-", &path)); | |
108 | ||
109 | { | |
110 | pthread_barrierattr_t attr; | |
111 | xpthread_barrierattr_init (&attr); | |
112 | xpthread_barrierattr_setpshared (&attr, PTHREAD_SCOPE_PROCESS); | |
113 | barrier = support_shared_allocate (sizeof (*barrier)); | |
114 | xpthread_barrier_init (barrier, &attr, 2); | |
115 | xpthread_barrierattr_destroy (&attr); | |
116 | } | |
117 | ||
118 | /* Write the initial entry. */ | |
119 | support_isolate_in_subprocess (subprocess_create_entry, NULL); | |
120 | ||
121 | pid_t locker_pid = xfork (); | |
122 | if (locker_pid == 0) | |
123 | subprocess_lock_file (); | |
124 | ||
125 | /* Wait for the file locking to complete. */ | |
126 | xpthread_barrier_wait (barrier); | |
127 | ||
128 | /* Try to add another entry. This attempt will fail, with EINTR or | |
129 | EAGAIN. */ | |
130 | TEST_COMPARE (utmpname (path), 0); | |
131 | TEST_VERIFY (write_entry (102) == NULL); | |
132 | if (errno != EINTR) | |
133 | TEST_COMPARE (errno, EAGAIN); | |
134 | ||
135 | /* Signal the subprocess to overwrite the entry. */ | |
136 | xpthread_barrier_wait (barrier); | |
137 | ||
138 | /* Wait for write and unlock to complete. */ | |
139 | { | |
140 | int status; | |
141 | xwaitpid (locker_pid, &status, 0); | |
142 | TEST_COMPARE (status, 0); | |
143 | } | |
144 | ||
145 | /* The file is no longer locked, so this operation will succeed. */ | |
146 | TEST_VERIFY (write_entry (103) != NULL); | |
147 | errno = 0; | |
148 | endutxent (); | |
149 | TEST_COMPARE (errno, 0); | |
150 | ||
151 | /* Check that there is just one entry with the expected contents. | |
152 | If pututxline becomes desynchronized internally, the entry is not | |
153 | overwritten (bug 24902). */ | |
154 | errno = 0; | |
155 | setutxent (); | |
156 | TEST_COMPARE (errno, 0); | |
157 | struct utmpx *ut = getutxent (); | |
158 | TEST_VERIFY_EXIT (ut != NULL); | |
159 | TEST_COMPARE (ut->ut_type, LOGIN_PROCESS); | |
160 | TEST_COMPARE_STRING (ut->ut_id, "1"); | |
161 | TEST_COMPARE_STRING (ut->ut_user, "root"); | |
162 | TEST_COMPARE (ut->ut_pid, 103); | |
163 | TEST_COMPARE_STRING (ut->ut_line, "entry"); | |
164 | TEST_COMPARE_STRING (ut->ut_host, "localhost"); | |
165 | TEST_VERIFY (getutxent () == NULL); | |
166 | errno = 0; | |
167 | endutxent (); | |
168 | TEST_COMPARE (errno, 0); | |
169 | ||
170 | xpthread_barrier_destroy (barrier); | |
171 | support_shared_free (barrier); | |
172 | free (path); | |
173 | return 0; | |
174 | } | |
175 | ||
176 | #include <support/test-driver.c> |