1 | /* |
2 | This file is part of Mtproto-proxy Library. |
3 | |
4 | Mtproto-proxy Library is free software: you can redistribute it and/or modify |
5 | it under the terms of the GNU Lesser General Public License as published by |
6 | the Free Software Foundation, either version 2 of the License, or |
7 | (at your option) any later version. |
8 | |
9 | Mtproto-proxy Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | GNU Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public License |
15 | along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>. |
16 | |
17 | Copyright 2009-2013 Vkontakte Ltd |
18 | 2008-2013 Nikolai Durov |
19 | 2008-2013 Andrey Lopatin |
20 | 2011-2013 Oleg Davydov |
21 | 2012-2013 Arseny Smirnov |
22 | 2012-2013 Aliaksei Levin |
23 | 2012-2013 Anton Maydell |
24 | 2013 Vitaliy Valtman |
25 | |
26 | Copyright 2014-2018 Telegram Messenger Inc |
27 | 2014-2018 Vitaly Valtman |
28 | */ |
29 | |
30 | |
31 | #define _FILE_OFFSET_BITS 64 |
32 | |
33 | #define _GNU_SOURCE 1 |
34 | |
35 | #include <arpa/inet.h> |
36 | #include <assert.h> |
37 | #include <errno.h> |
38 | #include <execinfo.h> |
39 | #include <fcntl.h> |
40 | #include <getopt.h> |
41 | #include <grp.h> |
42 | #include <netinet/in.h> |
43 | #include <pwd.h> |
44 | #include <signal.h> |
45 | #include <stdarg.h> |
46 | #include <stdio.h> |
47 | #include <stdlib.h> |
48 | #include <string.h> |
49 | #include <sys/resource.h> |
50 | #include <sys/wait.h> |
51 | #include <unistd.h> |
52 | #include <pthread.h> |
53 | |
54 | #include "common/kprintf.h" |
55 | #include "net/net-connections.h" |
56 | #include "net/net-events.h" |
57 | #include "net/net-msg-buffers.h" |
58 | |
59 | #include "server-functions.h" |
60 | |
61 | #define STR_HELPER(x) #x |
62 | #define STR(x) STR_HELPER(x) |
63 | |
64 | |
65 | long long max_allocated_buffer_bytes __attribute__ ((weak)); |
66 | |
67 | int engine_options_num; |
68 | char *engine_options[MAX_ENGINE_OPTIONS]; |
69 | |
70 | |
71 | int start_time; |
72 | |
73 | int daemonize = 0; |
74 | const char *username, *progname, *groupname; |
75 | |
76 | int change_user_group (const char *username, const char *groupname) { |
77 | struct passwd *pw; |
78 | /* lose root privileges if we have them */ |
79 | if (getuid() == 0 || geteuid() == 0) { |
80 | if (username == 0 || *username == '\0') { |
81 | username = DEFAULT_ENGINE_USER; |
82 | } |
83 | if ((pw = getpwnam (username)) == 0) { |
84 | kprintf ("change_user_group: can't find the user %s to switch to\n" , username); |
85 | return -1; |
86 | } |
87 | gid_t gid = pw->pw_gid; |
88 | if (setgroups (1, &gid) < 0) { |
89 | kprintf ("change_user_group: failed to clear supplementary groups list: %m\n" ); |
90 | return -1; |
91 | } |
92 | |
93 | if (groupname) { |
94 | struct group *g = getgrnam (groupname); |
95 | if (g == NULL) { |
96 | kprintf ("change_user_group: can't find the group %s to switch to\n" , groupname); |
97 | return -1; |
98 | } |
99 | gid = g->gr_gid; |
100 | } |
101 | |
102 | if (setgid (gid) < 0) { |
103 | kprintf ("change_user_group: setgid (%d) failed. %m\n" , (int) gid); |
104 | return -1; |
105 | } |
106 | |
107 | if (setuid (pw->pw_uid) < 0) { |
108 | kprintf ("change_user_group: failed to assume identity of user %s\n" , username); |
109 | return -1; |
110 | } |
111 | } |
112 | return 0; |
113 | } |
114 | |
115 | int change_user (const char *username) { |
116 | struct passwd *pw; |
117 | /* lose root privileges if we have them */ |
118 | if (getuid() == 0 || geteuid() == 0) { |
119 | if (username == 0 || *username == '\0') { |
120 | username = DEFAULT_ENGINE_USER; |
121 | // fprintf (stderr, "can't run as root without the -u switch\n"); |
122 | // return -1; |
123 | } |
124 | if ((pw = getpwnam (username)) == 0) { |
125 | kprintf ("can't find the user %s to switch to\n" , username); |
126 | return -1; |
127 | } |
128 | gid_t gid = pw->pw_gid; |
129 | if (setgroups(1, &gid) < 0) { |
130 | kprintf ("failed to clear supplementary groups list: %m\n" ); |
131 | return -1; |
132 | } |
133 | if (initgroups(username, gid) != 0) { |
134 | kprintf ("failed to load groups of user %s: %m\n" , username); |
135 | return -1; |
136 | } |
137 | if (setgid (pw->pw_gid) < 0 || setuid (pw->pw_uid) < 0) { |
138 | kprintf ("failed to assume identity of user %s\n" , username); |
139 | return -1; |
140 | } |
141 | } |
142 | return 0; |
143 | } |
144 | |
145 | int raise_file_rlimit (int maxfiles) { |
146 | struct rlimit rlim; |
147 | |
148 | if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) { |
149 | kprintf ("failed to getrlimit number of files\n" ); |
150 | return -1; |
151 | } else { |
152 | if (rlim.rlim_cur < maxfiles) |
153 | rlim.rlim_cur = maxfiles + 3; |
154 | if (rlim.rlim_max < rlim.rlim_cur) |
155 | rlim.rlim_max = rlim.rlim_cur; |
156 | if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) { |
157 | kprintf ("failed to set rlimit for open files. Try running as root or requesting smaller maxconns value.\n" ); |
158 | return -1; |
159 | } |
160 | } |
161 | return 0; |
162 | } |
163 | |
164 | |
165 | const char *get_version_string (void) __attribute__ ((weak)); |
166 | const char *get_version_string (void) { |
167 | return "unknown compiled at " __DATE__ " " __TIME__ " by gcc " __VERSION__; |
168 | } |
169 | |
170 | void print_backtrace (void) { |
171 | void *buffer[64]; |
172 | int nptrs = backtrace (buffer, 64); |
173 | kwrite (2, "\n------- Stack Backtrace -------\n" , 33); |
174 | backtrace_symbols_fd (buffer, nptrs, 2); |
175 | kwrite (2, "-------------------------------\n" , 32); |
176 | const char *s = get_version_string (); |
177 | if (s) { |
178 | kwrite (2, s, strlen (s)); |
179 | kwrite (2, "\n" , 1); |
180 | } |
181 | } |
182 | |
183 | pthread_t debug_main_pthread_id; |
184 | |
185 | void kill_main (void) { |
186 | if (debug_main_pthread_id && debug_main_pthread_id != pthread_self ()) { |
187 | pthread_kill (debug_main_pthread_id, SIGABRT); |
188 | } |
189 | } |
190 | |
191 | //can be called inside signal handler |
192 | void ksignal (int sig, void (*handler) (int)) { |
193 | struct sigaction act; |
194 | sigemptyset (&act.sa_mask); |
195 | act.sa_flags = SA_ONSTACK | SA_RESTART; |
196 | act.sa_handler = handler; |
197 | |
198 | if (sigaction (sig, &act, NULL) != 0) { |
199 | kwrite (2, "failed sigaction\n" , 17); |
200 | //_exit (EXIT_FAILURE); |
201 | } |
202 | } |
203 | |
204 | void ksignal_ex (int sig, void (*handler) (int, siginfo_t *, void *)) { |
205 | struct sigaction act; |
206 | sigemptyset (&act.sa_mask); |
207 | act.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; |
208 | act.sa_sigaction = handler; |
209 | |
210 | if (sigaction (sig, &act, NULL) != 0) { |
211 | kwrite (2, "failed sigaction\n" , 17); |
212 | _exit (EXIT_FAILURE); |
213 | } |
214 | } |
215 | |
216 | void queries_log_store (void *N, int limit, int max_size, int max_entry_size, int plain) __attribute__ ((weak)); |
217 | void queries_log_store (void *N, int limit, int max_size, int max_entry_size, int plain) {} |
218 | |
219 | void engine_set_terminal_attributes (void) __attribute__ ((weak)); |
220 | void engine_set_terminal_attributes (void) {} |
221 | |
222 | void extended_debug_handler (int sig, siginfo_t *info, void *cont) { |
223 | ksignal (sig, SIG_DFL); |
224 | |
225 | print_backtrace (); |
226 | |
227 | kill_main (); |
228 | |
229 | _exit (EXIT_FAILURE); |
230 | } |
231 | |
232 | void set_debug_handlers (void) { |
233 | ksignal_ex (SIGSEGV, extended_debug_handler); |
234 | ksignal_ex (SIGABRT, extended_debug_handler); |
235 | ksignal_ex (SIGFPE, extended_debug_handler); |
236 | ksignal_ex (SIGBUS, extended_debug_handler); |
237 | debug_main_pthread_id = pthread_self (); |
238 | } |
239 | |
240 | void usage (void) __attribute ((weak)); |
241 | |
242 | void usage (void) { |
243 | printf ("usage: %s <args>\n" , |
244 | progname ? progname : "SOMETHING" ); |
245 | exit (2); |
246 | } |
247 | |
248 | long long parse_memory_limit (const char *s) { |
249 | long long x; |
250 | char c = 0; |
251 | if (sscanf (s, "%lld%c" , &x, &c) < 1) { |
252 | kprintf ("Parsing limit for option fail: %s\n" , s); |
253 | usage (); |
254 | exit (1); |
255 | } |
256 | switch (c | 0x20) { |
257 | case ' ': break; |
258 | case 'k': x <<= 10; break; |
259 | case 'm': x <<= 20; break; |
260 | case 'g': x <<= 30; break; |
261 | case 't': x <<= 40; break; |
262 | default: |
263 | kprintf ("Parsing limit fail. Unknown suffix '%c'.\n" , c); |
264 | usage (); |
265 | exit (1); |
266 | } |
267 | return x; |
268 | } |
269 | |
270 | struct engine_parse_option *engine_parse_options; |
271 | int engine_parse_options_size; |
272 | int engine_parse_options_num; |
273 | |
274 | int find_parse_option (int val) { |
275 | int i; |
276 | for (i = 0; i < engine_parse_options_num; i++) { |
277 | struct engine_parse_option *P = &engine_parse_options[i]; |
278 | int j; |
279 | for (j = 0; j < P->val_cnt; j++) { |
280 | if (P->vals[j] == val) { |
281 | return i; |
282 | } |
283 | } |
284 | } |
285 | return -1; |
286 | } |
287 | |
288 | int find_parse_option_name (const char *name) { |
289 | int i; |
290 | for (i = 0; i < engine_parse_options_num; i++) { |
291 | struct engine_parse_option *P = &engine_parse_options[i]; |
292 | int j; |
293 | for (j = 0; j < P->longopts_cnt; j++) { |
294 | if (!strcmp (P->longopts[j], name)) { |
295 | return i; |
296 | } |
297 | } |
298 | } |
299 | return -1; |
300 | } |
301 | |
302 | int default_parse_option_func (int a) __attribute__ ((weak)); |
303 | int default_parse_option_func (int a) { return -1; } |
304 | |
305 | void parse_option_up (struct engine_parse_option *P) { |
306 | struct engine_parse_option *Q = P - 1; |
307 | while (Q >= engine_parse_options && Q->smallest_val > P->smallest_val) { |
308 | Q --; |
309 | } |
310 | Q ++; |
311 | if (Q != P) { |
312 | struct engine_parse_option T; |
313 | T = *P; |
314 | memmove (Q + 1, Q, (P - Q) * sizeof (struct engine_parse_option)); |
315 | *Q = T; |
316 | } |
317 | } |
318 | |
319 | void parse_option_down (struct engine_parse_option *P) { |
320 | struct engine_parse_option *Q = P + 1; |
321 | while (Q < engine_parse_options + engine_parse_options_num && Q->smallest_val < P->smallest_val) { |
322 | Q ++; |
323 | } |
324 | Q --; |
325 | if (Q != P) { |
326 | struct engine_parse_option T; |
327 | T = *Q; |
328 | memmove (P + 1, P, (P - Q) * sizeof (struct engine_parse_option)); |
329 | *P = T; |
330 | } |
331 | } |
332 | |
333 | void parse_option_internal (const char *name, int arg, int *var, int val, unsigned flags, int (*func)(int), char *help) { |
334 | int p = find_parse_option (val); |
335 | if (p >= 0) { |
336 | kprintf ("duplicate parse option %d\n" , val); |
337 | usage (); |
338 | } |
339 | assert (engine_parse_options_num <= engine_parse_options_size); |
340 | if (engine_parse_options_num == engine_parse_options_size) { |
341 | engine_parse_options_size = 10 + 2 * engine_parse_options_size; |
342 | engine_parse_options = realloc (engine_parse_options, sizeof (struct engine_parse_option) * engine_parse_options_size); |
343 | } |
344 | assert (engine_parse_options_num < engine_parse_options_size); |
345 | struct engine_parse_option *P = &engine_parse_options[engine_parse_options_num ++]; |
346 | P->arg = arg; |
347 | P->flags = flags; |
348 | P->func = func ? func : default_parse_option_func; |
349 | P->help = help; |
350 | |
351 | |
352 | P->longopts = malloc (sizeof (void *)); |
353 | P->longopts[0] = name; |
354 | P->longopts_cnt = 1; |
355 | |
356 | P->vals = malloc (sizeof (int)); |
357 | P->vals[0] = val; |
358 | P->val_cnt = 1; |
359 | P->smallest_val = val; |
360 | P->base_val = val; |
361 | |
362 | parse_option_up (P); |
363 | } |
364 | |
365 | void parse_option_ex (const char *name, int arg, int *var, int val, unsigned flags, int (*func)(int), const char *help, ...) { |
366 | char *h; |
367 | va_list ap; |
368 | va_start (ap, help); |
369 | assert (vasprintf (&h, help, ap) >= 0); |
370 | va_end (ap); |
371 | |
372 | parse_option_internal (name, arg, var, val, flags, func, h); |
373 | } |
374 | |
375 | void parse_option (const char *name, int arg, int *var, int val, const char *help, ...) { |
376 | char *h; |
377 | va_list ap; |
378 | va_start (ap, help); |
379 | assert (vasprintf (&h, help, ap) >= 0); |
380 | va_end (ap); |
381 | |
382 | parse_option_internal (name, arg, var, val, LONGOPT_CUSTOM_SET, NULL, h); |
383 | } |
384 | |
385 | int builtin_parse_option (int val); |
386 | void parse_option_builtin (const char *name, int arg, int *var, int val, unsigned flags, const char *help, ...) { |
387 | parse_option_internal (name, arg, var, val, flags, builtin_parse_option, help ? strdup (help) : NULL); |
388 | } |
389 | |
390 | |
391 | void remove_parse_option_completely (int val) { |
392 | int t = find_parse_option (val); |
393 | assert (t >= 0); |
394 | |
395 | struct engine_parse_option *P = &engine_parse_options[t]; |
396 | |
397 | assert (P->vals[0] == val); |
398 | if (P->help) { |
399 | free (P->help); |
400 | } |
401 | free (P->vals); |
402 | free (P->longopts); |
403 | memmove (engine_parse_options + t, engine_parse_options + t + 1, (engine_parse_options_num - t - 1) * sizeof (struct engine_parse_option)); |
404 | engine_parse_options_num --; |
405 | return; |
406 | } |
407 | |
408 | void remove_parse_option (int val) { |
409 | int t = find_parse_option (val); |
410 | if (t < 0) { |
411 | kprintf ("Can not remove unknown option %d\n" , val); |
412 | usage (); |
413 | } |
414 | |
415 | struct engine_parse_option *P = &engine_parse_options[t]; |
416 | |
417 | if (P->val_cnt == 1) { |
418 | assert (P->vals[0] == val); |
419 | free (P->help); |
420 | free (P->vals); |
421 | free (P->longopts); |
422 | memmove (engine_parse_options + t, engine_parse_options + t + 1, (engine_parse_options_num - t - 1) * sizeof (struct engine_parse_option)); |
423 | engine_parse_options_num --; |
424 | return; |
425 | } |
426 | |
427 | int *new_vals = malloc (4 * (P->val_cnt - 1)); |
428 | int i; |
429 | int p = 0; |
430 | for (i = 0; i < P->val_cnt; i++) { |
431 | if (P->vals[i] != val) { |
432 | new_vals[p ++] = P->vals[i]; |
433 | } |
434 | } |
435 | free (P->vals); |
436 | P->vals = new_vals; |
437 | P->val_cnt --; |
438 | |
439 | if (P->smallest_val == val) { |
440 | P->smallest_val = 0x7fffffff; |
441 | int i; |
442 | for (i = 0; i < P->val_cnt; i++) { |
443 | if (P->vals[i] < P->smallest_val) { |
444 | P->smallest_val = P->vals[i]; |
445 | } |
446 | } |
447 | parse_option_down (P); |
448 | } |
449 | if (P->base_val == val) { |
450 | P->base_val = P->smallest_val; |
451 | } |
452 | } |
453 | |
454 | void parse_option_alias (const char *name, int val) { |
455 | int l = find_parse_option (val); |
456 | if (l >= 0) { |
457 | if (val >= 33 && val <= 127) { |
458 | kprintf ("Duplicate option `%c`\n" , (char)val); |
459 | } else { |
460 | kprintf ("Duplicate option %d\n" , val); |
461 | } |
462 | usage (); |
463 | } |
464 | l = find_parse_option_name (name); |
465 | if (l < 0) { |
466 | kprintf ("can't find option '%s'\n" , name); |
467 | usage (); |
468 | } |
469 | |
470 | struct engine_parse_option *P = &engine_parse_options[l]; |
471 | P->val_cnt ++; |
472 | P->vals = realloc (P->vals, 4 * P->val_cnt); |
473 | P->vals[P->val_cnt - 1] = val; |
474 | if (val < P->smallest_val) { |
475 | P->smallest_val = val; |
476 | parse_option_up (P); |
477 | } |
478 | } |
479 | |
480 | void parse_option_long_alias (const char *name, const char *alias_name) { |
481 | int l = find_parse_option_name (alias_name); |
482 | if (l >= 0) { |
483 | kprintf ("Duplicate option %s\n" , alias_name); |
484 | usage (); |
485 | } |
486 | l = find_parse_option_name (name); |
487 | if (l < 0) { |
488 | kprintf ("can't find option '%s'\n" , name); |
489 | usage (); |
490 | } |
491 | |
492 | struct engine_parse_option *P = &engine_parse_options[l]; |
493 | P->longopts_cnt ++; |
494 | P->longopts = realloc (P->longopts, sizeof (void *) * P->longopts_cnt); |
495 | P->longopts[P->longopts_cnt - 1] = alias_name; |
496 | } |
497 | |
498 | int parse_usage (void) { |
499 | int max = 0; |
500 | |
501 | int i; |
502 | for (i = 0; i < engine_parse_options_num; i++) { |
503 | struct engine_parse_option *P = &engine_parse_options[i]; |
504 | int cur = 0; |
505 | int j; |
506 | for (j = 0; j < P->val_cnt; j++) { |
507 | if (P->vals[j] <= 127) { |
508 | cur += 3; |
509 | } |
510 | } |
511 | for (j = 0; j < P->longopts_cnt; j++) { |
512 | cur += strlen (P->longopts[j]) + 3; |
513 | } |
514 | |
515 | if (P->arg == required_argument) { |
516 | cur += 6; |
517 | } else if (P->arg == optional_argument) { |
518 | cur += 6; |
519 | } |
520 | if (cur > max) { |
521 | max = cur; |
522 | } |
523 | } |
524 | |
525 | for (i = 0; i < engine_parse_options_num; i++) { |
526 | struct engine_parse_option *P = &engine_parse_options[i]; |
527 | int cur = 0; |
528 | printf ("\t" ); |
529 | int j; |
530 | for (j = 0; j < P->longopts_cnt; j++) { |
531 | if (cur) { |
532 | printf ("/" ); |
533 | cur ++; |
534 | } |
535 | cur += strlen (P->longopts[j]) + 2; |
536 | printf ("--%s" , P->longopts[j]); |
537 | } |
538 | for (j = 0; j < P->val_cnt; j++) { |
539 | if (P->vals[j] <= 127) { |
540 | if (cur) { |
541 | printf ("/" ); |
542 | cur ++; |
543 | } |
544 | printf ("-%c" , (char)P->vals[j]); |
545 | cur += 2; |
546 | } |
547 | } |
548 | if (P->arg == required_argument) { |
549 | printf (" <arg>" ); |
550 | cur += 6; |
551 | } else if (P->arg == optional_argument) { |
552 | printf (" {arg}" ); |
553 | cur += 6; |
554 | } |
555 | while (cur < max) { |
556 | printf (" " ); |
557 | cur ++; |
558 | } |
559 | printf ("\t" ); |
560 | if (P->help) { |
561 | char *e = P->help; |
562 | while (*e) { |
563 | printf ("%c" , *e); |
564 | if (*e == '\n') { |
565 | printf ("\t" ); |
566 | int i; |
567 | for (i = 0; i < max; i++) { |
568 | printf (" " ); |
569 | } |
570 | printf ("\t" ); |
571 | } |
572 | e ++; |
573 | } |
574 | printf ("\n" ); |
575 | // printf ("%s\n", global_longopts_help[s]); |
576 | } else { |
577 | printf ("no help provided\n" ); |
578 | } |
579 | } |
580 | return 0; |
581 | } |
582 | |
583 | int builtin_parse_option (int val) { |
584 | switch (val) { |
585 | case 'v': |
586 | if (!optarg) { |
587 | verbosity++; |
588 | } else { |
589 | verbosity = atoi (optarg); |
590 | } |
591 | break; |
592 | case 'h': |
593 | usage (); |
594 | exit (2); |
595 | case 'u': |
596 | if (username) { |
597 | kprintf ("wrong option -u%s, username is already defined as '%s'.\n" , optarg, username); |
598 | exit (1); |
599 | } |
600 | username = optarg; |
601 | break; |
602 | case 'l': |
603 | logname = optarg; |
604 | break; |
605 | case 'd': |
606 | if (!optarg) { |
607 | daemonize ^= 1; |
608 | } else { |
609 | daemonize = atoi (optarg) != 0; |
610 | } |
611 | break; |
612 | case 202: |
613 | errno = 0; |
614 | if (nice (atoi (optarg)) == -1 && errno) { |
615 | perror ("nice" ); |
616 | } |
617 | break; |
618 | case 208: |
619 | max_allocated_buffer_bytes = parse_memory_limit (optarg); |
620 | break; |
621 | default: |
622 | return -1; |
623 | } |
624 | return 0; |
625 | } |
626 | |
627 | int parse_one_option (int val) { |
628 | int t = find_parse_option (val); |
629 | if (t < 0) { |
630 | return -1; |
631 | } |
632 | struct engine_parse_option *P = &engine_parse_options[t]; |
633 | return P->func (P->base_val); |
634 | } |
635 | |
636 | int parse_engine_options_long (int argc, char **argv) { |
637 | engine_options_num = argc; |
638 | memcpy ((void *)engine_options, argv, sizeof (void *) * argc); |
639 | |
640 | int total_longopts = 0; |
641 | int total_shortopts_len = 0; |
642 | int i; |
643 | for (i = 0; i < engine_parse_options_num; i++) { |
644 | struct engine_parse_option *P = &engine_parse_options[i]; |
645 | total_longopts += P->longopts_cnt; |
646 | int j; |
647 | for (j = 0; j < P->val_cnt; j++) { |
648 | if (P->vals[j] <= 127) { |
649 | total_shortopts_len += (P->arg == required_argument) ? 2 : 1; |
650 | } |
651 | } |
652 | } |
653 | |
654 | char *shortopts = malloc (total_shortopts_len + 1); |
655 | assert (shortopts); |
656 | struct option *longopts = malloc ((total_longopts + 1) * sizeof (struct option)); |
657 | int lpos = 0; |
658 | int spos = 0; |
659 | |
660 | for (i = 0; i < engine_parse_options_num; i++) { |
661 | struct engine_parse_option *P = &engine_parse_options[i]; |
662 | int j; |
663 | |
664 | for (j = 0; j < P->longopts_cnt; j++) { |
665 | assert (lpos < total_longopts); |
666 | longopts[lpos].flag = NULL; |
667 | longopts[lpos].has_arg = P->arg; |
668 | longopts[lpos].name = P->longopts[j]; |
669 | longopts[lpos].val = P->base_val; |
670 | lpos ++; |
671 | } |
672 | |
673 | for (j = 0; j < P->val_cnt; j++) { |
674 | if (P->vals[j] <= 127) { |
675 | assert (spos < total_shortopts_len); |
676 | shortopts[spos ++] = P->vals[j]; |
677 | if (P->arg == required_argument) { |
678 | assert (spos < total_shortopts_len); |
679 | shortopts[spos ++] = ':'; |
680 | } |
681 | } |
682 | } |
683 | } |
684 | |
685 | assert (lpos == total_longopts); |
686 | memset (&longopts[lpos], 0, sizeof (struct option)); |
687 | assert (spos == total_shortopts_len); |
688 | shortopts[spos] = 0; |
689 | |
690 | while (1) { |
691 | int option_index = -1; |
692 | int c = getopt_long (argc, argv, shortopts, longopts, &option_index); |
693 | if (c == -1) { break; } |
694 | if (!c) { continue; } |
695 | if (c == '?') { |
696 | kprintf ("Unrecognized option\n" ); |
697 | usage (); |
698 | } |
699 | if (parse_one_option (c) < 0) { |
700 | if (option_index >= 0) { |
701 | assert (option_index < total_longopts); |
702 | kprintf ("Can not parse option %s\n" , longopts[option_index].name); |
703 | usage (); |
704 | } else if (c <= 127) { |
705 | kprintf ("Can not parse option '%c'\n" , (char)c); |
706 | usage (); |
707 | } else { |
708 | kprintf ("Can not parse option %d\n" , c); |
709 | usage (); |
710 | } |
711 | } |
712 | } |
713 | return 0; |
714 | } |
715 | |
716 | int in_keep_options_list (const unsigned *list, unsigned num) { |
717 | if (!list) { return 0; } |
718 | const unsigned *a = list; |
719 | while (*a) { |
720 | if (*a == num) { return 1; } |
721 | a ++; |
722 | } |
723 | return 0; |
724 | } |
725 | |
726 | void engine_add_net_parse_options (void) __attribute__ ((weak)); |
727 | void engine_add_net_parse_options (void) {} |
728 | void engine_add_engine_parse_options (void) __attribute__ ((weak)); |
729 | void engine_add_engine_parse_options (void) {} |
730 | |
731 | void add_builtin_parse_options (void) { |
732 | parse_option_builtin ("verbosity" , optional_argument, 0, 'v', LONGOPT_COMMON_SET, "sets or increases verbosity level" ); |
733 | parse_option_builtin ("help" , no_argument, 0, 'h', LONGOPT_COMMON_SET, "prints help and exits" ); |
734 | parse_option_builtin ("user" , required_argument, 0, 'u', LONGOPT_COMMON_SET, "sets user name to make setuid" ); |
735 | parse_option_builtin ("log" , required_argument, 0, 'l', LONGOPT_COMMON_SET, "sets log file name" ); |
736 | parse_option_builtin ("daemonize" , optional_argument, 0, 'd', LONGOPT_COMMON_SET, "changes between daemonize/not daemonize mode" ); |
737 | parse_option_builtin ("nice" , required_argument, 0, 202, LONGOPT_COMMON_SET, "sets niceness" ); |
738 | parse_option_ex ("msg-buffers-size" , required_argument, 0, 208, LONGOPT_COMMON_SET, builtin_parse_option, "sets maximal buffers size (default %lld)" , (long long)MSG_DEFAULT_MAX_ALLOCATED_BYTES); |
739 | //parse_option_builtin ("tl-history", optional_argument, 0, 210, LONGOPT_NET_SET, "long }, |
740 | //parse_option_builtin ("tl-op-stat", no_argument, 0, 211, LONGOPT_NET_SET, "enabled stat about op usage"); |
741 | //{ "rwm-peak-recovery", no_argument, 0, 213}, |
742 | |
743 | engine_add_net_parse_options (); |
744 | engine_add_engine_parse_options (); |
745 | } |
746 | |
747 | |