1 | /* Copyright (C) 2002-2024 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) any later version. |
8 | |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; if not, see |
16 | <https://www.gnu.org/licenses/>. */ |
17 | |
18 | #include <errno.h> |
19 | #include <fcntl.h> |
20 | #include <getopt.h> |
21 | #include <signal.h> |
22 | #include <stdlib.h> |
23 | #include <semaphore.h> |
24 | #include <sys/mman.h> |
25 | |
26 | #include <support/check.h> |
27 | #include <support/support.h> |
28 | #include <support/temp_file.h> |
29 | #include <support/xstdio.h> |
30 | #include <support/xunistd.h> |
31 | #include <support/xthread.h> |
32 | |
33 | static const char *command; |
34 | static const char *pidfile; |
35 | static const char *semfile; |
36 | static char *pidfilename; |
37 | static char *semfilename; |
38 | |
39 | static sem_t *sem; |
40 | |
41 | static void * |
42 | tf (void *arg) |
43 | { |
44 | char *cmd = xasprintf (format: "%s --direct --sem %s --pidfile %s" , |
45 | command, semfilename, pidfilename); |
46 | if (system (command: cmd)) |
47 | FAIL_EXIT1("system call unexpectedly returned" ); |
48 | /* This call should never return. */ |
49 | return NULL; |
50 | } |
51 | |
52 | static void |
53 | sl (void) |
54 | { |
55 | FILE *f = xfopen (path: pidfile, mode: "w" ); |
56 | |
57 | fprintf (f, "%lld\n" , (long long) getpid ()); |
58 | fflush (f); |
59 | |
60 | if (sem_post (sem: sem) != 0) |
61 | FAIL_EXIT1 ("sem_post: %m" ); |
62 | |
63 | struct flock fl = |
64 | { |
65 | .l_type = F_WRLCK, |
66 | .l_start = 0, |
67 | .l_whence = SEEK_SET, |
68 | .l_len = 1 |
69 | }; |
70 | if (fcntl (fd: fileno (f), F_SETLK, &fl) != 0) |
71 | FAIL_EXIT1 ("fcntl (F_SETFL): %m" ); |
72 | |
73 | sigset_t ss; |
74 | sigfillset (&ss); |
75 | sigsuspend (set: &ss); |
76 | exit (0); |
77 | } |
78 | |
79 | |
80 | static void |
81 | do_prepare (int argc, char *argv[]) |
82 | { |
83 | int semfd; |
84 | if (semfile == NULL) |
85 | semfd = create_temp_file (base: "tst-cancel7." , filename: &semfilename); |
86 | else |
87 | semfd = open (file: semfile, O_RDWR); |
88 | TEST_VERIFY_EXIT (semfd != -1); |
89 | |
90 | sem = xmmap (NULL, length: sizeof (sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, |
91 | fd: semfd); |
92 | TEST_VERIFY_EXIT (sem != SEM_FAILED); |
93 | if (semfile == NULL) |
94 | { |
95 | xftruncate (fd: semfd, length: sizeof (sem_t)); |
96 | TEST_VERIFY_EXIT (sem_init (sem, 1, 0) != -1); |
97 | } |
98 | |
99 | if (command == NULL) |
100 | command = argv[0]; |
101 | |
102 | if (pidfile) |
103 | sl (); |
104 | |
105 | int fd = create_temp_file (base: "tst-cancel7-pid-" , filename: &pidfilename); |
106 | if (fd == -1) |
107 | FAIL_EXIT1 ("create_temp_file failed: %m" ); |
108 | |
109 | xwrite (fd, " " , 1); |
110 | xclose (fd); |
111 | } |
112 | |
113 | |
114 | static int |
115 | do_test (void) |
116 | { |
117 | pthread_t th = xpthread_create (NULL, thread_func: tf, NULL); |
118 | |
119 | /* Wait to cancel until after the pid is written. */ |
120 | if (sem_wait (sem: sem) != 0) |
121 | FAIL_EXIT1 ("sem_wait: %m" ); |
122 | |
123 | xpthread_cancel (thr: th); |
124 | void *r = xpthread_join (thr: th); |
125 | |
126 | FILE *f = xfopen (path: pidfilename, mode: "r+" ); |
127 | |
128 | long long ll; |
129 | if (fscanf (stream: f, format: "%lld\n" , &ll) != 1) |
130 | FAIL_EXIT1 ("fscanf: %m" ); |
131 | |
132 | struct flock fl = |
133 | { |
134 | .l_type = F_WRLCK, |
135 | .l_start = 0, |
136 | .l_whence = SEEK_SET, |
137 | .l_len = 1 |
138 | }; |
139 | if (fcntl (fd: fileno (f), F_GETLK, &fl) != 0) |
140 | FAIL_EXIT1 ("fcntl: %m" ); |
141 | |
142 | if (fl.l_type != F_UNLCK) |
143 | { |
144 | printf (format: "child %lld still running\n" , (long long) fl.l_pid); |
145 | if (fl.l_pid == ll) |
146 | kill (pid: fl.l_pid, SIGKILL); |
147 | |
148 | return 1; |
149 | } |
150 | |
151 | xfclose (f); |
152 | |
153 | return r != PTHREAD_CANCELED; |
154 | } |
155 | |
156 | static void |
157 | do_cleanup (void) |
158 | { |
159 | FILE *f = fopen (pidfilename, "r+" ); |
160 | long long ll; |
161 | |
162 | if (f != NULL && fscanf (stream: f, format: "%lld\n" , &ll) == 1) |
163 | { |
164 | struct flock fl = |
165 | { |
166 | .l_type = F_WRLCK, |
167 | .l_start = 0, |
168 | .l_whence = SEEK_SET, |
169 | .l_len = 1 |
170 | }; |
171 | if (fcntl (fd: fileno (f), F_GETLK, &fl) == 0 && fl.l_type != F_UNLCK |
172 | && fl.l_pid == ll) |
173 | kill (pid: fl.l_pid, SIGKILL); |
174 | |
175 | fclose (f); |
176 | } |
177 | } |
178 | |
179 | #define OPT_COMMAND 10000 |
180 | #define OPT_PIDFILE 10001 |
181 | #define OPT_SEMFILE 10002 |
182 | #define CMDLINE_OPTIONS \ |
183 | { "command", required_argument, NULL, OPT_COMMAND }, \ |
184 | { "pidfile", required_argument, NULL, OPT_PIDFILE }, \ |
185 | { "sem", required_argument, NULL, OPT_SEMFILE }, |
186 | static void |
187 | cmdline_process (int c) |
188 | { |
189 | switch (c) |
190 | { |
191 | case OPT_COMMAND: |
192 | command = optarg; |
193 | break; |
194 | case OPT_PIDFILE: |
195 | pidfile = optarg; |
196 | break; |
197 | case OPT_SEMFILE: |
198 | semfile = optarg; |
199 | break; |
200 | } |
201 | } |
202 | #define CMDLINE_PROCESS cmdline_process |
203 | #define CLEANUP_HANDLER do_cleanup |
204 | #define PREPARE do_prepare |
205 | #include <support/test-driver.c> |
206 | |