Line | Branch | Exec | Source |
---|---|---|---|
1 | /* GATE PROJECT LICENSE: | ||
2 | +----------------------------------------------------------------------------+ | ||
3 | | Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> | | ||
4 | | All rights reserved. | | ||
5 | | | | ||
6 | | Redistribution and use in source and binary forms, with or without | | ||
7 | | modification, are permitted provided that the following conditions are met:| | ||
8 | | | | ||
9 | | 1. Redistributions of source code must retain the above copyright notice, | | ||
10 | | this list of conditions and the following disclaimer. | | ||
11 | | 2. Redistributions in binary form must reproduce the above copyright | | ||
12 | | notice, this list of conditions and the following disclaimer in the | | ||
13 | | documentation and/or other materials provided with the distribution. | | ||
14 | | | | ||
15 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"| | ||
16 | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | ||
17 | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ||
18 | | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | | ||
19 | | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | ||
20 | | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | ||
21 | | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | ||
22 | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | ||
23 | | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | ||
24 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | | ||
25 | | THE POSSIBILITY OF SUCH DAMAGE. | | ||
26 | +----------------------------------------------------------------------------+ | ||
27 | */ | ||
28 | |||
29 | #include "gate/applications.h" | ||
30 | #include "gate/results.h" | ||
31 | #include "gate/values.h" | ||
32 | #include "gate/utilities.h" | ||
33 | |||
34 | #if defined(GATE_SYS_WIN) | ||
35 | #define GATE_CORE_APPLICATIONS_WINAPI_IMPL 1 | ||
36 | #elif defined(GATE_SYS_POSIX) | ||
37 | #define GATE_CORE_APPLCIATIONS_POSIX_IMPL 1 | ||
38 | #elif defined(GATE_SYS_EFI) | ||
39 | #define GATE_CORE_APPLCIATIONS_EFI_IMPL 1 | ||
40 | #else | ||
41 | #define GATE_CORE_APPLICATIONS_NO_IMPL 1 | ||
42 | #endif | ||
43 | |||
44 | 1 | gate_size_t gate_app_parse_args(gate_string_t const* argline, gate_string_t* dest_args, gate_size_t max_dest_args) | |
45 | { | ||
46 | 1 | gate_size_t used = 0; | |
47 | gate_string_t line; | ||
48 | gate_size_t pos; | ||
49 | 1 | gate_bool_t in_arg = false; | |
50 | 1 | gate_bool_t in_quote = false; | |
51 | 1 | gate_size_t begin_pos = 0; | |
52 | char chr; | ||
53 | |||
54 | 1 | gate_string_duplicate(&line, argline); | |
55 | |||
56 | 1 | gate_string_trim(&line, &line); | |
57 | |||
58 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (!gate_string_is_empty(&line)) |
59 | { | ||
60 |
3/4✓ Branch 0 taken 19 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
|
20 | for (pos = 0; (pos < line.length) && (used < max_dest_args); ++pos) |
61 | { | ||
62 | 19 | chr = line.str[pos]; | |
63 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 15 times.
|
19 | if (!in_arg) |
64 | { | ||
65 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
|
4 | if (gate_char_is_whitespace(chr)) |
66 | { | ||
67 | ✗ | continue; /* skip char */ | |
68 | } | ||
69 | 4 | in_arg = true; | |
70 | 4 | begin_pos = pos; | |
71 | } | ||
72 | |||
73 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
19 | if (chr == '\"') |
74 | { | ||
75 | ✗ | in_quote = !in_quote; | |
76 | } | ||
77 |
3/4✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 16 times.
|
19 | else if (!in_quote && gate_char_is_whitespace(chr)) |
78 | { | ||
79 | /* end of previous command*/ | ||
80 | 3 | in_arg = false; | |
81 | 3 | gate_string_substr(&dest_args[used], &line, begin_pos, pos - begin_pos); | |
82 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (dest_args[used].length >= 2) |
83 | { | ||
84 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
3 | if ((dest_args[used].str[0] == '\"') && (dest_args[used].str[dest_args[used].length - 1] == '\"')) |
85 | { | ||
86 | ✗ | ++dest_args[used].str; | |
87 | ✗ | dest_args[used].length -= 2; | |
88 | } | ||
89 | } | ||
90 | 3 | ++used; | |
91 | } | ||
92 | } | ||
93 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (in_arg) |
94 | { | ||
95 | 1 | gate_string_substr(&dest_args[used], &line, begin_pos, pos - begin_pos); | |
96 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (dest_args[used].length >= 2) |
97 | { | ||
98 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1 | if ((dest_args[used].str[0] == '\"') && (dest_args[used].str[dest_args[used].length - 1] == '\"')) |
99 | { | ||
100 | ✗ | ++dest_args[used].str; | |
101 | ✗ | dest_args[used].length -= 2; | |
102 | } | ||
103 | } | ||
104 | 1 | ++used; | |
105 | } | ||
106 | } | ||
107 | 1 | gate_string_release(&line); | |
108 | 1 | return used; | |
109 | } | ||
110 | |||
111 | 22 | static void skip_white_spaces(char const** ptr_buffer, gate_size_t* ptr_buffer_len) | |
112 | { | ||
113 | 22 | char const* buffer = *ptr_buffer; | |
114 | 22 | gate_size_t buffer_len = *ptr_buffer_len; | |
115 |
3/4✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 22 times.
|
26 | while ((buffer_len > 0) && (gate_char_is_whitespace(*buffer))) |
116 | { | ||
117 | 4 | ++buffer; | |
118 | 4 | --buffer_len; | |
119 | } | ||
120 | 22 | *ptr_buffer = buffer; | |
121 | 22 | *ptr_buffer_len = buffer_len; | |
122 | 22 | } | |
123 | |||
124 | 6 | gate_size_t gate_app_parse_args_buffer(gate_string_t const* argline, char* buffer, size_t buffer_len, | |
125 | char const** dest_args, gate_size_t max_dest_args) | ||
126 | { | ||
127 | 6 | gate_size_t used = 0; | |
128 | 6 | gate_bool_t in_arg = false; | |
129 | 6 | gate_bool_t in_quote = false; | |
130 | char chr; | ||
131 | char const* ptr_argline; | ||
132 | gate_size_t argline_len; | ||
133 | 6 | char* ptr_arg_start = NULL; | |
134 | |||
135 | 6 | ptr_argline = gate_string_ptr(argline, 0); | |
136 | 6 | argline_len = gate_string_length(argline); | |
137 | |||
138 |
4/6✓ Branch 0 taken 128 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 122 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 122 times.
✗ Branch 5 not taken.
|
128 | while ((buffer_len > 0) && (argline_len > 0) && (max_dest_args > 0)) |
139 | { | ||
140 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 100 times.
|
122 | if (!in_arg) |
141 | { | ||
142 | 22 | skip_white_spaces(&ptr_argline, &argline_len); | |
143 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
|
22 | if (argline_len == 0) |
144 | { | ||
145 | ✗ | break; | |
146 | } | ||
147 | 22 | in_arg = true; | |
148 | 22 | in_quote = ptr_argline[0] == '\"'; | |
149 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 17 times.
|
22 | if (in_quote) |
150 | { | ||
151 | 5 | ++ptr_argline; | |
152 | 5 | --argline_len; | |
153 | } | ||
154 | 22 | ptr_arg_start = buffer; | |
155 | } | ||
156 | |||
157 | 122 | chr = *ptr_argline; | |
158 | 122 | ++ptr_argline; | |
159 | 122 | --argline_len; | |
160 |
2/2✓ Branch 0 taken 82 times.
✓ Branch 1 taken 40 times.
|
122 | if (!in_quote) |
161 | { | ||
162 | /* not in quote mode: */ | ||
163 |
2/2✓ Branch 1 taken 17 times.
✓ Branch 2 taken 65 times.
|
82 | if (gate_char_is_whitespace(chr)) |
164 | { | ||
165 | /* end of arg reached */ | ||
166 | 17 | *buffer = '\0'; | |
167 | 17 | ++buffer; | |
168 | 17 | --buffer_len; | |
169 | 17 | *dest_args = ptr_arg_start; | |
170 | 17 | ++dest_args; | |
171 | 17 | --max_dest_args; | |
172 | 17 | ++used; | |
173 | 17 | in_arg = false; | |
174 | } | ||
175 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 65 times.
|
65 | else if (chr == '\"') |
176 | { | ||
177 | /* start quote mode */ | ||
178 | ✗ | in_quote = true; | |
179 | } | ||
180 | else | ||
181 | { | ||
182 | 65 | *buffer = chr; | |
183 | 65 | ++buffer; | |
184 | 65 | --buffer_len; | |
185 | } | ||
186 | } | ||
187 | else | ||
188 | { | ||
189 | /* within quote mode */ | ||
190 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 35 times.
|
40 | if (chr == '\"') |
191 | { | ||
192 | /* end of quote mode */ | ||
193 | 5 | in_quote = false; | |
194 | } | ||
195 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 31 times.
|
35 | else if (chr == '\\') |
196 | { | ||
197 | 4 | chr = *ptr_argline; | |
198 | 4 | ++ptr_argline; | |
199 | 4 | --argline_len; | |
200 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
4 | if ((chr == '\\') || (chr == '\"')) |
201 | { | ||
202 | /* escaped backslash or quote */ | ||
203 | 4 | *buffer = chr; | |
204 | } | ||
205 | else | ||
206 | { | ||
207 | ✗ | *buffer = '\\'; | |
208 | ✗ | ++buffer; | |
209 | ✗ | --buffer_len; | |
210 | ✗ | *buffer = chr; | |
211 | } | ||
212 | 4 | ++buffer; | |
213 | 4 | --buffer_len; | |
214 | } | ||
215 | else | ||
216 | { | ||
217 | 31 | *buffer = chr; | |
218 | 31 | ++buffer; | |
219 | 31 | --buffer_len; | |
220 | } | ||
221 | } | ||
222 | } | ||
223 | |||
224 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
|
6 | if (in_arg) |
225 | { | ||
226 | 5 | *buffer = '\0'; | |
227 | 5 | ++buffer; | |
228 | 5 | --buffer_len; | |
229 | 5 | *dest_args = ptr_arg_start; | |
230 | 5 | ++dest_args; | |
231 | 5 | --max_dest_args; | |
232 | 5 | ++used; | |
233 | } | ||
234 | |||
235 | 6 | return used; | |
236 | } | ||
237 | |||
238 | 13 | static gate_size_t gate_app_option_parse_arg(gate_string_t const* arg, | |
239 | gate_string_t const* opt_key, gate_string_t const* opt_alias) | ||
240 | { | ||
241 | gate_size_t pos; | ||
242 | 13 | gate_bool_t opt_key_empty = gate_string_is_empty(opt_key); | |
243 | 13 | gate_bool_t opt_alias_empty = gate_string_is_empty(opt_alias); | |
244 | |||
245 |
4/6✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 9 times.
✗ Branch 7 not taken.
|
13 | if ((gate_string_starts_with_char(arg, '/') || gate_string_starts_with_char(arg, '-')) && !opt_alias_empty) |
246 | { | ||
247 | /* try parse argument as option alias */ | ||
248 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if (0 == gate_str_compare(&arg->str[1], arg->length - 1, opt_alias->str, opt_alias->length)) |
249 | { | ||
250 | /* full argument matches key-alias */ | ||
251 | ✗ | return gate_string_length(arg); | |
252 | } | ||
253 | else | ||
254 | { | ||
255 | 9 | pos = gate_str_char_pos(arg->str, arg->length, '=', 1); | |
256 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (pos != GATE_STR_NPOS) |
257 | { | ||
258 | ✗ | if (0 == gate_str_compare(&arg->str[1], pos - 1, opt_alias->str, opt_alias->length)) | |
259 | { | ||
260 | /* argument matches option alias, and values is placed in it. | ||
261 | return amount of parsed characters */ | ||
262 | ✗ | return pos + 1; | |
263 | } | ||
264 | } | ||
265 | } | ||
266 | } | ||
267 | |||
268 |
3/4✓ Branch 1 taken 9 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
|
13 | if (gate_string_starts_with_str(arg, "--") && !opt_key_empty) |
269 | { | ||
270 | /* try parse argument as option key */ | ||
271 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | if (0 == gate_str_compare(&arg->str[2], arg->length - 2, opt_key->str, opt_key->length)) |
272 | { | ||
273 | /* full argument is option key */ | ||
274 | 9 | return gate_string_length(arg); | |
275 | } | ||
276 | else | ||
277 | { | ||
278 | ✗ | pos = gate_str_char_pos(arg->str, arg->length, '=', 2); | |
279 | ✗ | if (pos != GATE_STR_NPOS) | |
280 | { | ||
281 | ✗ | if (0 == gate_str_compare(&arg->str[2], pos - 2, opt_key->str, opt_key->length)) | |
282 | { | ||
283 | /* argument matches option key, and value is placed in it. | ||
284 | return amount of parsed characters */ | ||
285 | ✗ | return pos + 1; | |
286 | } | ||
287 | } | ||
288 | } | ||
289 | } | ||
290 | |||
291 |
4/6✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
|
4 | if (opt_key_empty && opt_alias_empty && !gate_string_starts_with_str(arg, "--")) |
292 | { | ||
293 | /* we search for an unnamed option, everything without a leading "--" is valid */ | ||
294 | 2 | return gate_string_length(arg); | |
295 | } | ||
296 | |||
297 | /* argument does not match option key or alias */ | ||
298 | 2 | return 0; | |
299 | } | ||
300 | |||
301 | 1 | static void gate_app_option_parse_bool(gate_string_t const* str, void* ptr_bool) | |
302 | { | ||
303 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (ptr_bool) |
304 | { | ||
305 | 1 | gate_value_load_bool(GATE_TYPE_STRING, str, (gate_bool_t*)ptr_bool); | |
306 | } | ||
307 | 1 | } | |
308 | 2 | static void gate_app_option_parse_i32(gate_string_t const* str, void* ptr_i32) | |
309 | { | ||
310 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (ptr_i32) |
311 | { | ||
312 | 2 | gate_value_load_int32(GATE_TYPE_STRING, str, (gate_int32_t*)ptr_i32); | |
313 | } | ||
314 | 2 | } | |
315 | 1 | static void gate_app_option_parse_i64(gate_string_t const* str, void* ptr_i64) | |
316 | { | ||
317 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (ptr_i64) |
318 | { | ||
319 | 1 | gate_value_load_int64(GATE_TYPE_STRING, str, (gate_int64_t*)ptr_i64); | |
320 | } | ||
321 | 1 | } | |
322 | 1 | static void gate_app_option_parse_real(gate_string_t const* str, void* ptr_real) | |
323 | { | ||
324 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (ptr_real) |
325 | { | ||
326 | 1 | gate_value_load_real64(GATE_TYPE_STRING, str, (gate_real64_t*)ptr_real); | |
327 | } | ||
328 | 1 | } | |
329 | 2 | static void gate_app_option_parse_string(gate_string_t const* str, void* ptr_str) | |
330 | { | ||
331 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (ptr_str) |
332 | { | ||
333 | 2 | gate_string_clone((gate_string_t*)ptr_str, str); | |
334 | } | ||
335 | 2 | } | |
336 | 3 | static void gate_app_option_parse_stringarray(gate_string_t const* str, void* ptr_str) | |
337 | { | ||
338 | 3 | gate_arraylist_t* arr = (gate_arraylist_t*)ptr_str; | |
339 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (arr) |
340 | { | ||
341 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (!*arr) |
342 | { | ||
343 | ✗ | *arr = gate_util_stringarray_create(); | |
344 | } | ||
345 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (*arr) |
346 | { | ||
347 | 3 | gate_arraylist_add(*arr, str); | |
348 | } | ||
349 | } | ||
350 | 3 | } | |
351 | |||
352 | 28 | static gate_bool_t is_key_option(gate_app_option_t* option) | |
353 | { | ||
354 |
3/4✓ Branch 1 taken 6 times.
✓ Branch 2 taken 22 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
|
28 | return !gate_string_is_empty(&option->option_key) || !gate_string_is_empty(&option->option_alias); |
355 | } | ||
356 | |||
357 | 1 | gate_size_t gate_app_options_parse(gate_string_t const* input_args, gate_size_t input_arg_count, | |
358 | gate_app_option_t* output_options, gate_size_t output_option_count) | ||
359 | { | ||
360 | 1 | gate_size_t ret = 0; | |
361 | gate_size_t index; | ||
362 | gate_size_t opt_count; | ||
363 | gate_size_t len_parsed; | ||
364 | 1 | gate_app_option_t* opts[512] = GATE_INIT_EMPTY; | |
365 | gate_app_option_t* current_opt; | ||
366 | gate_string_t const* current_arg; | ||
367 | 1 | gate_string_t opt_value = GATE_STRING_INIT_EMPTY; | |
368 | gate_bool_t remove_opt; | ||
369 | |||
370 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (output_option_count > (sizeof(opts) / sizeof(opts[0]))) |
371 | { | ||
372 | ✗ | output_option_count = (sizeof(opts) / sizeof(opts[0])); | |
373 | } | ||
374 | |||
375 | /* create local array with non-handled options, put key-based options first in list */ | ||
376 | 1 | opt_count = 0; | |
377 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
|
10 | for (index = 0; index != output_option_count; ++index) |
378 | { | ||
379 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 2 times.
|
9 | if (is_key_option(&output_options[index])) |
380 | { | ||
381 | 7 | opts[opt_count++] = &output_options[index]; | |
382 | } | ||
383 | } | ||
384 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
|
10 | for (index = 0; index != output_option_count; ++index) |
385 | { | ||
386 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 7 times.
|
9 | if (!is_key_option(&output_options[index])) |
387 | { | ||
388 | 2 | opts[opt_count++] = &output_options[index]; | |
389 | } | ||
390 | } | ||
391 | |||
392 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1 times.
|
12 | while (input_arg_count > 0) |
393 | { | ||
394 | 11 | current_arg = input_args; | |
395 | 11 | ++input_args; | |
396 | 11 | --input_arg_count; | |
397 | |||
398 |
1/2✓ Branch 0 taken 59 times.
✗ Branch 1 not taken.
|
59 | for (index = 0; index != opt_count; ++index) |
399 | { | ||
400 | 59 | current_opt = opts[index]; | |
401 |
2/2✓ Branch 0 taken 46 times.
✓ Branch 1 taken 13 times.
|
59 | if (!current_opt) |
402 | { | ||
403 | 46 | continue; | |
404 | } | ||
405 | 13 | len_parsed = gate_app_option_parse_arg(current_arg, ¤t_opt->option_key, ¤t_opt->option_alias); | |
406 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 11 times.
|
13 | if (len_parsed == 0) |
407 | { | ||
408 | /* no match with current option */ | ||
409 | 2 | continue; | |
410 | } | ||
411 | |||
412 | /* argument matches current option */ | ||
413 | 11 | ++ret; /* one argument holds key */ | |
414 | |||
415 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
|
11 | if ((current_opt->option_type != GATE_APP_OPTION_TYPE_SWITCH)) |
416 | { | ||
417 | /* we need to parse option value data */ | ||
418 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2 times.
|
10 | if (is_key_option(current_opt)) |
419 | { | ||
420 | /* it is a key based option, the value follows the key */ | ||
421 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
|
8 | if (len_parsed != gate_string_length(current_arg)) |
422 | { | ||
423 | /* the value is inside the key argument, eg. "--opt=value" */ | ||
424 | ✗ | gate_string_substr(&opt_value, current_arg, len_parsed, GATE_STR_NPOS); | |
425 | } | ||
426 | else | ||
427 | { | ||
428 | /* the following argument holds the value, eg. "--opt" "value" */ | ||
429 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | if (input_arg_count > 0) |
430 | { | ||
431 | 8 | gate_string_duplicate(&opt_value, input_args); | |
432 | 8 | ++input_args; | |
433 | 8 | --input_arg_count; | |
434 | 8 | ++ret; | |
435 | } | ||
436 | } | ||
437 | } | ||
438 | else | ||
439 | { | ||
440 | /* it is an unnamed option, the argument is the content */ | ||
441 | 2 | gate_string_duplicate(&opt_value, current_arg); | |
442 | } | ||
443 | } | ||
444 | |||
445 | 11 | remove_opt = true; | |
446 |
7/8✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
|
11 | switch (current_opt->option_type) |
447 | { | ||
448 | 1 | case GATE_APP_OPTION_TYPE_SWITCH: | |
449 | { | ||
450 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (current_opt->option_value) |
451 | { | ||
452 | 1 | *((gate_bool_t*)current_opt->option_value) = true; | |
453 | } | ||
454 | 1 | break; | |
455 | } | ||
456 | 1 | case GATE_APP_OPTION_TYPE_BOOL: | |
457 | { | ||
458 | 1 | gate_app_option_parse_bool(&opt_value, current_opt->option_value); | |
459 | 1 | break; | |
460 | } | ||
461 | 2 | case GATE_APP_OPTION_TYPE_I32: | |
462 | { | ||
463 | 2 | gate_app_option_parse_i32(&opt_value, current_opt->option_value); | |
464 | 2 | break; | |
465 | } | ||
466 | 1 | case GATE_APP_OPTION_TYPE_I64: | |
467 | { | ||
468 | 1 | gate_app_option_parse_i64(&opt_value, current_opt->option_value); | |
469 | 1 | break; | |
470 | } | ||
471 | 1 | case GATE_APP_OPTION_TYPE_REAL: | |
472 | { | ||
473 | 1 | gate_app_option_parse_real(&opt_value, current_opt->option_value); | |
474 | 1 | break; | |
475 | } | ||
476 | 2 | case GATE_APP_OPTION_TYPE_STRING: | |
477 | { | ||
478 | 2 | gate_app_option_parse_string(&opt_value, current_opt->option_value); | |
479 | 2 | break; | |
480 | } | ||
481 | 3 | case GATE_APP_OPTION_TYPE_ARRAY: | |
482 | { | ||
483 | 3 | gate_app_option_parse_stringarray(&opt_value, current_opt->option_value); | |
484 | 3 | remove_opt = false; /* can occur again */ | |
485 | 3 | break; | |
486 | } | ||
487 | } | ||
488 | 11 | gate_string_release(&opt_value); | |
489 | |||
490 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 3 times.
|
11 | if (remove_opt) |
491 | { | ||
492 | /* option's value was filled, remove it from list */ | ||
493 | 8 | opts[index] = NULL; | |
494 | } | ||
495 | /* the option was resolved, we can continue with the next argument */ | ||
496 | 11 | break; | |
497 | } /* for(each option) */ | ||
498 | } /* (input_arg_count > 0) */ | ||
499 | 1 | return ret; | |
500 | } | ||
501 | |||
502 | ✗ | gate_size_t gate_app_options_parse_strs(char const* const* input_args, gate_size_t input_arg_count, | |
503 | gate_app_option_t* output_options, gate_size_t output_option_count) | ||
504 | { | ||
505 | gate_string_t args[256]; | ||
506 | gate_size_t ndx; | ||
507 | ✗ | for (ndx = 0; ndx != input_arg_count; ++ndx) | |
508 | { | ||
509 | ✗ | gate_string_create_static(&args[ndx], input_args[ndx]); | |
510 | } | ||
511 | ✗ | return gate_app_options_parse(args, input_arg_count, output_options, output_option_count); | |
512 | } | ||
513 | |||
514 | 9 | static gate_result_t gate_app_option_print(gate_app_option_t const* option, gate_stream_t* out_stream) | |
515 | { | ||
516 | 9 | gate_bool_t print_opt_name = option->option_type != GATE_APP_OPTION_TYPE_SWITCH; | |
517 | |||
518 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 2 times.
|
9 | if (!gate_string_is_empty(&option->option_key)) |
519 | { | ||
520 | 7 | gate_stream_print(out_stream, | |
521 | GATE_PRINT_CSTR, "--", | ||
522 | GATE_PRINT_STRING, &option->option_key, | ||
523 | GATE_PRINT_END); | ||
524 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | if (!gate_string_is_empty(&option->option_alias)) |
525 | { | ||
526 | 7 | gate_stream_print(out_stream, | |
527 | GATE_PRINT_CSTR, ",-", | ||
528 | GATE_PRINT_STRING, &option->option_alias, | ||
529 | GATE_PRINT_END); | ||
530 | } | ||
531 | 7 | gate_stream_print(out_stream, | |
532 | GATE_PRINT_CSTR, " ", | ||
533 | GATE_PRINT_END); | ||
534 | } | ||
535 | |||
536 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
|
9 | if (print_opt_name) |
537 | { | ||
538 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | if (gate_string_is_empty(&option->option_name)) |
539 | { | ||
540 | 8 | gate_stream_print(out_stream, | |
541 | GATE_PRINT_CSTR, "[", | ||
542 | GATE_PRINT_STRING, &option->option_key, | ||
543 | GATE_PRINT_CSTR, "_option_value]", | ||
544 | GATE_PRINT_END); | ||
545 | } | ||
546 | else | ||
547 | { | ||
548 | ✗ | gate_stream_print(out_stream, | |
549 | GATE_PRINT_CSTR, "[", | ||
550 | GATE_PRINT_STRING, &option->option_name, | ||
551 | GATE_PRINT_CSTR, "]", | ||
552 | GATE_PRINT_END); | ||
553 | } | ||
554 | } | ||
555 | |||
556 | 9 | gate_stream_println_cstr(out_stream, NULL); | |
557 | |||
558 |
7/8✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
9 | switch (option->option_type) |
559 | { | ||
560 | 1 | case GATE_APP_OPTION_TYPE_SWITCH: | |
561 | 1 | gate_stream_print_cstr(out_stream, "\tswitch"); | |
562 | 1 | break; | |
563 | 1 | case GATE_APP_OPTION_TYPE_BOOL: | |
564 | 1 | gate_stream_print_cstr(out_stream, "\tboolean"); | |
565 | 1 | break; | |
566 | 2 | case GATE_APP_OPTION_TYPE_I32: | |
567 | 2 | gate_stream_print_cstr(out_stream, "\tint32"); | |
568 | 2 | break; | |
569 | 1 | case GATE_APP_OPTION_TYPE_I64: | |
570 | 1 | gate_stream_print_cstr(out_stream, "\tint64"); | |
571 | 1 | break; | |
572 | 1 | case GATE_APP_OPTION_TYPE_REAL: | |
573 | 1 | gate_stream_print_cstr(out_stream, "\tfloat"); | |
574 | 1 | break; | |
575 | 2 | case GATE_APP_OPTION_TYPE_STRING: | |
576 | 2 | gate_stream_print_cstr(out_stream, "\ttext"); | |
577 | 2 | break; | |
578 | 1 | case GATE_APP_OPTION_TYPE_ARRAY: | |
579 | 1 | gate_stream_print_cstr(out_stream, "\ttext-array"); | |
580 | 1 | break; | |
581 | ✗ | default: | |
582 | ✗ | gate_stream_print_cstr(out_stream, "\tunknown"); | |
583 | ✗ | break; | |
584 | } | ||
585 | |||
586 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if (!gate_string_is_empty(&option->option_description)) |
587 | { | ||
588 | ✗ | gate_stream_print(out_stream, | |
589 | GATE_PRINT_CSTR, ": ", | ||
590 | GATE_PRINT_STRING, &option->option_description, | ||
591 | GATE_PRINT_END); | ||
592 | } | ||
593 | 9 | gate_stream_print(out_stream, | |
594 | GATE_PRINT_NEWLINE, | ||
595 | GATE_PRINT_END); | ||
596 | 9 | return GATE_RESULT_OK; | |
597 | } | ||
598 | |||
599 | 1 | gate_result_t gate_app_options_print(gate_app_option_t const* options, gate_size_t options_count, gate_stream_t* out_stream) | |
600 | { | ||
601 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
|
10 | while (options_count-- > 0) |
602 | { | ||
603 | 9 | gate_app_option_print(options, out_stream); | |
604 | 9 | ++options; | |
605 | } | ||
606 | 1 | return GATE_RESULT_OK; | |
607 | } | ||
608 | |||
609 | 41 | void gate_app_option_init(gate_app_option_t* opt, gate_string_t const* key, gate_enumint_t type, void* ptr_value, | |
610 | gate_string_t const* alias, gate_string_t const* name_value, gate_string_t const* description) | ||
611 | { | ||
612 | 41 | gate_mem_clear(opt, sizeof(gate_app_option_t)); | |
613 | 41 | gate_string_duplicate(&opt->option_key, key); | |
614 | 41 | opt->option_type = type; | |
615 | 41 | opt->option_value = ptr_value; | |
616 | 41 | gate_string_duplicate(&opt->option_alias, alias); | |
617 | 41 | gate_string_duplicate(&opt->option_name, name_value); | |
618 | 41 | gate_string_duplicate(&opt->option_description, description); | |
619 | 41 | } | |
620 | |||
621 | ✗ | void gate_app_option_copy(gate_app_option_t* opt_dst, gate_app_option_t const* opt_src) | |
622 | { | ||
623 | ✗ | gate_mem_clear(opt_dst, sizeof(gate_app_option_t)); | |
624 | ✗ | gate_string_duplicate(&opt_dst->option_key, &opt_src->option_key); | |
625 | ✗ | opt_dst->option_type = opt_src->option_type; | |
626 | ✗ | opt_dst->option_value = opt_src->option_value; | |
627 | ✗ | gate_string_duplicate(&opt_dst->option_alias, &opt_src->option_alias); | |
628 | ✗ | gate_string_duplicate(&opt_dst->option_name, &opt_src->option_name); | |
629 | ✗ | gate_string_duplicate(&opt_dst->option_description, &opt_src->option_description); | |
630 | ✗ | } | |
631 | |||
632 | 41 | void gate_app_option_release(gate_app_option_t* opt) | |
633 | { | ||
634 | 41 | gate_string_release(&opt->option_key); | |
635 | 41 | gate_string_release(&opt->option_alias); | |
636 | 41 | gate_string_release(&opt->option_name); | |
637 | 41 | gate_string_release(&opt->option_description); | |
638 | 41 | gate_mem_clear(opt, sizeof(gate_app_option_t)); | |
639 | 41 | } | |
640 | |||
641 | ✗ | void gate_app_init(gate_app_t* app, | |
642 | gate_result_t(*init)(gate_app_t* app, char const* program, char const* const* arguments, gate_size_t argcount, gate_uintptr_t apphandle), | ||
643 | gate_result_t(*run)(gate_app_t* app), | ||
644 | gate_result_t(*on_signal)(gate_app_t* app, int appsignal)) | ||
645 | { | ||
646 | ✗ | app->init = init; | |
647 | ✗ | app->run = run; | |
648 | ✗ | app->on_signal = on_signal; | |
649 | ✗ | } | |
650 | |||
651 | ✗ | void gate_appservice_init(gate_appservice_t* service, | |
652 | gate_result_t(*init_cb)(gate_appservice_t*, char const*, char const* const*, gate_size_t, gate_uintptr_t), | ||
653 | gate_result_t(*run_cb)(gate_appservice_t*), | ||
654 | |||
655 | gate_result_t(*on_signal_cb)(gate_appservice_t*, int), | ||
656 | |||
657 | gate_size_t(*get_servicename_cb)(gate_appservice_t*, char*, gate_size_t), | ||
658 | |||
659 | gate_result_t(*on_start_cb)(gate_appservice_t*), | ||
660 | gate_result_t(*on_stop_cb)(gate_appservice_t*), | ||
661 | gate_result_t(*on_pause_cb)(gate_appservice_t*), | ||
662 | gate_result_t(*on_continue_cb)(gate_appservice_t*), | ||
663 | gate_result_t(*on_error_cb)(gate_appservice_t*, gate_result_t, gate_int32_t, char const*)) | ||
664 | { | ||
665 | ✗ | service->init = init_cb; | |
666 | ✗ | service->run = run_cb; | |
667 | |||
668 | ✗ | service->on_signal = on_signal_cb; | |
669 | |||
670 | ✗ | service->get_servicename = get_servicename_cb; | |
671 | |||
672 | ✗ | service->on_start = on_start_cb; | |
673 | ✗ | service->on_stop = on_stop_cb; | |
674 | ✗ | service->on_pause = on_pause_cb; | |
675 | ✗ | service->on_continue = on_continue_cb; | |
676 | ✗ | service->on_error = on_error_cb; | |
677 | ✗ | } | |
678 | |||
679 | #if defined(GATE_CORE_APPLICATIONS_WINAPI_IMPL) | ||
680 | |||
681 | #include "gate/platforms.h" | ||
682 | #include "gate/memalloc.h" | ||
683 | #include "gate/platform/windows/win32registry.h" | ||
684 | |||
685 | static gate_app_t* volatile gate_global_app_instance = NULL; | ||
686 | |||
687 | #if !defined(GATE_SYS_WINCE) && !defined(GATE_SYS_WIN16) | ||
688 | |||
689 | static BOOL WINAPI gate_app_handler_routine(DWORD dwCtrlType) | ||
690 | { | ||
691 | gate_app_t* app = gate_global_app_instance; | ||
692 | gate_result_t ret = GATE_RESULT_NOTSUPPORTED; | ||
693 | |||
694 | if (app != NULL) | ||
695 | { | ||
696 | switch (dwCtrlType) | ||
697 | { | ||
698 | case CTRL_BREAK_EVENT: | ||
699 | { | ||
700 | /* CTRL + BREAK */ | ||
701 | ret = app->on_signal(app, GATE_APP_SIGNAL_CANCEL); | ||
702 | break; | ||
703 | } | ||
704 | case CTRL_C_EVENT: | ||
705 | { | ||
706 | /* CTRL + C */ | ||
707 | ret = app->on_signal(app, GATE_APP_SIGNAL_CANCEL); | ||
708 | break; | ||
709 | } | ||
710 | case CTRL_CLOSE_EVENT: | ||
711 | { | ||
712 | /* a console/terminal was closed and all attached processes need to terminate */ | ||
713 | ret = app->on_signal(app, GATE_APP_SIGNAL_TERMINATE); | ||
714 | break; | ||
715 | } | ||
716 | case CTRL_LOGOFF_EVENT: | ||
717 | { | ||
718 | /* ignore user logoffs, because only services receive this signal */ | ||
719 | break; | ||
720 | } | ||
721 | case CTRL_SHUTDOWN_EVENT: | ||
722 | { | ||
723 | /* system shutdown */ | ||
724 | ret = app->on_signal(app, GATE_APP_SIGNAL_SHUTDOWN); | ||
725 | break; | ||
726 | } | ||
727 | } | ||
728 | } | ||
729 | return GATE_SUCCEEDED(ret) ? TRUE : FALSE; | ||
730 | } | ||
731 | |||
732 | #endif | ||
733 | |||
734 | #if defined(GATE_SYS_WINSTORE) | ||
735 | |||
736 | #include "gate/platform/winrt/winrtapp.h" | ||
737 | |||
738 | int gate_app_run(gate_app_t* app, char const* program, char const* const* arguments, gate_size_t argcount, gate_uintptr_t apphandle) | ||
739 | { | ||
740 | gate_result_t result; | ||
741 | gate_winrt_app_t wrt_app; | ||
742 | |||
743 | do | ||
744 | { | ||
745 | result = gate_winrt_app_init(&wrt_app); | ||
746 | GATE_BREAK_IF_FAILED(result); | ||
747 | |||
748 | do | ||
749 | { | ||
750 | result = gate_winrt_app_start(&wrt_app); | ||
751 | GATE_BREAK_IF_FAILED(result); | ||
752 | |||
753 | result = app->init(app, program, arguments, argcount, apphandle); | ||
754 | GATE_BREAK_IF_FAILED(result); | ||
755 | |||
756 | result = app->run(app); | ||
757 | GATE_BREAK_IF_FAILED(result); | ||
758 | |||
759 | gate_winrt_app_stop(&wrt_app); | ||
760 | |||
761 | } while (0); | ||
762 | gate_winrt_app_uninit(&wrt_app); | ||
763 | } while (0); | ||
764 | |||
765 | return GATE_RESULT_TO_EXITCODE(result); | ||
766 | } | ||
767 | |||
768 | #else /* GATE_SYS_WINSTORE */ | ||
769 | |||
770 | int gate_app_run(gate_app_t* app, char const* program, char const* const* arguments, gate_size_t argcount, gate_uintptr_t apphandle) | ||
771 | { | ||
772 | gate_result_t result; | ||
773 | #if !defined(GATE_SYS_WINCE) && !defined(GATE_SYS_WIN16) | ||
774 | BOOL ctrlhandler_added; | ||
775 | #endif | ||
776 | |||
777 | gate_app_t* oldapp = gate_global_app_instance; | ||
778 | |||
779 | gate_global_app_instance = app; | ||
780 | #if !defined(GATE_SYS_WINCE) && !defined(GATE_SYS_WIN16) | ||
781 | ctrlhandler_added = SetConsoleCtrlHandler(&gate_app_handler_routine, TRUE); | ||
782 | #endif | ||
783 | |||
784 | result = app->init(app, program, arguments, argcount, apphandle); | ||
785 | if (result == GATE_RESULT_OK) | ||
786 | { | ||
787 | result = app->run(app); | ||
788 | } | ||
789 | |||
790 | #if !defined(GATE_SYS_WINCE) && !defined(GATE_SYS_WIN16) | ||
791 | if (ctrlhandler_added) | ||
792 | { | ||
793 | SetConsoleCtrlHandler(&gate_app_handler_routine, FALSE); | ||
794 | } | ||
795 | #endif | ||
796 | |||
797 | gate_global_app_instance = oldapp; | ||
798 | |||
799 | if (result >= 0) | ||
800 | { | ||
801 | return (int)result; | ||
802 | } | ||
803 | else | ||
804 | { | ||
805 | return GATE_RESULT_TO_EXITCODE(result); | ||
806 | } | ||
807 | } | ||
808 | |||
809 | #endif | ||
810 | |||
811 | #if defined(GATE_SYS_WINCE) || defined(GATE_SYS_WINSTORE) || defined(GATE_SYS_WIN16) | ||
812 | |||
813 | // #include "gate/platform/winrt/winrtbase.h" | ||
814 | // #include "gate/platform/winrt/wrt_abi.h" | ||
815 | |||
816 | int gate_appservice_run(gate_appservice_t* appservice, char const* program, char const* const* arguments, gate_size_t argcount, gate_uintptr_t apphandle) | ||
817 | { | ||
818 | GATE_UNUSED_ARG(appservice); | ||
819 | GATE_UNUSED_ARG(program); | ||
820 | GATE_UNUSED_ARG(arguments); | ||
821 | GATE_UNUSED_ARG(argcount); | ||
822 | GATE_UNUSED_ARG(apphandle); | ||
823 | return GATE_RESULT_TO_EXITCODE(GATE_RESULT_NOTSUPPORTED); | ||
824 | } | ||
825 | |||
826 | #else /* !GATE_SYS_WINCE */ | ||
827 | |||
828 | static TCHAR gate_win32_global_winsvcname[256]; | ||
829 | static gate_appservice_t* gate_win32_global_appservice = NULL; | ||
830 | static SERVICE_TABLE_ENTRY gate_win32_global_dispatchtable[2]; | ||
831 | static SERVICE_STATUS gate_win32_global_service_status = GATE_INIT_EMPTY; | ||
832 | static SERVICE_STATUS_HANDLE gate_win32_global_service_handle = NULL; | ||
833 | static HANDLE gate_win32_global_service_run_completed = NULL; | ||
834 | static DWORD gate_win32_global_service_stop_timeout_ms = 60000; | ||
835 | |||
836 | static SERVICE_STATUS_HANDLE(WINAPI* AdvRegisterServiceCtrlHandler)(LPCTSTR lpServiceName, LPHANDLER_FUNCTION lpHandlerProc); | ||
837 | static BOOL(WINAPI* AdvSetServiceStatus)(SERVICE_STATUS_HANDLE hServiceStatus, LPSERVICE_STATUS lpServiceStatus); | ||
838 | static BOOL(WINAPI* AdvStartServiceCtrlDispatcher)(SERVICE_TABLE_ENTRY const* lpServiceStartTable); | ||
839 | |||
840 | static void load_adv_service_functions() | ||
841 | { | ||
842 | static HMODULE global_advapi = NULL; | ||
843 | |||
844 | if (NULL == global_advapi) | ||
845 | { | ||
846 | HMODULE hadvapi = gate_win32_get_advapi_module(); | ||
847 | if (NULL != hadvapi) | ||
848 | { | ||
849 | gate_win32_get_proc_address(hadvapi, GATE_WIN32_DUAL_NAME("RegisterServiceCtrlHandler"), &AdvRegisterServiceCtrlHandler); | ||
850 | gate_win32_get_proc_address(hadvapi, "SetServiceStatus", &AdvSetServiceStatus); | ||
851 | gate_win32_get_proc_address(hadvapi, GATE_WIN32_DUAL_NAME("StartServiceCtrlDispatcher"), &AdvStartServiceCtrlDispatcher); | ||
852 | global_advapi = hadvapi; | ||
853 | } | ||
854 | } | ||
855 | } | ||
856 | |||
857 | static void gate_win32_update_service_state(gate_appservice_t* service, SERVICE_STATUS_HANDLE hServiceStatus, LPSERVICE_STATUS lpServiceStatus) | ||
858 | { | ||
859 | if (AdvSetServiceStatus != NULL) | ||
860 | { | ||
861 | if (!AdvSetServiceStatus(hServiceStatus, lpServiceStatus)) | ||
862 | { | ||
863 | DWORD dwError = gate_win32_getlasterror(); | ||
864 | service->on_error(service, GATE_RESULT_FAILED, dwError, "SetServiceStatus() failed"); | ||
865 | } | ||
866 | } | ||
867 | } | ||
868 | |||
869 | #ifndef SERVICE_CONTROL_STOP | ||
870 | #define SERVICE_CONTROL_STOP 0x00000001 | ||
871 | #endif | ||
872 | #ifndef SERVICE_CONTROL_PAUSE | ||
873 | #define SERVICE_CONTROL_PAUSE 0x00000002 | ||
874 | #endif | ||
875 | #ifndef SERVICE_CONTROL_CONTINUE | ||
876 | #define SERVICE_CONTROL_CONTINUE 0x00000003 | ||
877 | #endif | ||
878 | #ifndef SERVICE_CONTROL_INTERROGATE | ||
879 | #define SERVICE_CONTROL_INTERROGATE 0x00000004 | ||
880 | #endif | ||
881 | #ifndef SERVICE_CONTROL_SHUTDOWN | ||
882 | #define SERVICE_CONTROL_SHUTDOWN 0x00000005 | ||
883 | #endif | ||
884 | #ifndef SERVICE_CONTROL_PARAMCHANGE | ||
885 | #define SERVICE_CONTROL_PARAMCHANGE 0x00000006 | ||
886 | #endif | ||
887 | |||
888 | #ifndef SERVICE_STOPPED | ||
889 | #define SERVICE_STOPPED 0x00000001 | ||
890 | #endif | ||
891 | #ifndef SERVICE_START_PENDING | ||
892 | #define SERVICE_START_PENDING 0x00000002 | ||
893 | #endif | ||
894 | #ifndef SERVICE_STOP_PENDING | ||
895 | #define SERVICE_STOP_PENDING 0x00000003 | ||
896 | #endif | ||
897 | #ifndef SERVICE_RUNNING | ||
898 | #define SERVICE_RUNNING 0x00000004 | ||
899 | #endif | ||
900 | #ifndef SERVICE_CONTINUE_PENDING | ||
901 | #define SERVICE_CONTINUE_PENDING 0x00000005 | ||
902 | #endif | ||
903 | #ifndef SERVICE_PAUSE_PENDING | ||
904 | #define SERVICE_PAUSE_PENDING 0x00000006 | ||
905 | #endif | ||
906 | #ifndef SERVICE_PAUSED | ||
907 | #define SERVICE_PAUSED 0x00000007 | ||
908 | #endif | ||
909 | |||
910 | #ifndef SERVICE_ACCEPT_STOP | ||
911 | #define SERVICE_ACCEPT_STOP 0x00000001 | ||
912 | #endif | ||
913 | #ifndef SERVICE_ACCEPT_SHUTDOWN | ||
914 | #define SERVICE_ACCEPT_SHUTDOWN 0x00000004 | ||
915 | #endif | ||
916 | |||
917 | static VOID WINAPI gate_win32_service_ctrl_handler(gate_appservice_t* service, LPSERVICE_STATUS svcstatus, SERVICE_STATUS_HANDLE svcstatushandle, DWORD opcode) | ||
918 | { | ||
919 | gate_result_t result = GATE_RESULT_OK; | ||
920 | |||
921 | switch (opcode) | ||
922 | { | ||
923 | case SERVICE_CONTROL_PAUSE: | ||
924 | { | ||
925 | if (service->on_pause) | ||
926 | { | ||
927 | svcstatus->dwCurrentState = SERVICE_PAUSE_PENDING; | ||
928 | svcstatus->dwWin32ExitCode = NO_ERROR; | ||
929 | svcstatus->dwServiceSpecificExitCode = 0; | ||
930 | gate_win32_update_service_state(service, svcstatushandle, svcstatus); | ||
931 | |||
932 | result = service->on_pause(service); | ||
933 | |||
934 | if (GATE_SUCCEEDED(result)) | ||
935 | { | ||
936 | svcstatus->dwCurrentState = SERVICE_PAUSED; | ||
937 | } | ||
938 | } | ||
939 | else | ||
940 | { | ||
941 | result = GATE_RESULT_NOTSUPPORTED; | ||
942 | } | ||
943 | break; | ||
944 | } | ||
945 | case SERVICE_CONTROL_CONTINUE: | ||
946 | { | ||
947 | if (service->on_continue) | ||
948 | { | ||
949 | svcstatus->dwCurrentState = SERVICE_CONTINUE_PENDING; | ||
950 | svcstatus->dwWin32ExitCode = NO_ERROR; | ||
951 | svcstatus->dwServiceSpecificExitCode = 0; | ||
952 | gate_win32_update_service_state(service, svcstatushandle, svcstatus); | ||
953 | |||
954 | result = service->on_continue(service); | ||
955 | |||
956 | if (GATE_SUCCEEDED(result)) | ||
957 | { | ||
958 | svcstatus->dwCurrentState = SERVICE_RUNNING; | ||
959 | } | ||
960 | } | ||
961 | else | ||
962 | { | ||
963 | result = GATE_RESULT_NOTSUPPORTED; | ||
964 | } | ||
965 | break; | ||
966 | } | ||
967 | case SERVICE_CONTROL_STOP: | ||
968 | { | ||
969 | svcstatus->dwCurrentState = SERVICE_STOP_PENDING; | ||
970 | gate_win32_update_service_state(service, svcstatushandle, svcstatus); | ||
971 | |||
972 | if (service->on_stop) | ||
973 | { | ||
974 | result = service->on_stop(service); | ||
975 | } | ||
976 | |||
977 | if (GATE_SUCCEEDED(result)) | ||
978 | { | ||
979 | if (NULL != gate_win32_global_service_run_completed) | ||
980 | { | ||
981 | if (WAIT_OBJECT_0 != WaitForSingleObject(gate_win32_global_service_run_completed, gate_win32_global_service_stop_timeout_ms)) | ||
982 | { | ||
983 | service->on_error(service, GATE_RESULT_TIMEOUT, 0, "Service did not exit run() method within shutdown timeout"); | ||
984 | } | ||
985 | } | ||
986 | svcstatus->dwCurrentState = SERVICE_STOPPED; | ||
987 | } | ||
988 | break; | ||
989 | } | ||
990 | case SERVICE_CONTROL_SHUTDOWN: | ||
991 | { | ||
992 | if (service->on_signal) | ||
993 | { | ||
994 | /*result = */ service->on_signal(service, GATE_APP_SIGNAL_SHUTDOWN); | ||
995 | } | ||
996 | break; | ||
997 | } | ||
998 | case SERVICE_CONTROL_INTERROGATE: | ||
999 | { | ||
1000 | break; | ||
1001 | } | ||
1002 | default: | ||
1003 | { | ||
1004 | break; | ||
1005 | } | ||
1006 | } | ||
1007 | |||
1008 | if (GATE_SUCCEEDED(result)) | ||
1009 | { | ||
1010 | svcstatus->dwWin32ExitCode = NO_ERROR; | ||
1011 | svcstatus->dwServiceSpecificExitCode = 0; | ||
1012 | } | ||
1013 | else | ||
1014 | { | ||
1015 | svcstatus->dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; | ||
1016 | svcstatus->dwServiceSpecificExitCode = (DWORD)result; | ||
1017 | } | ||
1018 | |||
1019 | gate_win32_update_service_state(service, svcstatushandle, svcstatus); | ||
1020 | } | ||
1021 | |||
1022 | static VOID WINAPI gate_win32_service_ctrl_handler_dispatcher(DWORD opcode) | ||
1023 | { | ||
1024 | if (gate_win32_global_appservice != NULL) | ||
1025 | { | ||
1026 | gate_win32_service_ctrl_handler( | ||
1027 | gate_win32_global_appservice, | ||
1028 | &gate_win32_global_service_status, | ||
1029 | gate_win32_global_service_handle, | ||
1030 | opcode); | ||
1031 | } | ||
1032 | } | ||
1033 | |||
1034 | #if defined(GATE_WIN32_ANSI) | ||
1035 | |||
1036 | /* Win9x service registration point */ | ||
1037 | static DWORD(WINAPI* Win32RegisterServiceProcess)(DWORD dwProcessId, DWORD dwType) = NULL; | ||
1038 | static HANDLE win9x_service_stopped_event = NULL; | ||
1039 | |||
1040 | static BOOL WINAPI win9x_service_handler_routine(DWORD dwCtrlType) | ||
1041 | { | ||
1042 | gate_appservice_t* service = gate_win32_global_appservice; | ||
1043 | if (service) | ||
1044 | { | ||
1045 | switch (dwCtrlType) | ||
1046 | { | ||
1047 | case CTRL_CLOSE_EVENT: | ||
1048 | case CTRL_SHUTDOWN_EVENT: | ||
1049 | case CTRL_C_EVENT: | ||
1050 | case CTRL_BREAK_EVENT: | ||
1051 | service->on_stop(service); | ||
1052 | if (win9x_service_stopped_event) | ||
1053 | { | ||
1054 | SetEvent(win9x_service_stopped_event); | ||
1055 | } | ||
1056 | return TRUE; | ||
1057 | default: | ||
1058 | break; | ||
1059 | } | ||
1060 | } | ||
1061 | return FALSE; | ||
1062 | } | ||
1063 | |||
1064 | /* simplified service-main for Win9X */ | ||
1065 | static VOID WINAPI gate_win32_service_main_9x() | ||
1066 | { | ||
1067 | gate_appservice_t* service = gate_win32_global_appservice; | ||
1068 | BOOL service_handler_added = FALSE; | ||
1069 | gate_result_t result; | ||
1070 | |||
1071 | if (service == NULL) | ||
1072 | { | ||
1073 | return; | ||
1074 | } | ||
1075 | |||
1076 | if (service->on_start != NULL) | ||
1077 | { | ||
1078 | result = service->on_start(service); | ||
1079 | } | ||
1080 | else | ||
1081 | { | ||
1082 | result = GATE_RESULT_OK; | ||
1083 | } | ||
1084 | |||
1085 | if (GATE_FAILED(result)) | ||
1086 | { | ||
1087 | return; | ||
1088 | } | ||
1089 | |||
1090 | win9x_service_stopped_event = CreateEvent(NULL, TRUE, FALSE, NULL); | ||
1091 | service_handler_added = SetConsoleCtrlHandler(&win9x_service_handler_routine, TRUE); | ||
1092 | |||
1093 | if (service->run != NULL) | ||
1094 | { | ||
1095 | result = service->run(service); | ||
1096 | if (GATE_FAILED(result)) | ||
1097 | { | ||
1098 | if (service->on_error != NULL) | ||
1099 | { | ||
1100 | service->on_error(service, result, 0, "service->run() failed"); | ||
1101 | } | ||
1102 | } | ||
1103 | } | ||
1104 | |||
1105 | if (gate_win32_global_service_run_completed) | ||
1106 | { | ||
1107 | SetEvent(gate_win32_global_service_run_completed); | ||
1108 | } | ||
1109 | |||
1110 | if (win9x_service_stopped_event) | ||
1111 | { | ||
1112 | /* wait until service was stopped */ | ||
1113 | WaitForSingleObject(win9x_service_stopped_event, INFINITE); | ||
1114 | } | ||
1115 | if (service_handler_added) | ||
1116 | { | ||
1117 | SetConsoleCtrlHandler(&win9x_service_handler_routine, FALSE); | ||
1118 | } | ||
1119 | if (win9x_service_stopped_event) | ||
1120 | { | ||
1121 | CloseHandle(win9x_service_stopped_event); | ||
1122 | } | ||
1123 | } | ||
1124 | |||
1125 | #endif /* GATE_WIN32_ANSI */ | ||
1126 | |||
1127 | static VOID WINAPI gate_win32_service_main(DWORD dwArgc, LPTSTR* lpszArgv) | ||
1128 | { | ||
1129 | BOOL success; | ||
1130 | DWORD dwError; | ||
1131 | DWORD dwExitCode = 0; | ||
1132 | |||
1133 | LPSERVICE_STATUS svcstatus = &gate_win32_global_service_status; | ||
1134 | SERVICE_STATUS_HANDLE* svchandle = &gate_win32_global_service_handle; | ||
1135 | gate_appservice_t* service = gate_win32_global_appservice; | ||
1136 | |||
1137 | gate_result_t result; | ||
1138 | |||
1139 | GATE_UNUSED_ARG(dwArgc); | ||
1140 | GATE_UNUSED_ARG(lpszArgv); | ||
1141 | |||
1142 | gate_mem_clear(svcstatus, sizeof(SERVICE_STATUS)); | ||
1143 | |||
1144 | if (service == NULL) | ||
1145 | { | ||
1146 | return; | ||
1147 | } | ||
1148 | |||
1149 | svcstatus->dwServiceType = SERVICE_WIN32; | ||
1150 | svcstatus->dwCurrentState = SERVICE_START_PENDING; | ||
1151 | svcstatus->dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; | ||
1152 | |||
1153 | if (AdvRegisterServiceCtrlHandler != NULL) | ||
1154 | { | ||
1155 | *svchandle = AdvRegisterServiceCtrlHandler(gate_win32_global_winsvcname, &gate_win32_service_ctrl_handler_dispatcher); | ||
1156 | } | ||
1157 | |||
1158 | if (*svchandle == 0) | ||
1159 | { | ||
1160 | dwError = gate_win32_getlasterror(); | ||
1161 | if (service->on_error != NULL) | ||
1162 | { | ||
1163 | service->on_error(service, GATE_RESULT_FAILED, dwError, "RegisterServiceCtrlHandler() failed"); | ||
1164 | } | ||
1165 | return; | ||
1166 | } | ||
1167 | |||
1168 | success = FALSE; | ||
1169 | if (AdvSetServiceStatus != NULL) | ||
1170 | { | ||
1171 | success = AdvSetServiceStatus(*svchandle, svcstatus); | ||
1172 | } | ||
1173 | else | ||
1174 | { | ||
1175 | gate_win32_setlasterror(ERROR_NOT_SUPPORTED); | ||
1176 | } | ||
1177 | |||
1178 | if (!success) | ||
1179 | { | ||
1180 | dwError = gate_win32_getlasterror(); | ||
1181 | if (service->on_error != NULL) | ||
1182 | { | ||
1183 | service->on_error(service, GATE_RESULT_FAILED, dwError, "SetServiceStatus() failed"); | ||
1184 | } | ||
1185 | dwExitCode = dwError; | ||
1186 | return; | ||
1187 | } | ||
1188 | |||
1189 | if (service->on_start != NULL) | ||
1190 | { | ||
1191 | result = service->on_start(service); | ||
1192 | } | ||
1193 | else | ||
1194 | { | ||
1195 | result = GATE_RESULT_OK; | ||
1196 | } | ||
1197 | |||
1198 | if (GATE_FAILED(result)) | ||
1199 | { | ||
1200 | svcstatus->dwCurrentState = SERVICE_STOPPED; | ||
1201 | svcstatus->dwWin32ExitCode = (dwExitCode == 0) ? 0 : ERROR_SERVICE_SPECIFIC_ERROR; | ||
1202 | svcstatus->dwServiceSpecificExitCode = dwExitCode; | ||
1203 | if (AdvSetServiceStatus != NULL) | ||
1204 | { | ||
1205 | AdvSetServiceStatus(*svchandle, svcstatus); | ||
1206 | } | ||
1207 | return; | ||
1208 | } | ||
1209 | |||
1210 | svcstatus->dwCurrentState = SERVICE_RUNNING; | ||
1211 | svcstatus->dwWin32ExitCode = 0; | ||
1212 | svcstatus->dwServiceSpecificExitCode = 0; | ||
1213 | |||
1214 | success = FALSE; | ||
1215 | if (AdvSetServiceStatus != NULL) | ||
1216 | { | ||
1217 | success = AdvSetServiceStatus(*svchandle, svcstatus); | ||
1218 | } | ||
1219 | else | ||
1220 | { | ||
1221 | gate_win32_setlasterror(ERROR_NOT_SUPPORTED); | ||
1222 | } | ||
1223 | |||
1224 | if (!success) | ||
1225 | { | ||
1226 | dwError = gate_win32_getlasterror(); | ||
1227 | if (service->on_error != NULL) | ||
1228 | { | ||
1229 | service->on_error(service, GATE_RESULT_FAILED, dwError, "SetServiceStatus() failed"); | ||
1230 | } | ||
1231 | return; | ||
1232 | } | ||
1233 | |||
1234 | if (service->run != NULL) | ||
1235 | { | ||
1236 | result = service->run(service); | ||
1237 | if (GATE_FAILED(result)) | ||
1238 | { | ||
1239 | if (service->on_error != NULL) | ||
1240 | { | ||
1241 | service->on_error(service, result, 0, "service->run() failed"); | ||
1242 | } | ||
1243 | } | ||
1244 | } | ||
1245 | |||
1246 | if (gate_win32_global_service_run_completed) | ||
1247 | { | ||
1248 | SetEvent(gate_win32_global_service_run_completed); | ||
1249 | } | ||
1250 | } | ||
1251 | |||
1252 | int gate_appservice_run(gate_appservice_t* appservice, char const* program, char const* const* arguments, gate_size_t argcount, gate_uintptr_t apphandle) | ||
1253 | { | ||
1254 | char svcname[256]; | ||
1255 | gate_size_t svcnamelength = appservice->get_servicename(appservice, svcname, sizeof(svcname)); | ||
1256 | gate_result_t result; | ||
1257 | BOOL success; | ||
1258 | |||
1259 | load_adv_service_functions(); | ||
1260 | |||
1261 | gate_win32_utf8_2_winstr(svcname, svcnamelength, gate_win32_global_winsvcname, sizeof(gate_win32_global_winsvcname) / sizeof(gate_win32_global_winsvcname[0])); | ||
1262 | |||
1263 | if (appservice->init) | ||
1264 | { | ||
1265 | result = appservice->init(appservice, program, arguments, argcount, apphandle); | ||
1266 | if (GATE_FAILED(result)) | ||
1267 | { | ||
1268 | if (GATE_RESULT_CANCELED == result) | ||
1269 | { | ||
1270 | return 0; | ||
1271 | } | ||
1272 | return GATE_RESULT_TO_EXITCODE(result); | ||
1273 | } | ||
1274 | } | ||
1275 | |||
1276 | gate_win32_global_service_run_completed = CreateEvent(NULL, TRUE, FALSE, NULL); | ||
1277 | if (NULL == gate_win32_global_service_run_completed) | ||
1278 | { | ||
1279 | return GATE_RESULT_TO_EXITCODE(GATE_RESULT_OUTOFRESOURCES); | ||
1280 | } | ||
1281 | |||
1282 | gate_win32_global_appservice = appservice; | ||
1283 | success = FALSE; | ||
1284 | |||
1285 | do | ||
1286 | { | ||
1287 | #if defined(GATE_WIN32_ANSI) | ||
1288 | if (!gate_win32_is_winnt4_or_newer()) | ||
1289 | { | ||
1290 | if (!Win32RegisterServiceProcess) | ||
1291 | { | ||
1292 | gate_win32_get_proc_address(gate_win32_get_kernel_module(), "RegisterServiceProcess", &Win32RegisterServiceProcess); | ||
1293 | } | ||
1294 | |||
1295 | if (Win32RegisterServiceProcess) | ||
1296 | { | ||
1297 | success = Win32RegisterServiceProcess(GetCurrentProcessId(), 1); | ||
1298 | if (!success) | ||
1299 | { | ||
1300 | break; | ||
1301 | } | ||
1302 | gate_win32_service_main_9x(); | ||
1303 | Win32RegisterServiceProcess(GetCurrentProcessId(), 0); | ||
1304 | success = TRUE; | ||
1305 | break; | ||
1306 | } | ||
1307 | } | ||
1308 | #endif | ||
1309 | if (AdvStartServiceCtrlDispatcher != NULL) | ||
1310 | { | ||
1311 | /* WinNT service */ | ||
1312 | gate_win32_global_dispatchtable[0].lpServiceName = gate_win32_global_winsvcname; | ||
1313 | gate_win32_global_dispatchtable[0].lpServiceProc = &gate_win32_service_main; | ||
1314 | gate_win32_global_dispatchtable[1].lpServiceName = NULL; | ||
1315 | gate_win32_global_dispatchtable[1].lpServiceProc = NULL; | ||
1316 | success = AdvStartServiceCtrlDispatcher(gate_win32_global_dispatchtable); | ||
1317 | break; | ||
1318 | } | ||
1319 | |||
1320 | gate_win32_setlasterror(ERROR_NOT_SUPPORTED); | ||
1321 | |||
1322 | } while (0); | ||
1323 | |||
1324 | gate_win32_global_appservice = NULL; | ||
1325 | |||
1326 | CloseHandle(gate_win32_global_service_run_completed); | ||
1327 | gate_win32_global_service_run_completed = NULL; | ||
1328 | |||
1329 | if (success) | ||
1330 | { | ||
1331 | return 0; | ||
1332 | } | ||
1333 | else | ||
1334 | { | ||
1335 | DWORD lasterror = gate_win32_getlasterror(); | ||
1336 | if (appservice->on_error != NULL) | ||
1337 | { | ||
1338 | appservice->on_error(appservice, GATE_RESULT_CANCELED, lasterror, "StartServiceCtrlDispatcher() failed"); | ||
1339 | } | ||
1340 | return GATE_RESULT_TO_EXITCODE(GATE_RESULT_CANCELED); | ||
1341 | } | ||
1342 | } | ||
1343 | |||
1344 | #endif /* !GATE_SYS_WINCE */ | ||
1345 | |||
1346 | #endif /* GATE_CORE_APPLICATIONS_WINAPI_IMPL */ | ||
1347 | |||
1348 | #if defined(GATE_CORE_APPLCIATIONS_POSIX_IMPL) | ||
1349 | |||
1350 | #include "gate/platforms.h" | ||
1351 | #include "gate/processes.h" | ||
1352 | #include "gate/synchronization.h" | ||
1353 | #include "gate/debugging.h" | ||
1354 | |||
1355 | #include <signal.h> | ||
1356 | |||
1357 | 1 | int gate_app_run(gate_app_t* app, char const* program, char const* const* arguments, gate_size_t argcount, gate_uintptr_t apphandle) | |
1358 | { | ||
1359 | gate_result_t result; | ||
1360 | 1 | result = app->init(app, program, arguments, argcount, apphandle); | |
1361 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (result == GATE_RESULT_OK) |
1362 | { | ||
1363 | 1 | result = app->run(app); | |
1364 | } | ||
1365 | |||
1366 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (result >= 0) |
1367 | { | ||
1368 | 1 | return result; | |
1369 | } | ||
1370 | else | ||
1371 | { | ||
1372 | ✗ | return GATE_RESULT_TO_EXITCODE(result); | |
1373 | } | ||
1374 | } | ||
1375 | |||
1376 | ✗ | static gate_size_t gate_appservice_build_pid_file(char* target, gate_size_t target_capacity, char const* appname) | |
1377 | { | ||
1378 | gate_strbuilder_t builder; | ||
1379 | ✗ | gate_strbuilder_create_static(&builder, target, target_capacity, 0); | |
1380 | ✗ | gate_strbuilder_append_cstr(&builder, "/var/run/"); | |
1381 | ✗ | gate_strbuilder_append_cstr(&builder, appname); | |
1382 | ✗ | gate_strbuilder_append_cstr(&builder, ".pid"); | |
1383 | ✗ | return gate_strbuilder_length(&builder); | |
1384 | } | ||
1385 | |||
1386 | ✗ | static gate_result_t gate_appservice_read_pid_file(char const* appname, pid_t* ptr_pid) | |
1387 | { | ||
1388 | ✗ | gate_result_t ret = GATE_RESULT_FAILED; | |
1389 | char pid_file[GATE_MAX_FILEPATH_LENGTH]; | ||
1390 | ✗ | gate_size_t len = 0; | |
1391 | gate_platform_stream_t strm; | ||
1392 | char pid_buffer[256]; | ||
1393 | ✗ | gate_size_t const pid_buffer_len = sizeof(pid_buffer); | |
1394 | gate_size_t tmp; | ||
1395 | ✗ | gate_size_t retrieved = 0; | |
1396 | ✗ | gate_int64_t pid_num = -1; | |
1397 | |||
1398 | do | ||
1399 | { | ||
1400 | ✗ | len = gate_appservice_build_pid_file(pid_file, sizeof(pid_file), appname); | |
1401 | ✗ | if (len == 0) | |
1402 | { | ||
1403 | ✗ | ret = GATE_RESULT_FAILED; | |
1404 | ✗ | break; | |
1405 | } | ||
1406 | ✗ | ret = gate_platform_stream_open(pid_file, GATE_PLATFORM_STREAM_OPEN_READ, 0, &strm); | |
1407 | ✗ | GATE_BREAK_IF_FAILED(ret); | |
1408 | |||
1409 | ✗ | while (retrieved < pid_buffer_len) | |
1410 | { | ||
1411 | ✗ | ret = gate_platform_stream_read(strm, &pid_buffer[retrieved], pid_buffer_len - retrieved, &tmp); | |
1412 | ✗ | GATE_BREAK_IF_FAILED(ret); | |
1413 | ✗ | if (tmp == 0) | |
1414 | { | ||
1415 | ✗ | break; | |
1416 | } | ||
1417 | ✗ | retrieved += tmp; | |
1418 | } | ||
1419 | ✗ | gate_platform_stream_close(&strm); | |
1420 | ✗ | GATE_BREAK_IF_FAILED(ret); | |
1421 | |||
1422 | ✗ | gate_str_parse_int64(pid_buffer, retrieved, &pid_num); | |
1423 | ✗ | if (ptr_pid) | |
1424 | { | ||
1425 | ✗ | *ptr_pid = (pid_t)pid_num; | |
1426 | } | ||
1427 | /* code */ | ||
1428 | } while (0); | ||
1429 | ✗ | return ret; | |
1430 | } | ||
1431 | |||
1432 | ✗ | static gate_result_t gate_appservice_write_pid_file(char const* appname, pid_t pid) | |
1433 | { | ||
1434 | ✗ | gate_result_t ret = GATE_RESULT_FAILED; | |
1435 | char pid_file[GATE_MAX_FILEPATH_LENGTH]; | ||
1436 | ✗ | gate_size_t len = 0; | |
1437 | gate_platform_stream_t strm; | ||
1438 | char pid_buffer[64]; | ||
1439 | gate_size_t used; | ||
1440 | gate_size_t tmp; | ||
1441 | ✗ | gate_size_t written = 0; | |
1442 | |||
1443 | do | ||
1444 | { | ||
1445 | ✗ | len = gate_appservice_build_pid_file(pid_file, sizeof(pid_file), appname); | |
1446 | ✗ | if (len == 0) | |
1447 | { | ||
1448 | ✗ | ret = GATE_RESULT_FAILED; | |
1449 | ✗ | break; | |
1450 | } | ||
1451 | ✗ | ret = gate_platform_stream_open(pid_file, GATE_PLATFORM_STREAM_OPEN_WRITE, 0, &strm); | |
1452 | ✗ | GATE_BREAK_IF_FAILED(ret); | |
1453 | ✗ | used = gate_str_print_int64(pid_buffer, sizeof(pid_buffer), (gate_int64_t)pid); | |
1454 | ✗ | while (written < used) | |
1455 | { | ||
1456 | ✗ | ret = gate_platform_stream_write(strm, &pid_buffer[written], used - written, &tmp); | |
1457 | ✗ | GATE_BREAK_IF_FAILED(ret); | |
1458 | ✗ | written += tmp; | |
1459 | } | ||
1460 | ✗ | gate_platform_stream_close(&strm); | |
1461 | /* code */ | ||
1462 | } while (0); | ||
1463 | ✗ | return ret; | |
1464 | } | ||
1465 | |||
1466 | ✗ | static gate_result_t gate_appservice_delete_pid_file(char const* appname) | |
1467 | { | ||
1468 | ✗ | gate_result_t ret = GATE_RESULT_FAILED; | |
1469 | char pid_file[GATE_MAX_FILEPATH_LENGTH]; | ||
1470 | gate_size_t len; | ||
1471 | |||
1472 | do | ||
1473 | { | ||
1474 | ✗ | len = gate_appservice_build_pid_file(pid_file, sizeof(pid_file), appname); | |
1475 | ✗ | if (len == 0) | |
1476 | { | ||
1477 | ✗ | ret = GATE_RESULT_FAILED; | |
1478 | ✗ | break; | |
1479 | } | ||
1480 | ✗ | if (0 == unlink(pid_file)) | |
1481 | { | ||
1482 | ✗ | ret = GATE_RESULT_OK; | |
1483 | } | ||
1484 | else | ||
1485 | { | ||
1486 | ✗ | ret = GATE_RESULT_EXECUTIONFAILED; | |
1487 | } | ||
1488 | } while (0); | ||
1489 | ✗ | return ret; | |
1490 | } | ||
1491 | |||
1492 | static gate_appservice_t* volatile registered_service_instance = NULL; | ||
1493 | static gate_syncevent_t registered_service_stopped; | ||
1494 | |||
1495 | ✗ | static gate_bool_t is_foreign_pid(pid_t pid) | |
1496 | { | ||
1497 | ✗ | pid_t self_pid = getpid(); | |
1498 | gate_process_infobuffer_t proc_info; | ||
1499 | gate_result_t result; | ||
1500 | |||
1501 | ✗ | if (pid == self_pid) | |
1502 | { | ||
1503 | ✗ | return false; | |
1504 | } | ||
1505 | ✗ | gate_mem_clear(&proc_info, sizeof(proc_info)); | |
1506 | ✗ | result = gate_process_getinfo(&proc_info, (gate_process_id_t)pid, GATE_PROCESS_ENUM_NAME); | |
1507 | ✗ | return (GATE_SUCCEEDED(result)); | |
1508 | } | ||
1509 | |||
1510 | ✗ | static void gate_appservice_signal_handler(int signum) | |
1511 | { | ||
1512 | ✗ | gate_appservice_t* service = registered_service_instance; | |
1513 | ✗ | if (service) | |
1514 | { | ||
1515 | ✗ | switch (signum) | |
1516 | { | ||
1517 | ✗ | case SIGSTOP: | |
1518 | case SIGTSTP: | ||
1519 | { | ||
1520 | ✗ | GATE_DEBUG_TRACE("appservice-signal PAUSE"); | |
1521 | ✗ | if (service->on_pause) | |
1522 | { | ||
1523 | ✗ | service->on_pause(service); | |
1524 | } | ||
1525 | ✗ | break; | |
1526 | } | ||
1527 | ✗ | case SIGCONT: | |
1528 | { | ||
1529 | ✗ | GATE_DEBUG_TRACE("appservice-signal CONTINUE"); | |
1530 | ✗ | if (service->on_continue) | |
1531 | { | ||
1532 | ✗ | service->on_continue(service); | |
1533 | } | ||
1534 | ✗ | break; | |
1535 | } | ||
1536 | ✗ | case SIGTERM: | |
1537 | { | ||
1538 | ✗ | GATE_DEBUG_TRACE("appservice-signal TERM"); | |
1539 | ✗ | if (service->on_stop) | |
1540 | { | ||
1541 | ✗ | service->on_stop(service); | |
1542 | } | ||
1543 | ✗ | gate_syncevent_set(®istered_service_stopped); | |
1544 | ✗ | break; | |
1545 | } | ||
1546 | ✗ | case SIGHUP: | |
1547 | { | ||
1548 | ✗ | GATE_DEBUG_TRACE("appservice-signal HUP"); | |
1549 | ✗ | if (service->on_signal) | |
1550 | { | ||
1551 | ✗ | service->on_signal(service, GATE_APP_SIGNAL_SHUTDOWN); | |
1552 | } | ||
1553 | ✗ | break; | |
1554 | } | ||
1555 | ✗ | case SIGQUIT: | |
1556 | { | ||
1557 | ✗ | GATE_DEBUG_TRACE("appservice-signal QUIT"); | |
1558 | ✗ | if (service->on_signal) | |
1559 | { | ||
1560 | ✗ | service->on_signal(service, GATE_APP_SIGNAL_TERMINATE); | |
1561 | } | ||
1562 | ✗ | break; | |
1563 | } | ||
1564 | ✗ | case SIGINT: | |
1565 | { | ||
1566 | ✗ | GATE_DEBUG_TRACE("appservice-signal INT"); | |
1567 | ✗ | if (service->on_signal) | |
1568 | { | ||
1569 | ✗ | service->on_signal(service, GATE_APP_SIGNAL_CANCEL); | |
1570 | } | ||
1571 | ✗ | break; | |
1572 | } | ||
1573 | } | ||
1574 | ✗ | } | |
1575 | ✗ | } | |
1576 | |||
1577 | 2 | int gate_appservice_run(gate_appservice_t* appservice, char const* program, char const* const* arguments, gate_size_t argcount, gate_uintptr_t apphandle) | |
1578 | { | ||
1579 | 2 | int exit_code = 666; | |
1580 | char svcname[256]; | ||
1581 | 2 | gate_size_t svcnamelength = appservice->get_servicename(appservice, svcname, sizeof(svcname)); | |
1582 | gate_result_t result; | ||
1583 | pid_t daemon_pid; | ||
1584 | |||
1585 | 2 | registered_service_instance = appservice; | |
1586 | |||
1587 | do | ||
1588 | { | ||
1589 | 2 | svcname[svcnamelength] = 0; | |
1590 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (appservice->init) |
1591 | { | ||
1592 | 2 | GATE_DEBUG_TRACE("appservice-init"); | |
1593 | 2 | result = appservice->init(appservice, program, arguments, argcount, apphandle); | |
1594 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (GATE_FAILED(result)) |
1595 | { | ||
1596 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (result == GATE_RESULT_CANCELED) |
1597 | { | ||
1598 | /* cancelation of init-method is not returned as exit code */ | ||
1599 | 1 | GATE_DEBUG_TRACE("appservice-init canceled"); | |
1600 | 1 | exit_code = 0; | |
1601 | } | ||
1602 | else | ||
1603 | { | ||
1604 | 1 | GATE_DEBUG_TRACE("appservice-init failed"); | |
1605 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | exit_code = GATE_RESULT_TO_EXITCODE(result); |
1606 | } | ||
1607 | 2 | break; | |
1608 | } | ||
1609 | } | ||
1610 | |||
1611 | ✗ | result = gate_appservice_read_pid_file(svcname, &daemon_pid); | |
1612 | ✗ | if (GATE_SUCCEEDED(result)) | |
1613 | { | ||
1614 | ✗ | if (is_foreign_pid(daemon_pid)) | |
1615 | { | ||
1616 | /* PID file points to an existing foreign process */ | ||
1617 | ✗ | GATE_DEBUG_TRACE("appservice foreign PID detected"); | |
1618 | ✗ | exit_code = 1; /* notify error */ | |
1619 | ✗ | break; | |
1620 | } | ||
1621 | } | ||
1622 | |||
1623 | /* create the forked daemon process: */ | ||
1624 | ✗ | result = gate_posix_daemonize(&daemon_pid); | |
1625 | ✗ | if (GATE_FAILED(result)) | |
1626 | { | ||
1627 | ✗ | GATE_DEBUG_TRACE("appservice-daemonize failed"); | |
1628 | ✗ | exit_code = GATE_RESULT_TO_EXITCODE(result); | |
1629 | ✗ | break; | |
1630 | } | ||
1631 | |||
1632 | ✗ | if (daemon_pid != 0) | |
1633 | { | ||
1634 | /* daemon successfully started */ | ||
1635 | /* this process can be terminated immediately */ | ||
1636 | /* signal success to caller */ | ||
1637 | ✗ | GATE_DEBUG_TRACE("appservice parent process completed"); | |
1638 | ✗ | _exit(0); | |
1639 | } | ||
1640 | |||
1641 | /* forked daemon process continues: */ | ||
1642 | ✗ | GATE_DEBUG_TRACE("appservice daemon process ready"); | |
1643 | ✗ | daemon_pid = getpid(); | |
1644 | ✗ | gate_appservice_write_pid_file(svcname, daemon_pid); | |
1645 | |||
1646 | ✗ | gate_syncevent_create(®istered_service_stopped, false); | |
1647 | ✗ | gate_syncevent_reset(®istered_service_stopped); | |
1648 | |||
1649 | ✗ | signal(SIGTSTP, &gate_appservice_signal_handler); | |
1650 | ✗ | signal(SIGCONT, &gate_appservice_signal_handler); | |
1651 | ✗ | signal(SIGTERM, &gate_appservice_signal_handler); | |
1652 | ✗ | signal(SIGHUP, &gate_appservice_signal_handler); | |
1653 | ✗ | signal(SIGQUIT, &gate_appservice_signal_handler); | |
1654 | ✗ | signal(SIGINT, &gate_appservice_signal_handler); | |
1655 | |||
1656 | ✗ | GATE_DEBUG_TRACE("appservice-on_start"); | |
1657 | ✗ | result = appservice->on_start(appservice); | |
1658 | ✗ | if (GATE_SUCCEEDED(result)) | |
1659 | { | ||
1660 | ✗ | GATE_DEBUG_TRACE("appservice-run"); | |
1661 | ✗ | result = appservice->run(appservice); | |
1662 | ✗ | GATE_DEBUG_TRACE("appservice-run completed"); | |
1663 | |||
1664 | ✗ | gate_syncevent_wait(®istered_service_stopped); | |
1665 | } | ||
1666 | else | ||
1667 | { | ||
1668 | ✗ | GATE_DEBUG_TRACE("appservice-on_start FAILED"); | |
1669 | } | ||
1670 | |||
1671 | ✗ | signal(SIGTSTP, SIG_DFL); | |
1672 | ✗ | signal(SIGCONT, SIG_DFL); | |
1673 | ✗ | signal(SIGTERM, SIG_DFL); | |
1674 | ✗ | signal(SIGHUP, SIG_DFL); | |
1675 | ✗ | signal(SIGQUIT, SIG_DFL); | |
1676 | ✗ | signal(SIGINT, SIG_DFL); | |
1677 | |||
1678 | ✗ | gate_syncevent_destroy(®istered_service_stopped); | |
1679 | |||
1680 | ✗ | gate_appservice_delete_pid_file(svcname); | |
1681 | |||
1682 | ✗ | exit_code = GATE_RESULT_TO_EXITCODE(result); | |
1683 | |||
1684 | } while (0); | ||
1685 | |||
1686 | 2 | registered_service_instance = NULL; | |
1687 | 2 | return exit_code; | |
1688 | } | ||
1689 | |||
1690 | #endif /* GATE_CORE_APPLCIATIONS_POSIX_IMPL */ | ||
1691 | |||
1692 | #if defined(GATE_CORE_APPLCIATIONS_EFI_IMPL) | ||
1693 | |||
1694 | int gate_app_run(gate_app_t* app, char const* program, char const* const* arguments, gate_size_t argcount, gate_uintptr_t apphandle) | ||
1695 | { | ||
1696 | gate_result_t result; | ||
1697 | result = app->init(app, program, arguments, argcount, apphandle); | ||
1698 | if (result == GATE_RESULT_OK) | ||
1699 | { | ||
1700 | result = app->run(app); | ||
1701 | } | ||
1702 | |||
1703 | if (result >= 0) | ||
1704 | { | ||
1705 | return 0; | ||
1706 | } | ||
1707 | else | ||
1708 | { | ||
1709 | return GATE_RESULT_TO_EXITCODE(result); | ||
1710 | } | ||
1711 | } | ||
1712 | |||
1713 | int gate_appservice_run(gate_appservice_t* appservice, char const* program, char const* const* arguments, gate_size_t argcount, gate_uintptr_t apphandle) | ||
1714 | { | ||
1715 | return GATE_RESULT_TO_EXITCODE(GATE_RESULT_NOTIMPLEMENTED); | ||
1716 | } | ||
1717 | |||
1718 | #endif /* GATE_CORE_APPLCIATIONS_EFI_IMPL */ | ||
1719 | |||
1720 | #if defined(GATE_CORE_APPLICATIONS_NO_IMPL) | ||
1721 | |||
1722 | int gate_app_run(gate_app_t* app, char const* program, char const* const* arguments, gate_size_t argcount, gate_uintptr_t apphandle) | ||
1723 | { | ||
1724 | gate_result_t result; | ||
1725 | result = app->init(app, program, arguments, argcount, apphandle); | ||
1726 | if (result == GATE_RESULT_OK) | ||
1727 | { | ||
1728 | result = app->run(app); | ||
1729 | } | ||
1730 | |||
1731 | if (result >= 0) | ||
1732 | { | ||
1733 | return 0; | ||
1734 | } | ||
1735 | else | ||
1736 | { | ||
1737 | return GATE_RESULT_TO_EXITCODE(result); | ||
1738 | } | ||
1739 | } | ||
1740 | |||
1741 | int gate_appservice_run(gate_appservice_t* appservice, char const* program, char const* const* arguments, gate_size_t argcount, gate_uintptr_t apphandle) | ||
1742 | { | ||
1743 | GATE_UNUSED_ARG(appservice); | ||
1744 | GATE_UNUSED_ARG(program); | ||
1745 | GATE_UNUSED_ARG(arguments); | ||
1746 | GATE_UNUSED_ARG(argcount); | ||
1747 | GATE_UNUSED_ARG(apphandle); | ||
1748 | return GATE_RESULT_TO_EXITCODE(GATE_RESULT_NOTIMPLEMENTED); | ||
1749 | } | ||
1750 | |||
1751 | #endif /* GATE_CORE_APPLICATIONS_NO_IMPL */ | ||
1752 |