1 | /* Tests for exec. |
2 | Copyright (C) 2000-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 <fcntl.h> |
22 | #include <stdlib.h> |
23 | #include <string.h> |
24 | #include <unistd.h> |
25 | #include <wait.h> |
26 | |
27 | |
28 | /* Nonzero if the program gets called via `exec'. */ |
29 | static int restart; |
30 | |
31 | |
32 | #define CMDLINE_OPTIONS \ |
33 | { "restart", no_argument, &restart, 1 }, |
34 | |
35 | /* Prototype for our test function. */ |
36 | extern void do_prepare (int argc, char *argv[]); |
37 | extern int do_test (int argc, char *argv[]); |
38 | |
39 | /* We have a preparation function. */ |
40 | #define PREPARE do_prepare |
41 | |
42 | #include "../test-skeleton.c" |
43 | |
44 | |
45 | /* Name of the temporary files. */ |
46 | static char *name1; |
47 | static char *name2; |
48 | |
49 | /* File descriptors for these temporary files. */ |
50 | static int temp_fd1 = -1; |
51 | static int temp_fd2 = -1; |
52 | |
53 | /* The contents of our files. */ |
54 | static const char fd1string[] = "This file should get closed" ; |
55 | static const char fd2string[] = "This file should stay opened" ; |
56 | |
57 | |
58 | /* We have a preparation function. */ |
59 | void |
60 | do_prepare (int argc, char *argv[]) |
61 | { |
62 | /* We must not open any files in the restart case. */ |
63 | if (restart) |
64 | return; |
65 | |
66 | temp_fd1 = create_temp_file (base: "exec" , filename: &name1); |
67 | temp_fd2 = create_temp_file (base: "exec" , filename: &name2); |
68 | if (temp_fd1 < 0 || temp_fd2 < 0) |
69 | exit (1); |
70 | } |
71 | |
72 | |
73 | static int |
74 | handle_restart (const char *fd1s, const char *fd2s, const char *name) |
75 | { |
76 | char buf[100]; |
77 | int fd1; |
78 | int fd2; |
79 | |
80 | /* First get the descriptors. */ |
81 | fd1 = atol (nptr: fd1s); |
82 | fd2 = atol (nptr: fd2s); |
83 | |
84 | /* Sanity check. */ |
85 | if (fd1 == fd2) |
86 | error (EXIT_FAILURE, errnum: 0, format: "value of fd1 and fd2 is the same" ); |
87 | |
88 | /* First the easy part: read from the file descriptor which is |
89 | supposed to be open. */ |
90 | if (lseek (fd: fd2, offset: 0, SEEK_CUR) != strlen (fd2string)) |
91 | error (EXIT_FAILURE, errno, format: "file 2 not in right position" ); |
92 | if (lseek (fd: fd2, offset: 0, SEEK_SET) != 0) |
93 | error (EXIT_FAILURE, errnum: 0, format: "cannot reset position in file 2" ); |
94 | if (read (fd2, buf, sizeof buf) != strlen (fd2string)) |
95 | error (EXIT_FAILURE, errnum: 0, format: "cannot read file 2" ); |
96 | if (memcmp (fd2string, buf, strlen (fd2string)) != 0) |
97 | error (EXIT_FAILURE, errnum: 0, format: "file 2 does not match" ); |
98 | |
99 | /* No try to read the first file. First make sure it is not opened. */ |
100 | if (lseek (fd: fd1, offset: 0, SEEK_CUR) != (off_t) -1 || errno != EBADF) |
101 | error (EXIT_FAILURE, errnum: 0, format: "file 1 (%d) is not closed" , fd1); |
102 | |
103 | /* Now open the file and read it. */ |
104 | fd1 = open (file: name, O_RDONLY); |
105 | if (fd1 == -1) |
106 | error (EXIT_FAILURE, errno, |
107 | format: "cannot open first file \"%s\" for verification" , name); |
108 | |
109 | if (read (fd1, buf, sizeof buf) != strlen (fd1string)) |
110 | error (EXIT_FAILURE, errno, format: "cannot read file 1" ); |
111 | if (memcmp (fd1string, buf, strlen (fd1string)) != 0) |
112 | error (EXIT_FAILURE, errnum: 0, format: "file 1 does not match" ); |
113 | |
114 | return 0; |
115 | } |
116 | |
117 | |
118 | int |
119 | do_test (int argc, char *argv[]) |
120 | { |
121 | pid_t pid; |
122 | int flags; |
123 | int status; |
124 | |
125 | /* We must have |
126 | - one or four parameters left if called initially |
127 | + path for ld.so optional |
128 | + "--library-path" optional |
129 | + the library path optional |
130 | + the application name |
131 | - three parameters left if called through re-execution |
132 | + file descriptor number which is supposed to be closed |
133 | + the open file descriptor |
134 | + the name of the closed desriptor |
135 | */ |
136 | |
137 | if (restart) |
138 | { |
139 | if (argc != 4) |
140 | error (EXIT_FAILURE, errnum: 0, format: "wrong number of arguments (%d)" , argc); |
141 | |
142 | return handle_restart (fd1s: argv[1], fd2s: argv[2], name: argv[3]); |
143 | } |
144 | |
145 | if (argc != 2 && argc != 5) |
146 | error (EXIT_FAILURE, errnum: 0, format: "wrong number of arguments (%d)" , argc); |
147 | |
148 | /* Prepare the test. We are creating two files: one which file descriptor |
149 | will be marked with FD_CLOEXEC, another which is not. */ |
150 | |
151 | /* Set the bit. */ |
152 | flags = fcntl (fd: temp_fd1, F_GETFD, 0); |
153 | if (flags < 0) |
154 | error (EXIT_FAILURE, errno, format: "cannot get flags" ); |
155 | flags |= FD_CLOEXEC; |
156 | if (fcntl (fd: temp_fd1, F_SETFD, flags) < 0) |
157 | error (EXIT_FAILURE, errno, format: "cannot set flags" ); |
158 | |
159 | /* Write something in the files. */ |
160 | if (write (temp_fd1, fd1string, strlen (fd1string)) != strlen (fd1string)) |
161 | error (EXIT_FAILURE, errno, format: "cannot write to first file" ); |
162 | if (write (temp_fd2, fd2string, strlen (fd2string)) != strlen (fd2string)) |
163 | error (EXIT_FAILURE, errno, format: "cannot write to second file" ); |
164 | |
165 | /* We want to test the `exec' function. To do this we restart the program |
166 | with an additional parameter. But first create another process. */ |
167 | pid = fork (); |
168 | if (pid == 0) |
169 | { |
170 | char fd1name[18]; |
171 | char fd2name[18]; |
172 | |
173 | snprintf (s: fd1name, maxlen: sizeof fd1name, format: "%d" , temp_fd1); |
174 | snprintf (s: fd2name, maxlen: sizeof fd2name, format: "%d" , temp_fd2); |
175 | |
176 | /* This is the child. Construct the command line. */ |
177 | if (argc == 5) |
178 | execl (argv[1], argv[1], argv[2], argv[3], argv[4], "--direct" , |
179 | "--restart" , fd1name, fd2name, name1, NULL); |
180 | else |
181 | execl (argv[1], argv[1], "--direct" , |
182 | "--restart" , fd1name, fd2name, name1, NULL); |
183 | |
184 | error (EXIT_FAILURE, errno, format: "cannot exec" ); |
185 | } |
186 | else if (pid == (pid_t) -1) |
187 | error (EXIT_FAILURE, errno, format: "cannot fork" ); |
188 | |
189 | /* Wait for the child. */ |
190 | if (waitpid (pid: pid, stat_loc: &status, options: 0) != pid) |
191 | error (EXIT_FAILURE, errno, format: "wrong child" ); |
192 | |
193 | if (WTERMSIG (status) != 0) |
194 | error (EXIT_FAILURE, errnum: 0, format: "Child terminated incorrectly" ); |
195 | status = WEXITSTATUS (status); |
196 | |
197 | return status; |
198 | } |
199 | |