GCC Code Coverage Report


Directory: src/gate/
File: src/gate/applications.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 278 478 58.2%
Functions: 18 28 64.3%
Branches: 122 247 49.4%

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, &current_opt->option_key, &current_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(&registered_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(&registered_service_stopped, false);
1647 gate_syncevent_reset(&registered_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(&registered_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(&registered_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