Branch data Line data Source code
1 : : #include "test_libtestitall_all.h"
2 : :
3 : : #include <errno.h>
4 : :
5 : : /**
6 : : * @brief Require a launch error rather than an accepted child exit code
7 : : *
8 : : * The caller buffers start with stale content and must be cleared even when
9 : : * no child starts. A requested exit code of 127 must not make a spawn error
10 : : * successful. The complete diagnostic remains in the shared STDERR buffer
11 : : *
12 : : * @param expected_errno Platform error code returned when the executable cannot start
13 : : * @return SUCCESS when the launch failure and output buffers match expectations
14 : : */
15 : 3 : static Return check_external_spawn_error(int expected_errno)
16 : : {
17 : : /* Status returned by this function through provide()
18 : : Default value assumes successful completion */
19 : 3 : Return status = SUCCESS;
20 : 3 : m_create(char,captured_stdout,MEMORY_STRING);
21 : 3 : m_create(char,captured_stderr,MEMORY_STRING);
22 : : char expected_error_pattern[128];
23 : :
24 : 3 : ASSERT(SUCCESS == m_copy_literal(captured_stdout,"stale stdout"));
25 : 3 : ASSERT(SUCCESS == m_copy_literal(captured_stderr,"stale stderr"));
26 : 3 : ASSERT(FAILURE & runit("",captured_stdout,captured_stderr,127,STDERR_ALLOW));
27 : 3 : ASSERT(captured_stdout->string_length == 0U);
28 : 3 : ASSERT(captured_stderr->string_length == 0U);
29 : 3 : ASSERT(STDOUT->string_length == 0U);
30 : 3 : ASSERT(snprintf(expected_error_pattern,sizeof(expected_error_pattern),
31 : : "\\AFailed to spawn process for EXTERNAL_CALL \\(errno %d\\)\\z",expected_errno) > 0);
32 : 3 : ASSERT(SUCCESS == assert_stderr_matches_pattern(expected_error_pattern));
33 : :
34 : 3 : call(m_del(captured_stderr));
35 : 3 : call(m_del(captured_stdout));
36 : :
37 : 3 : deliver(status);
38 : : }
39 : :
40 : : /**
41 : : * @brief Check external runit execution context and executable launch failures
42 : : *
43 : : * A temporary precizer script runs /bin/sh to expose the child's working
44 : : * directory, inherited environment, and quoted arguments through captured
45 : : * output. A real exit code of 127 is accepted when requested, while missing,
46 : : * nonexecutable, and unsupported executable files remain launch failures.
47 : : * The test restores the runner mode, environment, and parent working directory
48 : : * and removes its temporary files even after a failed assertion
49 : : *
50 : : * @return Return status code
51 : : */
52 : 1 : Return test_libtestitall_0009(void)
53 : : {
54 : 1 : INITTEST;
55 : :
56 : : static const char *const environment_names[] = {
57 : : "BINDIR","TMPDIR","TESTITALL_SPAWN_TEST_VALUE"
58 : : };
59 : 1 : const char inherited_value[] = "inherited value ; $literal";
60 : 1 : const char arguments[] =
61 : : "-c 'pwd -P; "
62 : : "printf \"env=<%s>\\nargc=<%s>\\narg1=<%s>\\narg2=<%s>\\narg3=<%s>\\n\" "
63 : : "\"$TESTITALL_SPAWN_TEST_VALUE\" \"$#\" \"$1\" \"$2\" \"$3\"; "
64 : : "printf \"captured stderr\\n\" >&2' "
65 : : "child 'two words' '' 'literal ; | & > $dollar *'";
66 : 1 : char *saved_environment[3] = {NULL,NULL,NULL};
67 : 1 : const size_t environment_count = sizeof(environment_names) / sizeof(environment_names[0]);
68 : 1 : const enum run_mode saved_run_mode = testitall_runit_mode;
69 : 1 : bool environment_snapshot_ready = false;
70 : 1 : bool directory_created = false;
71 : 1 : bool program_created = false;
72 : 1 : char *original_directory = NULL;
73 : 1 : char *current_directory = NULL;
74 : 1 : char *canonical_temporary_directory = NULL;
75 : 1 : char *expected_stdout = NULL;
76 : 1 : FILE *program_file = NULL;
77 : 1 : m_create(char,temporary_directory,MEMORY_STRING);
78 : 1 : m_create(char,program_path,MEMORY_STRING);
79 : 1 : m_create(char,captured_stdout,MEMORY_STRING);
80 : 1 : m_create(char,captured_stderr,MEMORY_STRING);
81 : :
82 [ + + + - ]: 4 : for(size_t index = 0U; index < environment_count && SUCCESS == status; ++index)
83 : : {
84 : 3 : const char *value = getenv(environment_names[index]);
85 : :
86 [ + + ]: 3 : if(value != NULL)
87 : : {
88 : 2 : saved_environment[index] = strdup(value);
89 : 2 : ASSERT(saved_environment[index] != NULL);
90 : : }
91 : : }
92 : :
93 [ + - ]: 1 : if(SUCCESS == status)
94 : : {
95 : 1 : environment_snapshot_ready = true;
96 : 1 : original_directory = getcwd(NULL,0);
97 : 1 : ASSERT(original_directory != NULL);
98 : : }
99 : :
100 : 1 : ASSERT(SUCCESS == create_tmpdir(temporary_directory));
101 : :
102 [ + - ]: 1 : if(SUCCESS == status)
103 : : {
104 : 1 : directory_created = true;
105 : 1 : canonical_temporary_directory = realpath(m_text(temporary_directory),NULL);
106 : 1 : ASSERT(canonical_temporary_directory != NULL);
107 : : }
108 : :
109 : 1 : ASSERT(SUCCESS == m_copy(program_path,temporary_directory));
110 : 1 : ASSERT(SUCCESS == m_concat_literal(program_path,"/precizer"));
111 : 1 : ASSERT(setenv("BINDIR",m_text(temporary_directory),1) == 0);
112 : 1 : ASSERT(setenv("TMPDIR",m_text(temporary_directory),1) == 0);
113 : 1 : ASSERT(setenv("TESTITALL_SPAWN_TEST_VALUE",inherited_value,1) == 0);
114 : :
115 [ + - ]: 1 : if(SUCCESS == status)
116 : : {
117 : 1 : testitall_runit_mode = EXTERNAL_CALL;
118 : : }
119 : :
120 : 1 : ASSERT(SUCCESS == check_external_spawn_error(ENOENT));
121 : :
122 [ + - ]: 1 : if(SUCCESS == status)
123 : : {
124 : 1 : program_file = fopen(m_text(program_path),"wb");
125 : 1 : ASSERT(program_file != NULL);
126 : : }
127 : :
128 [ + - ]: 1 : if(SUCCESS == status)
129 : : {
130 : 1 : program_created = true;
131 : 1 : ASSERT(fputs("#!/bin/sh\nexec /bin/sh \"$@\"\n",program_file) != EOF);
132 : : }
133 : :
134 [ + - ]: 1 : if(program_file != NULL)
135 : : {
136 : 1 : const int close_status = fclose(program_file);
137 : 1 : program_file = NULL;
138 : 1 : ASSERT(close_status == 0);
139 : : }
140 : :
141 : 1 : ASSERT(chmod(m_text(program_path),0700) == 0);
142 : :
143 : 1 : ASSERT(asprintf(&expected_stdout,
144 : : "%s\nenv=<%s>\nargc=<3>\narg1=<two words>\narg2=<>\narg3=<literal ; | & > $dollar *>\n",
145 : : canonical_temporary_directory,inherited_value) >= 0);
146 : 1 : ASSERT(SUCCESS == runit(arguments,captured_stdout,captured_stderr,0,STDERR_ALLOW));
147 : 1 : ASSERT(strcmp(m_text(captured_stdout),expected_stdout) == 0);
148 : 1 : ASSERT(strcmp(m_text(captured_stderr),"captured stderr\n") == 0);
149 : :
150 : 1 : ASSERT(SUCCESS == runit("-c 'exit 127'",captured_stdout,captured_stderr,127,STDERR_ALLOW));
151 : 1 : ASSERT(captured_stdout->string_length == 0U);
152 : 1 : ASSERT(captured_stderr->string_length == 0U);
153 : 1 : ASSERT(unlink(m_text(program_path)) == 0);
154 : :
155 [ + - ]: 1 : if(SUCCESS == status)
156 : : {
157 : 1 : program_created = false;
158 : 1 : program_file = fopen(m_text(program_path),"wb");
159 : 1 : ASSERT(program_file != NULL);
160 : : }
161 : :
162 [ + - ]: 1 : if(SUCCESS == status)
163 : : {
164 : 1 : program_created = true;
165 : 1 : ASSERT(fputs("printf unexpected-shell-fallback\n",program_file) != EOF);
166 : : }
167 : :
168 [ + - ]: 1 : if(program_file != NULL)
169 : : {
170 : 1 : const int close_status = fclose(program_file);
171 : 1 : program_file = NULL;
172 : 1 : ASSERT(close_status == 0);
173 : : }
174 : :
175 : 1 : ASSERT(chmod(m_text(program_path),0600) == 0);
176 : 1 : ASSERT(SUCCESS == check_external_spawn_error(EACCES));
177 : 1 : ASSERT(chmod(m_text(program_path),0700) == 0);
178 : 1 : ASSERT(SUCCESS == check_external_spawn_error(ENOEXEC));
179 : :
180 [ + - ]: 1 : if(SUCCESS == status)
181 : : {
182 : 1 : current_directory = getcwd(NULL,0);
183 : 1 : ASSERT(current_directory != NULL);
184 : 1 : ASSERT(strcmp(current_directory,original_directory) == 0);
185 : : }
186 : :
187 : : /* Restore shared state even when an assertion stopped the test */
188 : 1 : testitall_runit_mode = saved_run_mode;
189 : :
190 [ + - ]: 1 : if(environment_snapshot_ready == true)
191 : : {
192 [ + + ]: 4 : for(size_t index = 0U; index < environment_count; ++index)
193 : : {
194 : 3 : int restore_status = 0;
195 : :
196 [ + + ]: 3 : if(saved_environment[index] != NULL)
197 : : {
198 : 2 : restore_status = setenv(environment_names[index],saved_environment[index],1);
199 : : } else {
200 : 1 : restore_status = unsetenv(environment_names[index]);
201 : : }
202 : :
203 : 3 : ASSERT(restore_status == 0);
204 : : }
205 : : }
206 : :
207 [ + - ]: 1 : if(original_directory != NULL)
208 : : {
209 : 1 : const int restore_status = chdir(original_directory);
210 : 1 : ASSERT(restore_status == 0);
211 : : }
212 : :
213 [ + - ]: 1 : if(program_created == true)
214 : : {
215 : 1 : const int remove_status = unlink(m_text(program_path));
216 : 1 : ASSERT(remove_status == 0);
217 : : }
218 : :
219 [ + - ]: 1 : if(directory_created == true)
220 : : {
221 : 1 : const int remove_status = rmdir(m_text(temporary_directory));
222 : 1 : ASSERT(remove_status == 0);
223 : : }
224 : :
225 [ + + ]: 4 : for(size_t index = 0U; index < environment_count; ++index)
226 : : {
227 : 3 : free(saved_environment[index]);
228 : : }
229 : :
230 : 1 : free(expected_stdout);
231 : 1 : free(canonical_temporary_directory);
232 : 1 : free(current_directory);
233 : 1 : free(original_directory);
234 : 1 : call(m_del(captured_stderr));
235 : 1 : call(m_del(captured_stdout));
236 : 1 : call(m_del(program_path));
237 : 1 : call(m_del(temporary_directory));
238 : :
239 : 1 : RETURN_STATUS;
240 : : }
|