xref: /unit/src/nxt_conf_validation.c (revision 1988:574e6e17fc19)
1 
2 /*
3  * Copyright (C) Valentin V. Bartenev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 #include <nxt_conf.h>
9 #include <nxt_cert.h>
10 #include <nxt_router.h>
11 #include <nxt_http.h>
12 #include <nxt_sockaddr.h>
13 #include <nxt_http_route_addr.h>
14 #include <nxt_regex.h>
15 
16 
17 typedef enum {
18     NXT_CONF_VLDT_NULL    = 1 << NXT_CONF_NULL,
19     NXT_CONF_VLDT_BOOLEAN = 1 << NXT_CONF_BOOLEAN,
20     NXT_CONF_VLDT_INTEGER = 1 << NXT_CONF_INTEGER,
21     NXT_CONF_VLDT_NUMBER  = (1 << NXT_CONF_NUMBER) | NXT_CONF_VLDT_INTEGER,
22     NXT_CONF_VLDT_STRING  = 1 << NXT_CONF_STRING,
23     NXT_CONF_VLDT_ARRAY   = 1 << NXT_CONF_ARRAY,
24     NXT_CONF_VLDT_OBJECT  = 1 << NXT_CONF_OBJECT,
25 } nxt_conf_vldt_type_t;
26 
27 #define NXT_CONF_VLDT_ANY_TYPE  (NXT_CONF_VLDT_NULL                           \
28                                  |NXT_CONF_VLDT_BOOLEAN                       \
29                                  |NXT_CONF_VLDT_NUMBER                        \
30                                  |NXT_CONF_VLDT_STRING                        \
31                                  |NXT_CONF_VLDT_ARRAY                         \
32                                  |NXT_CONF_VLDT_OBJECT)
33 
34 
35 typedef enum {
36     NXT_CONF_VLDT_REQUIRED  = 1 << 0,
37     NXT_CONF_VLDT_VAR       = 1 << 1,
38 } nxt_conf_vldt_flags_t;
39 
40 
41 typedef nxt_int_t (*nxt_conf_vldt_handler_t)(nxt_conf_validation_t *vldt,
42                                              nxt_conf_value_t *value,
43                                              void *data);
44 typedef nxt_int_t (*nxt_conf_vldt_member_t)(nxt_conf_validation_t *vldt,
45                                             nxt_str_t *name,
46                                             nxt_conf_value_t *value);
47 typedef nxt_int_t (*nxt_conf_vldt_element_t)(nxt_conf_validation_t *vldt,
48                                              nxt_conf_value_t *value);
49 
50 
51 typedef struct nxt_conf_vldt_object_s  nxt_conf_vldt_object_t;
52 
53 struct nxt_conf_vldt_object_s {
54     nxt_str_t                     name;
55     nxt_conf_vldt_type_t          type:32;
56     nxt_conf_vldt_flags_t         flags:32;
57     nxt_conf_vldt_handler_t       validator;
58 
59     union {
60         nxt_conf_vldt_object_t    *members;
61         nxt_conf_vldt_object_t    *next;
62         nxt_conf_vldt_member_t    object;
63         nxt_conf_vldt_element_t   array;
64         const char                *string;
65     } u;
66 };
67 
68 
69 #define NXT_CONF_VLDT_NEXT(next)  { .u.members = next }
70 #define NXT_CONF_VLDT_END         { .name = nxt_null_string }
71 
72 
73 static nxt_int_t nxt_conf_vldt_type(nxt_conf_validation_t *vldt,
74     nxt_str_t *name, nxt_conf_value_t *value, nxt_conf_vldt_type_t type);
75 static nxt_int_t nxt_conf_vldt_error(nxt_conf_validation_t *vldt,
76     const char *fmt, ...);
77 static nxt_int_t nxt_conf_vldt_var(nxt_conf_validation_t *vldt, nxt_str_t *name,
78     nxt_str_t *value);
79 nxt_inline nxt_int_t nxt_conf_vldt_unsupported(nxt_conf_validation_t *vldt,
80     nxt_conf_value_t *value, void *data);
81 
82 static nxt_int_t nxt_conf_vldt_mtypes(nxt_conf_validation_t *vldt,
83     nxt_conf_value_t *value, void *data);
84 static nxt_int_t nxt_conf_vldt_mtypes_type(nxt_conf_validation_t *vldt,
85     nxt_str_t *name, nxt_conf_value_t *value);
86 static nxt_int_t nxt_conf_vldt_mtypes_extension(nxt_conf_validation_t *vldt,
87     nxt_conf_value_t *value);
88 static nxt_int_t nxt_conf_vldt_listener(nxt_conf_validation_t *vldt,
89     nxt_str_t *name, nxt_conf_value_t *value);
90 #if (NXT_TLS)
91 static nxt_int_t nxt_conf_vldt_certificate(nxt_conf_validation_t *vldt,
92     nxt_conf_value_t *value, void *data);
93 #if (NXT_HAVE_OPENSSL_CONF_CMD)
94 static nxt_int_t nxt_conf_vldt_object_conf_commands(nxt_conf_validation_t *vldt,
95     nxt_conf_value_t *value, void *data);
96 #endif
97 static nxt_int_t nxt_conf_vldt_certificate_element(nxt_conf_validation_t *vldt,
98     nxt_conf_value_t *value);
99 static nxt_int_t nxt_conf_vldt_tls_cache_size(nxt_conf_validation_t *vldt,
100     nxt_conf_value_t *value, void *data);
101 static nxt_int_t nxt_conf_vldt_tls_timeout(nxt_conf_validation_t *vldt,
102     nxt_conf_value_t *value, void *data);
103 #if (NXT_HAVE_OPENSSL_TLSEXT)
104 static nxt_int_t nxt_conf_vldt_ticket_key(nxt_conf_validation_t *vldt,
105     nxt_conf_value_t *value, void *data);
106 static nxt_int_t nxt_conf_vldt_ticket_key_element(nxt_conf_validation_t *vldt,
107     nxt_conf_value_t *value);
108 #endif
109 #endif
110 static nxt_int_t nxt_conf_vldt_action(nxt_conf_validation_t *vldt,
111     nxt_conf_value_t *value, void *data);
112 static nxt_int_t nxt_conf_vldt_pass(nxt_conf_validation_t *vldt,
113     nxt_conf_value_t *value, void *data);
114 static nxt_int_t nxt_conf_vldt_return(nxt_conf_validation_t *vldt,
115     nxt_conf_value_t *value, void *data);
116 static nxt_int_t nxt_conf_vldt_share(nxt_conf_validation_t *vldt,
117      nxt_conf_value_t *value, void *data);
118 static nxt_int_t nxt_conf_vldt_share_element(nxt_conf_validation_t *vldt,
119     nxt_conf_value_t *value);
120 static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt,
121     nxt_conf_value_t *value, void *data);
122 static nxt_int_t nxt_conf_vldt_python(nxt_conf_validation_t *vldt,
123     nxt_conf_value_t *value, void *data);
124 static nxt_int_t nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt,
125     nxt_conf_value_t *value, void *data);
126 static nxt_int_t nxt_conf_vldt_python_path_element(nxt_conf_validation_t *vldt,
127     nxt_conf_value_t *value);
128 static nxt_int_t nxt_conf_vldt_python_protocol(nxt_conf_validation_t *vldt,
129     nxt_conf_value_t *value, void *data);
130 static nxt_int_t nxt_conf_vldt_threads(nxt_conf_validation_t *vldt,
131     nxt_conf_value_t *value, void *data);
132 static nxt_int_t nxt_conf_vldt_thread_stack_size(nxt_conf_validation_t *vldt,
133     nxt_conf_value_t *value, void *data);
134 static nxt_int_t nxt_conf_vldt_routes(nxt_conf_validation_t *vldt,
135     nxt_conf_value_t *value, void *data);
136 static nxt_int_t nxt_conf_vldt_routes_member(nxt_conf_validation_t *vldt,
137     nxt_str_t *name, nxt_conf_value_t *value);
138 static nxt_int_t nxt_conf_vldt_route(nxt_conf_validation_t *vldt,
139     nxt_conf_value_t *value);
140 static nxt_int_t nxt_conf_vldt_match_encoded_patterns_sets(
141     nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data);
142 static nxt_int_t nxt_conf_vldt_match_encoded_patterns_set(
143     nxt_conf_validation_t *vldt, nxt_conf_value_t *value);
144 static nxt_int_t nxt_conf_vldt_match_encoded_patterns_set_member(
145     nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value);
146 static nxt_int_t nxt_conf_vldt_match_encoded_patterns(
147     nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data);
148 static nxt_int_t nxt_conf_vldt_match_encoded_pattern(
149     nxt_conf_validation_t *vldt, nxt_conf_value_t *value);
150 static nxt_int_t nxt_conf_vldt_match_patterns(nxt_conf_validation_t *vldt,
151     nxt_conf_value_t *value, void *data);
152 static nxt_int_t nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt,
153     nxt_conf_value_t *value);
154 static nxt_int_t nxt_conf_vldt_match_patterns_sets(nxt_conf_validation_t *vldt,
155     nxt_conf_value_t *value, void *data);
156 static nxt_int_t nxt_conf_vldt_match_patterns_set(nxt_conf_validation_t *vldt,
157     nxt_conf_value_t *value);
158 static nxt_int_t nxt_conf_vldt_match_patterns_set_member(
159     nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value);
160 static nxt_int_t nxt_conf_vldt_match_scheme_pattern(nxt_conf_validation_t *vldt,
161     nxt_conf_value_t *value, void *data);
162 static nxt_int_t nxt_conf_vldt_match_addrs(nxt_conf_validation_t *vldt,
163     nxt_conf_value_t *value, void *data);
164 static nxt_int_t nxt_conf_vldt_match_addr(nxt_conf_validation_t *vldt,
165     nxt_conf_value_t *value);
166 static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt,
167     nxt_conf_value_t *value, void *data);
168 static nxt_int_t nxt_conf_vldt_app(nxt_conf_validation_t *vldt,
169     nxt_str_t *name, nxt_conf_value_t *value);
170 static nxt_int_t nxt_conf_vldt_object(nxt_conf_validation_t *vldt,
171     nxt_conf_value_t *value, void *data);
172 static nxt_int_t nxt_conf_vldt_processes(nxt_conf_validation_t *vldt,
173     nxt_conf_value_t *value, void *data);
174 static nxt_int_t nxt_conf_vldt_object_iterator(nxt_conf_validation_t *vldt,
175     nxt_conf_value_t *value, void *data);
176 static nxt_int_t nxt_conf_vldt_array_iterator(nxt_conf_validation_t *vldt,
177     nxt_conf_value_t *value, void *data);
178 static nxt_int_t nxt_conf_vldt_environment(nxt_conf_validation_t *vldt,
179     nxt_str_t *name, nxt_conf_value_t *value);
180 static nxt_int_t nxt_conf_vldt_targets_exclusive(
181     nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data);
182 static nxt_int_t nxt_conf_vldt_targets(nxt_conf_validation_t *vldt,
183     nxt_conf_value_t *value, void *data);
184 static nxt_int_t nxt_conf_vldt_target(nxt_conf_validation_t *vldt,
185     nxt_str_t *name, nxt_conf_value_t *value);
186 static nxt_int_t nxt_conf_vldt_argument(nxt_conf_validation_t *vldt,
187     nxt_conf_value_t *value);
188 static nxt_int_t nxt_conf_vldt_php(nxt_conf_validation_t *vldt,
189     nxt_conf_value_t *value, void *data);
190 static nxt_int_t nxt_conf_vldt_php_option(nxt_conf_validation_t *vldt,
191     nxt_str_t *name, nxt_conf_value_t *value);
192 static nxt_int_t nxt_conf_vldt_java_classpath(nxt_conf_validation_t *vldt,
193     nxt_conf_value_t *value);
194 static nxt_int_t nxt_conf_vldt_java_option(nxt_conf_validation_t *vldt,
195     nxt_conf_value_t *value);
196 static nxt_int_t nxt_conf_vldt_upstream(nxt_conf_validation_t *vldt,
197      nxt_str_t *name, nxt_conf_value_t *value);
198 static nxt_int_t nxt_conf_vldt_server(nxt_conf_validation_t *vldt,
199     nxt_str_t *name, nxt_conf_value_t *value);
200 static nxt_int_t nxt_conf_vldt_server_weight(nxt_conf_validation_t *vldt,
201     nxt_conf_value_t *value, void *data);
202 
203 static nxt_int_t nxt_conf_vldt_isolation(nxt_conf_validation_t *vldt,
204     nxt_conf_value_t *value, void *data);
205 static nxt_int_t nxt_conf_vldt_clone_namespaces(nxt_conf_validation_t *vldt,
206     nxt_conf_value_t *value, void *data);
207 
208 #if (NXT_HAVE_CLONE_NEWUSER)
209 static nxt_int_t nxt_conf_vldt_clone_procmap(nxt_conf_validation_t *vldt,
210     const char* mapfile, nxt_conf_value_t *value);
211 static nxt_int_t nxt_conf_vldt_clone_uidmap(nxt_conf_validation_t *vldt,
212     nxt_conf_value_t *value);
213 static nxt_int_t nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt,
214     nxt_conf_value_t *value);
215 #endif
216 
217 
218 static nxt_conf_vldt_object_t  nxt_conf_vldt_setting_members[];
219 static nxt_conf_vldt_object_t  nxt_conf_vldt_http_members[];
220 static nxt_conf_vldt_object_t  nxt_conf_vldt_websocket_members[];
221 static nxt_conf_vldt_object_t  nxt_conf_vldt_static_members[];
222 static nxt_conf_vldt_object_t  nxt_conf_vldt_client_ip_members[];
223 #if (NXT_TLS)
224 static nxt_conf_vldt_object_t  nxt_conf_vldt_tls_members[];
225 static nxt_conf_vldt_object_t  nxt_conf_vldt_session_members[];
226 #endif
227 static nxt_conf_vldt_object_t  nxt_conf_vldt_match_members[];
228 static nxt_conf_vldt_object_t  nxt_conf_vldt_python_target_members[];
229 static nxt_conf_vldt_object_t  nxt_conf_vldt_php_common_members[];
230 static nxt_conf_vldt_object_t  nxt_conf_vldt_php_options_members[];
231 static nxt_conf_vldt_object_t  nxt_conf_vldt_php_target_members[];
232 static nxt_conf_vldt_object_t  nxt_conf_vldt_common_members[];
233 static nxt_conf_vldt_object_t  nxt_conf_vldt_app_limits_members[];
234 static nxt_conf_vldt_object_t  nxt_conf_vldt_app_processes_members[];
235 static nxt_conf_vldt_object_t  nxt_conf_vldt_app_isolation_members[];
236 static nxt_conf_vldt_object_t  nxt_conf_vldt_app_namespaces_members[];
237 #if (NXT_HAVE_ISOLATION_ROOTFS)
238 static nxt_conf_vldt_object_t  nxt_conf_vldt_app_automount_members[];
239 #endif
240 
241 
242 static nxt_conf_vldt_object_t  nxt_conf_vldt_root_members[] = {
243     {
244         .name       = nxt_string("settings"),
245         .type       = NXT_CONF_VLDT_OBJECT,
246         .validator  = nxt_conf_vldt_object,
247         .u.members  = nxt_conf_vldt_setting_members,
248     }, {
249         .name       = nxt_string("listeners"),
250         .type       = NXT_CONF_VLDT_OBJECT,
251         .validator  = nxt_conf_vldt_object_iterator,
252         .u.object   = nxt_conf_vldt_listener,
253     }, {
254         .name       = nxt_string("routes"),
255         .type       = NXT_CONF_VLDT_ARRAY | NXT_CONF_VLDT_OBJECT,
256         .validator  = nxt_conf_vldt_routes,
257     }, {
258         .name       = nxt_string("applications"),
259         .type       = NXT_CONF_VLDT_OBJECT,
260         .validator  = nxt_conf_vldt_object_iterator,
261         .u.object   = nxt_conf_vldt_app,
262     }, {
263         .name       = nxt_string("upstreams"),
264         .type       = NXT_CONF_VLDT_OBJECT,
265         .validator  = nxt_conf_vldt_object_iterator,
266         .u.object   = nxt_conf_vldt_upstream,
267     }, {
268         .name       = nxt_string("access_log"),
269         .type       = NXT_CONF_VLDT_STRING,
270     },
271 
272     NXT_CONF_VLDT_END
273 };
274 
275 
276 static nxt_conf_vldt_object_t  nxt_conf_vldt_setting_members[] = {
277     {
278         .name       = nxt_string("http"),
279         .type       = NXT_CONF_VLDT_OBJECT,
280         .validator  = nxt_conf_vldt_object,
281         .u.members  = nxt_conf_vldt_http_members,
282     },
283 
284     NXT_CONF_VLDT_END
285 };
286 
287 
288 static nxt_conf_vldt_object_t  nxt_conf_vldt_http_members[] = {
289     {
290         .name       = nxt_string("header_read_timeout"),
291         .type       = NXT_CONF_VLDT_INTEGER,
292     }, {
293         .name       = nxt_string("body_read_timeout"),
294         .type       = NXT_CONF_VLDT_INTEGER,
295     }, {
296         .name       = nxt_string("send_timeout"),
297         .type       = NXT_CONF_VLDT_INTEGER,
298     }, {
299         .name       = nxt_string("idle_timeout"),
300         .type       = NXT_CONF_VLDT_INTEGER,
301     }, {
302         .name       = nxt_string("body_buffer_size"),
303         .type       = NXT_CONF_VLDT_INTEGER,
304     }, {
305         .name       = nxt_string("max_body_size"),
306         .type       = NXT_CONF_VLDT_INTEGER,
307     }, {
308         .name       = nxt_string("body_temp_path"),
309         .type       = NXT_CONF_VLDT_STRING,
310     }, {
311         .name       = nxt_string("discard_unsafe_fields"),
312         .type       = NXT_CONF_VLDT_BOOLEAN,
313     }, {
314         .name       = nxt_string("websocket"),
315         .type       = NXT_CONF_VLDT_OBJECT,
316         .validator  = nxt_conf_vldt_object,
317         .u.members  = nxt_conf_vldt_websocket_members,
318     }, {
319         .name       = nxt_string("static"),
320         .type       = NXT_CONF_VLDT_OBJECT,
321         .validator  = nxt_conf_vldt_object,
322         .u.members  = nxt_conf_vldt_static_members,
323     },
324 
325     NXT_CONF_VLDT_END
326 };
327 
328 
329 static nxt_conf_vldt_object_t  nxt_conf_vldt_websocket_members[] = {
330     {
331         .name       = nxt_string("read_timeout"),
332         .type       = NXT_CONF_VLDT_INTEGER,
333     }, {
334 
335         .name       = nxt_string("keepalive_interval"),
336         .type       = NXT_CONF_VLDT_INTEGER,
337     }, {
338         .name       = nxt_string("max_frame_size"),
339         .type       = NXT_CONF_VLDT_INTEGER,
340     },
341 
342     NXT_CONF_VLDT_END
343 };
344 
345 
346 static nxt_conf_vldt_object_t  nxt_conf_vldt_static_members[] = {
347     {
348         .name       = nxt_string("mime_types"),
349         .type       = NXT_CONF_VLDT_OBJECT,
350         .validator  = nxt_conf_vldt_mtypes,
351     },
352 
353     NXT_CONF_VLDT_END
354 };
355 
356 
357 static nxt_conf_vldt_object_t  nxt_conf_vldt_listener_members[] = {
358     {
359         .name       = nxt_string("pass"),
360         .type       = NXT_CONF_VLDT_STRING,
361         .validator  = nxt_conf_vldt_pass,
362         .flags      = NXT_CONF_VLDT_VAR,
363     }, {
364         .name       = nxt_string("application"),
365         .type       = NXT_CONF_VLDT_STRING,
366         .validator  = nxt_conf_vldt_app_name,
367     }, {
368         .name       = nxt_string("client_ip"),
369         .type       = NXT_CONF_VLDT_OBJECT,
370         .validator  = nxt_conf_vldt_object,
371         .u.members  = nxt_conf_vldt_client_ip_members
372     },
373 
374 #if (NXT_TLS)
375     {
376         .name       = nxt_string("tls"),
377         .type       = NXT_CONF_VLDT_OBJECT,
378         .validator  = nxt_conf_vldt_object,
379         .u.members  = nxt_conf_vldt_tls_members,
380     },
381 #endif
382 
383     NXT_CONF_VLDT_END
384 };
385 
386 
387 static nxt_conf_vldt_object_t  nxt_conf_vldt_client_ip_members[] = {
388     {
389         .name       = nxt_string("source"),
390         .type       = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
391         .validator  = nxt_conf_vldt_match_addrs,
392         .flags      = NXT_CONF_VLDT_REQUIRED
393     }, {
394         .name       = nxt_string("header"),
395         .type       = NXT_CONF_VLDT_STRING,
396         .flags      = NXT_CONF_VLDT_REQUIRED
397     }, {
398         .name       = nxt_string("recursive"),
399         .type       = NXT_CONF_VLDT_BOOLEAN,
400     },
401 
402     NXT_CONF_VLDT_END
403 };
404 
405 
406 #if (NXT_TLS)
407 
408 static nxt_conf_vldt_object_t  nxt_conf_vldt_tls_members[] = {
409     {
410         .name       = nxt_string("certificate"),
411         .type       = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
412         .flags      = NXT_CONF_VLDT_REQUIRED,
413         .validator  = nxt_conf_vldt_certificate,
414     }, {
415         .name       = nxt_string("conf_commands"),
416         .type       = NXT_CONF_VLDT_OBJECT,
417 #if (NXT_HAVE_OPENSSL_CONF_CMD)
418         .validator  = nxt_conf_vldt_object_conf_commands,
419 #else
420         .validator  = nxt_conf_vldt_unsupported,
421         .u.string   = "conf_commands",
422 #endif
423     }, {
424         .name       = nxt_string("session"),
425         .type       = NXT_CONF_VLDT_OBJECT,
426         .validator  = nxt_conf_vldt_object,
427         .u.members  = nxt_conf_vldt_session_members,
428     },
429 
430     NXT_CONF_VLDT_END
431 };
432 
433 
434 static nxt_conf_vldt_object_t  nxt_conf_vldt_session_members[] = {
435     {
436         .name       = nxt_string("cache_size"),
437         .type       = NXT_CONF_VLDT_INTEGER,
438         .validator  = nxt_conf_vldt_tls_cache_size,
439     }, {
440         .name       = nxt_string("timeout"),
441         .type       = NXT_CONF_VLDT_INTEGER,
442         .validator  = nxt_conf_vldt_tls_timeout,
443     }, {
444         .name       = nxt_string("tickets"),
445         .type       = NXT_CONF_VLDT_STRING
446                      | NXT_CONF_VLDT_ARRAY
447                      | NXT_CONF_VLDT_BOOLEAN,
448 #if (NXT_HAVE_OPENSSL_TLSEXT)
449         .validator  = nxt_conf_vldt_ticket_key,
450 #else
451         .validator  = nxt_conf_vldt_unsupported,
452         .u.string   = "tickets",
453 #endif
454     },
455 
456     NXT_CONF_VLDT_END
457 };
458 
459 
460 static nxt_int_t
461 nxt_conf_vldt_tls_cache_size(nxt_conf_validation_t *vldt,
462     nxt_conf_value_t *value, void *data)
463 {
464     int64_t  cache_size;
465 
466     cache_size = nxt_conf_get_number(value);
467 
468     if (cache_size < 0) {
469         return nxt_conf_vldt_error(vldt, "The \"cache_size\" number must not "
470                                          "be negative.");
471     }
472 
473     return NXT_OK;
474 }
475 
476 
477 static nxt_int_t
478 nxt_conf_vldt_tls_timeout(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
479     void *data)
480 {
481     int64_t  timeout;
482 
483     timeout = nxt_conf_get_number(value);
484 
485     if (timeout <= 0) {
486         return nxt_conf_vldt_error(vldt, "The \"timeout\" number must be "
487                                          "greater than zero.");
488     }
489 
490     return NXT_OK;
491 }
492 
493 #endif
494 
495 #if (NXT_HAVE_OPENSSL_TLSEXT)
496 
497 static nxt_int_t
498 nxt_conf_vldt_ticket_key(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
499     void *data)
500 {
501     if (nxt_conf_type(value) == NXT_CONF_BOOLEAN) {
502         return NXT_OK;
503     }
504 
505     if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
506         return nxt_conf_vldt_array_iterator(vldt, value,
507                                             &nxt_conf_vldt_ticket_key_element);
508     }
509 
510     /* NXT_CONF_STRING */
511 
512     return nxt_conf_vldt_ticket_key_element(vldt, value);
513 }
514 
515 
516 static nxt_int_t
517 nxt_conf_vldt_ticket_key_element(nxt_conf_validation_t *vldt,
518     nxt_conf_value_t *value)
519 {
520     ssize_t    ret;
521     nxt_str_t  key;
522 
523     if (nxt_conf_type(value) != NXT_CONF_STRING) {
524         return nxt_conf_vldt_error(vldt, "The \"key\" array must "
525                                    "contain only string values.");
526     }
527 
528     nxt_conf_get_string(value, &key);
529 
530     ret = nxt_base64_decode(NULL, key.start, key.length);
531     if (ret == NXT_ERROR) {
532         return nxt_conf_vldt_error(vldt, "Invalid Base64 format for the ticket "
533                                    "key \"%V\".", &key);
534     }
535 
536     if (ret != 48 && ret != 80) {
537         return nxt_conf_vldt_error(vldt, "Invalid length %d of the ticket "
538                                    "key \"%V\".  Must be 48 or 80 bytes.",
539                                    ret, &key);
540     }
541 
542     return NXT_OK;
543 }
544 
545 #endif
546 
547 
548 static nxt_conf_vldt_object_t  nxt_conf_vldt_route_members[] = {
549     {
550         .name       = nxt_string("match"),
551         .type       = NXT_CONF_VLDT_OBJECT,
552         .validator  = nxt_conf_vldt_object,
553         .u.members  = nxt_conf_vldt_match_members,
554     }, {
555         .name       = nxt_string("action"),
556         .type       = NXT_CONF_VLDT_OBJECT,
557         .validator  = nxt_conf_vldt_action,
558     },
559 
560     NXT_CONF_VLDT_END
561 };
562 
563 
564 static nxt_conf_vldt_object_t  nxt_conf_vldt_match_members[] = {
565     {
566         .name       = nxt_string("method"),
567         .type       = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
568         .validator  = nxt_conf_vldt_match_patterns,
569         .u.string   = "method",
570     }, {
571         .name       = nxt_string("scheme"),
572         .type       = NXT_CONF_VLDT_STRING,
573         .validator  = nxt_conf_vldt_match_scheme_pattern,
574     }, {
575         .name       = nxt_string("host"),
576         .type       = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
577         .validator  = nxt_conf_vldt_match_patterns,
578         .u.string   = "host",
579     }, {
580         .name       = nxt_string("source"),
581         .type       = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
582         .validator  = nxt_conf_vldt_match_addrs,
583     }, {
584         .name       = nxt_string("destination"),
585         .type       = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
586         .validator  = nxt_conf_vldt_match_addrs,
587     }, {
588         .name       = nxt_string("uri"),
589         .type       = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
590         .validator  = nxt_conf_vldt_match_encoded_patterns,
591         .u.string   = "uri"
592     }, {
593         .name       = nxt_string("arguments"),
594         .type       = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY,
595         .validator  = nxt_conf_vldt_match_encoded_patterns_sets,
596     }, {
597         .name       = nxt_string("headers"),
598         .type       = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY,
599         .validator  = nxt_conf_vldt_match_patterns_sets,
600         .u.string   = "headers"
601     }, {
602         .name       = nxt_string("cookies"),
603         .type       = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY,
604         .validator  = nxt_conf_vldt_match_patterns_sets,
605         .u.string   = "cookies"
606     },
607 
608     NXT_CONF_VLDT_END
609 };
610 
611 
612 static nxt_conf_vldt_object_t  nxt_conf_vldt_pass_action_members[] = {
613     {
614         .name       = nxt_string("pass"),
615         .type       = NXT_CONF_VLDT_STRING,
616         .validator  = nxt_conf_vldt_pass,
617         .flags      = NXT_CONF_VLDT_VAR,
618     },
619 
620     NXT_CONF_VLDT_END
621 };
622 
623 
624 static nxt_conf_vldt_object_t  nxt_conf_vldt_return_action_members[] = {
625     {
626         .name       = nxt_string("return"),
627         .type       = NXT_CONF_VLDT_INTEGER,
628         .validator  = nxt_conf_vldt_return,
629     }, {
630         .name       = nxt_string("location"),
631         .type       = NXT_CONF_VLDT_STRING,
632     },
633 
634     NXT_CONF_VLDT_END
635 };
636 
637 
638 static nxt_conf_vldt_object_t  nxt_conf_vldt_share_action_members[] = {
639     {
640         .name       = nxt_string("share"),
641         .type       = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
642         .validator  = nxt_conf_vldt_share,
643     }, {
644         .name       = nxt_string("types"),
645         .type       = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
646         .validator  = nxt_conf_vldt_match_patterns,
647     }, {
648         .name       = nxt_string("fallback"),
649         .type       = NXT_CONF_VLDT_OBJECT,
650         .validator  = nxt_conf_vldt_action,
651     }, {
652         .name       = nxt_string("chroot"),
653         .type       = NXT_CONF_VLDT_STRING,
654 #if !(NXT_HAVE_OPENAT2)
655         .validator  = nxt_conf_vldt_unsupported,
656         .u.string   = "chroot",
657 #endif
658         .flags      = NXT_CONF_VLDT_VAR,
659     }, {
660         .name       = nxt_string("follow_symlinks"),
661         .type       = NXT_CONF_VLDT_BOOLEAN,
662 #if !(NXT_HAVE_OPENAT2)
663         .validator  = nxt_conf_vldt_unsupported,
664         .u.string   = "follow_symlinks",
665 #endif
666     }, {
667         .name       = nxt_string("traverse_mounts"),
668         .type       = NXT_CONF_VLDT_BOOLEAN,
669 #if !(NXT_HAVE_OPENAT2)
670         .validator  = nxt_conf_vldt_unsupported,
671         .u.string   = "traverse_mounts",
672 #endif
673     },
674 
675     NXT_CONF_VLDT_END
676 };
677 
678 
679 static nxt_conf_vldt_object_t  nxt_conf_vldt_proxy_action_members[] = {
680     {
681         .name       = nxt_string("proxy"),
682         .type       = NXT_CONF_VLDT_STRING,
683         .validator  = nxt_conf_vldt_proxy,
684     },
685 
686     NXT_CONF_VLDT_END
687 };
688 
689 
690 static nxt_conf_vldt_object_t  nxt_conf_vldt_external_members[] = {
691     {
692         .name       = nxt_string("executable"),
693         .type       = NXT_CONF_VLDT_STRING,
694         .flags      = NXT_CONF_VLDT_REQUIRED,
695     }, {
696         .name       = nxt_string("arguments"),
697         .type       = NXT_CONF_VLDT_ARRAY,
698         .validator  = nxt_conf_vldt_array_iterator,
699         .u.array    = nxt_conf_vldt_argument,
700     },
701 
702     NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
703 };
704 
705 
706 static nxt_conf_vldt_object_t  nxt_conf_vldt_python_common_members[] = {
707     {
708         .name       = nxt_string("home"),
709         .type       = NXT_CONF_VLDT_STRING,
710     }, {
711         .name       = nxt_string("path"),
712         .type       = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
713         .validator  = nxt_conf_vldt_python_path,
714     }, {
715         .name       = nxt_string("protocol"),
716         .type       = NXT_CONF_VLDT_STRING,
717         .validator  = nxt_conf_vldt_python_protocol,
718     }, {
719         .name       = nxt_string("threads"),
720         .type       = NXT_CONF_VLDT_INTEGER,
721         .validator  = nxt_conf_vldt_threads,
722     }, {
723         .name       = nxt_string("thread_stack_size"),
724         .type       = NXT_CONF_VLDT_INTEGER,
725         .validator  = nxt_conf_vldt_thread_stack_size,
726     },
727 
728     NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
729 };
730 
731 static nxt_conf_vldt_object_t  nxt_conf_vldt_python_members[] = {
732     {
733         .name       = nxt_string("module"),
734         .type       = NXT_CONF_VLDT_STRING,
735         .validator  = nxt_conf_vldt_targets_exclusive,
736         .u.string   = "module",
737     }, {
738         .name       = nxt_string("callable"),
739         .type       = NXT_CONF_VLDT_STRING,
740         .validator  = nxt_conf_vldt_targets_exclusive,
741         .u.string   = "callable",
742     }, {
743         .name       = nxt_string("targets"),
744         .type       = NXT_CONF_VLDT_OBJECT,
745         .validator  = nxt_conf_vldt_targets,
746         .u.members  = nxt_conf_vldt_python_target_members
747     },
748 
749     NXT_CONF_VLDT_NEXT(nxt_conf_vldt_python_common_members)
750 };
751 
752 
753 static nxt_conf_vldt_object_t  nxt_conf_vldt_python_target_members[] = {
754     {
755         .name       = nxt_string("module"),
756         .type       = NXT_CONF_VLDT_STRING,
757         .flags      = NXT_CONF_VLDT_REQUIRED,
758     }, {
759         .name       = nxt_string("callable"),
760         .type       = NXT_CONF_VLDT_STRING,
761     },
762 
763     NXT_CONF_VLDT_END
764 };
765 
766 
767 static nxt_conf_vldt_object_t  nxt_conf_vldt_python_notargets_members[] = {
768     {
769         .name       = nxt_string("module"),
770         .type       = NXT_CONF_VLDT_STRING,
771         .flags      = NXT_CONF_VLDT_REQUIRED,
772     }, {
773         .name       = nxt_string("callable"),
774         .type       = NXT_CONF_VLDT_STRING,
775     },
776 
777     NXT_CONF_VLDT_NEXT(nxt_conf_vldt_python_common_members)
778 };
779 
780 
781 static nxt_conf_vldt_object_t  nxt_conf_vldt_php_members[] = {
782     {
783         .name       = nxt_string("root"),
784         .type       = NXT_CONF_VLDT_ANY_TYPE,
785         .validator  = nxt_conf_vldt_targets_exclusive,
786         .u.string   = "root",
787     }, {
788         .name       = nxt_string("script"),
789         .type       = NXT_CONF_VLDT_ANY_TYPE,
790         .validator  = nxt_conf_vldt_targets_exclusive,
791         .u.string   = "script",
792     }, {
793         .name       = nxt_string("index"),
794         .type       = NXT_CONF_VLDT_ANY_TYPE,
795         .validator  = nxt_conf_vldt_targets_exclusive,
796         .u.string   = "index",
797     }, {
798         .name       = nxt_string("targets"),
799         .type       = NXT_CONF_VLDT_OBJECT,
800         .validator  = nxt_conf_vldt_targets,
801         .u.members  = nxt_conf_vldt_php_target_members
802     },
803 
804     NXT_CONF_VLDT_NEXT(nxt_conf_vldt_php_common_members)
805 };
806 
807 
808 static nxt_conf_vldt_object_t  nxt_conf_vldt_php_common_members[] = {
809     {
810         .name       = nxt_string("options"),
811         .type       = NXT_CONF_VLDT_OBJECT,
812         .validator  = nxt_conf_vldt_object,
813         .u.members  = nxt_conf_vldt_php_options_members,
814     },
815 
816     NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
817 };
818 
819 
820 static nxt_conf_vldt_object_t  nxt_conf_vldt_php_options_members[] = {
821     {
822         .name       = nxt_string("file"),
823         .type       = NXT_CONF_VLDT_STRING,
824     }, {
825         .name       = nxt_string("admin"),
826         .type       = NXT_CONF_VLDT_OBJECT,
827         .validator  = nxt_conf_vldt_object_iterator,
828         .u.object   = nxt_conf_vldt_php_option,
829     }, {
830         .name       = nxt_string("user"),
831         .type       = NXT_CONF_VLDT_OBJECT,
832         .validator  = nxt_conf_vldt_object_iterator,
833         .u.object   = nxt_conf_vldt_php_option,
834     },
835 
836     NXT_CONF_VLDT_END
837 };
838 
839 
840 static nxt_conf_vldt_object_t  nxt_conf_vldt_php_target_members[] = {
841     {
842         .name       = nxt_string("root"),
843         .type       = NXT_CONF_VLDT_STRING,
844         .flags      = NXT_CONF_VLDT_REQUIRED,
845     }, {
846         .name       = nxt_string("script"),
847         .type       = NXT_CONF_VLDT_STRING,
848     }, {
849         .name       = nxt_string("index"),
850         .type       = NXT_CONF_VLDT_STRING,
851     },
852 
853     NXT_CONF_VLDT_END
854 };
855 
856 
857 static nxt_conf_vldt_object_t  nxt_conf_vldt_php_notargets_members[] = {
858     {
859         .name       = nxt_string("root"),
860         .type       = NXT_CONF_VLDT_STRING,
861         .flags      = NXT_CONF_VLDT_REQUIRED,
862     }, {
863         .name       = nxt_string("script"),
864         .type       = NXT_CONF_VLDT_STRING,
865     }, {
866         .name       = nxt_string("index"),
867         .type       = NXT_CONF_VLDT_STRING,
868     },
869 
870     NXT_CONF_VLDT_NEXT(nxt_conf_vldt_php_common_members)
871 };
872 
873 
874 static nxt_conf_vldt_object_t  nxt_conf_vldt_perl_members[] = {
875     {
876         .name       = nxt_string("script"),
877         .type       = NXT_CONF_VLDT_STRING,
878         .flags      = NXT_CONF_VLDT_REQUIRED,
879     }, {
880         .name       = nxt_string("threads"),
881         .type       = NXT_CONF_VLDT_INTEGER,
882         .validator  = nxt_conf_vldt_threads,
883     }, {
884         .name       = nxt_string("thread_stack_size"),
885         .type       = NXT_CONF_VLDT_INTEGER,
886         .validator  = nxt_conf_vldt_thread_stack_size,
887     },
888 
889     NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
890 };
891 
892 
893 static nxt_conf_vldt_object_t  nxt_conf_vldt_ruby_members[] = {
894     {
895         .name       = nxt_string("script"),
896         .type       = NXT_CONF_VLDT_STRING,
897         .flags      = NXT_CONF_VLDT_REQUIRED,
898     }, {
899         .name       = nxt_string("threads"),
900         .type       = NXT_CONF_VLDT_INTEGER,
901         .validator  = nxt_conf_vldt_threads,
902     }, {
903         .name       = nxt_string("hooks"),
904         .type       = NXT_CONF_VLDT_STRING
905     },
906 
907     NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
908 };
909 
910 
911 static nxt_conf_vldt_object_t  nxt_conf_vldt_java_members[] = {
912     {
913         .name       = nxt_string("classpath"),
914         .type       = NXT_CONF_VLDT_ARRAY,
915         .validator  = nxt_conf_vldt_array_iterator,
916         .u.array    = nxt_conf_vldt_java_classpath,
917     }, {
918         .name       = nxt_string("webapp"),
919         .type       = NXT_CONF_VLDT_STRING,
920         .flags      = NXT_CONF_VLDT_REQUIRED,
921     }, {
922         .name       = nxt_string("options"),
923         .type       = NXT_CONF_VLDT_ARRAY,
924         .validator  = nxt_conf_vldt_array_iterator,
925         .u.array    = nxt_conf_vldt_java_option,
926     }, {
927         .name       = nxt_string("unit_jars"),
928         .type       = NXT_CONF_VLDT_STRING,
929     }, {
930         .name       = nxt_string("threads"),
931         .type       = NXT_CONF_VLDT_INTEGER,
932         .validator  = nxt_conf_vldt_threads,
933     }, {
934         .name       = nxt_string("thread_stack_size"),
935         .type       = NXT_CONF_VLDT_INTEGER,
936         .validator  = nxt_conf_vldt_thread_stack_size,
937     },
938 
939     NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members)
940 };
941 
942 
943 static nxt_conf_vldt_object_t  nxt_conf_vldt_common_members[] = {
944     {
945         .name       = nxt_string("type"),
946         .type       = NXT_CONF_VLDT_STRING,
947     }, {
948         .name       = nxt_string("limits"),
949         .type       = NXT_CONF_VLDT_OBJECT,
950         .validator  = nxt_conf_vldt_object,
951         .u.members  = nxt_conf_vldt_app_limits_members,
952     }, {
953         .name       = nxt_string("processes"),
954         .type       = NXT_CONF_VLDT_INTEGER | NXT_CONF_VLDT_OBJECT,
955         .validator  = nxt_conf_vldt_processes,
956         .u.members  = nxt_conf_vldt_app_processes_members,
957     }, {
958         .name       = nxt_string("user"),
959         .type       = NXT_CONF_VLDT_STRING,
960     }, {
961         .name       = nxt_string("group"),
962         .type       = NXT_CONF_VLDT_STRING,
963     }, {
964         .name       = nxt_string("working_directory"),
965         .type       = NXT_CONF_VLDT_STRING,
966     }, {
967         .name       = nxt_string("environment"),
968         .type       = NXT_CONF_VLDT_OBJECT,
969         .validator  = nxt_conf_vldt_object_iterator,
970         .u.object   = nxt_conf_vldt_environment,
971     }, {
972         .name       = nxt_string("isolation"),
973         .type       = NXT_CONF_VLDT_OBJECT,
974         .validator  = nxt_conf_vldt_isolation,
975         .u.members  = nxt_conf_vldt_app_isolation_members,
976     },
977 
978     NXT_CONF_VLDT_END
979 };
980 
981 
982 static nxt_conf_vldt_object_t  nxt_conf_vldt_app_limits_members[] = {
983     {
984         .name       = nxt_string("timeout"),
985         .type       = NXT_CONF_VLDT_INTEGER,
986     }, {
987         .name       = nxt_string("requests"),
988         .type       = NXT_CONF_VLDT_INTEGER,
989     }, {
990         .name       = nxt_string("shm"),
991         .type       = NXT_CONF_VLDT_INTEGER,
992     },
993 
994     NXT_CONF_VLDT_END
995 };
996 
997 
998 static nxt_conf_vldt_object_t  nxt_conf_vldt_app_processes_members[] = {
999     {
1000         .name       = nxt_string("spare"),
1001         .type       = NXT_CONF_VLDT_INTEGER,
1002     }, {
1003         .name       = nxt_string("max"),
1004         .type       = NXT_CONF_VLDT_INTEGER,
1005     }, {
1006         .name       = nxt_string("idle_timeout"),
1007         .type       = NXT_CONF_VLDT_INTEGER,
1008     },
1009 
1010     NXT_CONF_VLDT_END
1011 };
1012 
1013 
1014 static nxt_conf_vldt_object_t  nxt_conf_vldt_app_isolation_members[] = {
1015     {
1016         .name       = nxt_string("namespaces"),
1017         .type       = NXT_CONF_VLDT_OBJECT,
1018         .validator  = nxt_conf_vldt_clone_namespaces,
1019         .u.members  = nxt_conf_vldt_app_namespaces_members,
1020     },
1021 
1022 #if (NXT_HAVE_CLONE_NEWUSER)
1023     {
1024         .name       = nxt_string("uidmap"),
1025         .type       = NXT_CONF_VLDT_ARRAY,
1026         .validator  = nxt_conf_vldt_array_iterator,
1027         .u.array    = nxt_conf_vldt_clone_uidmap,
1028     }, {
1029         .name       = nxt_string("gidmap"),
1030         .type       = NXT_CONF_VLDT_ARRAY,
1031         .validator  = nxt_conf_vldt_array_iterator,
1032         .u.array    = nxt_conf_vldt_clone_gidmap,
1033     },
1034 #endif
1035 
1036 #if (NXT_HAVE_ISOLATION_ROOTFS)
1037     {
1038         .name       = nxt_string("rootfs"),
1039         .type       = NXT_CONF_VLDT_STRING,
1040     }, {
1041         .name       = nxt_string("automount"),
1042         .type       = NXT_CONF_VLDT_OBJECT,
1043         .validator  = nxt_conf_vldt_object,
1044         .u.members  = nxt_conf_vldt_app_automount_members,
1045     },
1046 #endif
1047 
1048 #if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
1049     {
1050         .name       = nxt_string("new_privs"),
1051         .type       = NXT_CONF_VLDT_BOOLEAN,
1052     },
1053 #endif
1054 
1055     NXT_CONF_VLDT_END
1056 };
1057 
1058 
1059 static nxt_conf_vldt_object_t  nxt_conf_vldt_app_namespaces_members[] = {
1060 
1061 #if (NXT_HAVE_CLONE_NEWUSER)
1062     {
1063         .name       = nxt_string("credential"),
1064         .type       = NXT_CONF_VLDT_BOOLEAN,
1065     },
1066 #endif
1067 
1068 #if (NXT_HAVE_CLONE_NEWPID)
1069     {
1070         .name       = nxt_string("pid"),
1071         .type       = NXT_CONF_VLDT_BOOLEAN,
1072     },
1073 #endif
1074 
1075 #if (NXT_HAVE_CLONE_NEWNET)
1076     {
1077         .name       = nxt_string("network"),
1078         .type       = NXT_CONF_VLDT_BOOLEAN,
1079     },
1080 #endif
1081 
1082 #if (NXT_HAVE_CLONE_NEWNS)
1083     {
1084         .name       = nxt_string("mount"),
1085         .type       = NXT_CONF_VLDT_BOOLEAN,
1086     },
1087 #endif
1088 
1089 #if (NXT_HAVE_CLONE_NEWUTS)
1090     {
1091         .name       = nxt_string("uname"),
1092         .type       = NXT_CONF_VLDT_BOOLEAN,
1093     },
1094 #endif
1095 
1096 #if (NXT_HAVE_CLONE_NEWCGROUP)
1097     {
1098         .name       = nxt_string("cgroup"),
1099         .type       = NXT_CONF_VLDT_BOOLEAN,
1100     },
1101 #endif
1102 
1103     NXT_CONF_VLDT_END
1104 };
1105 
1106 
1107 #if (NXT_HAVE_ISOLATION_ROOTFS)
1108 
1109 static nxt_conf_vldt_object_t  nxt_conf_vldt_app_automount_members[] = {
1110     {
1111         .name       = nxt_string("language_deps"),
1112         .type       = NXT_CONF_VLDT_BOOLEAN,
1113     }, {
1114         .name       = nxt_string("tmpfs"),
1115         .type       = NXT_CONF_VLDT_BOOLEAN,
1116     }, {
1117         .name       = nxt_string("procfs"),
1118         .type       = NXT_CONF_VLDT_BOOLEAN,
1119     },
1120 
1121     NXT_CONF_VLDT_END
1122 };
1123 
1124 #endif
1125 
1126 
1127 #if (NXT_HAVE_CLONE_NEWUSER)
1128 
1129 static nxt_conf_vldt_object_t nxt_conf_vldt_app_procmap_members[] = {
1130     {
1131         .name       = nxt_string("container"),
1132         .type       = NXT_CONF_VLDT_INTEGER,
1133     }, {
1134         .name       = nxt_string("host"),
1135         .type       = NXT_CONF_VLDT_INTEGER,
1136     }, {
1137         .name       = nxt_string("size"),
1138         .type       = NXT_CONF_VLDT_INTEGER,
1139     },
1140 
1141     NXT_CONF_VLDT_END
1142 };
1143 
1144 #endif
1145 
1146 
1147 static nxt_conf_vldt_object_t  nxt_conf_vldt_upstream_members[] = {
1148     {
1149         .name       = nxt_string("servers"),
1150         .type       = NXT_CONF_VLDT_OBJECT,
1151         .validator  = nxt_conf_vldt_object_iterator,
1152         .u.object   = nxt_conf_vldt_server,
1153     },
1154 
1155     NXT_CONF_VLDT_END
1156 };
1157 
1158 
1159 static nxt_conf_vldt_object_t  nxt_conf_vldt_upstream_server_members[] = {
1160     {
1161         .name       = nxt_string("weight"),
1162         .type       = NXT_CONF_VLDT_NUMBER,
1163         .validator  = nxt_conf_vldt_server_weight,
1164     },
1165 
1166     NXT_CONF_VLDT_END
1167 };
1168 
1169 
1170 nxt_int_t
1171 nxt_conf_validate(nxt_conf_validation_t *vldt)
1172 {
1173     nxt_int_t  ret;
1174 
1175     ret = nxt_conf_vldt_type(vldt, NULL, vldt->conf, NXT_CONF_VLDT_OBJECT);
1176     if (ret != NXT_OK) {
1177         return ret;
1178     }
1179 
1180     return nxt_conf_vldt_object(vldt, vldt->conf, nxt_conf_vldt_root_members);
1181 }
1182 
1183 
1184 #define NXT_CONF_VLDT_ANY_TYPE_STR                                            \
1185     "either a null, a boolean, an integer, "                                  \
1186     "a number, a string, an array, or an object"
1187 
1188 
1189 static nxt_int_t
1190 nxt_conf_vldt_type(nxt_conf_validation_t *vldt, nxt_str_t *name,
1191     nxt_conf_value_t *value, nxt_conf_vldt_type_t type)
1192 {
1193     u_char      *p;
1194     nxt_str_t   expected;
1195     nxt_bool_t  comma;
1196     nxt_uint_t  value_type, n, t;
1197     u_char      buf[nxt_length(NXT_CONF_VLDT_ANY_TYPE_STR)];
1198 
1199     static nxt_str_t  type_name[] = {
1200         nxt_string("a null"),
1201         nxt_string("a boolean"),
1202         nxt_string("an integer number"),
1203         nxt_string("a fractional number"),
1204         nxt_string("a string"),
1205         nxt_string("an array"),
1206         nxt_string("an object"),
1207     };
1208 
1209     value_type = nxt_conf_type(value);
1210 
1211     if ((1 << value_type) & type) {
1212         return NXT_OK;
1213     }
1214 
1215     p = buf;
1216 
1217     n = nxt_popcount(type);
1218 
1219     if (n > 1) {
1220         p = nxt_cpymem(p, "either ", 7);
1221     }
1222 
1223     comma = (n > 2);
1224 
1225     for ( ;; ) {
1226         t = __builtin_ffs(type) - 1;
1227 
1228         p = nxt_cpymem(p, type_name[t].start, type_name[t].length);
1229 
1230         n--;
1231 
1232         if (n == 0) {
1233             break;
1234         }
1235 
1236         if (comma) {
1237             *p++ = ',';
1238         }
1239 
1240         if (n == 1) {
1241             p = nxt_cpymem(p, " or", 3);
1242         }
1243 
1244         *p++ = ' ';
1245 
1246         type = type & ~(1 << t);
1247     }
1248 
1249     expected.length = p - buf;
1250     expected.start = buf;
1251 
1252     if (name == NULL) {
1253         return nxt_conf_vldt_error(vldt,
1254                                    "The configuration must be %V, but not %V.",
1255                                    &expected, &type_name[value_type]);
1256     }
1257 
1258     return nxt_conf_vldt_error(vldt,
1259                                "The \"%V\" value must be %V, but not %V.",
1260                                name, &expected, &type_name[value_type]);
1261 }
1262 
1263 
1264 static nxt_int_t
1265 nxt_conf_vldt_error(nxt_conf_validation_t *vldt, const char *fmt, ...)
1266 {
1267     u_char   *p, *end;
1268     size_t   size;
1269     va_list  args;
1270     u_char   error[NXT_MAX_ERROR_STR];
1271 
1272     va_start(args, fmt);
1273     end = nxt_vsprintf(error, error + NXT_MAX_ERROR_STR, fmt, args);
1274     va_end(args);
1275 
1276     size = end - error;
1277 
1278     p = nxt_mp_nget(vldt->pool, size);
1279     if (p == NULL) {
1280         return NXT_ERROR;
1281     }
1282 
1283     nxt_memcpy(p, error, size);
1284 
1285     vldt->error.length = size;
1286     vldt->error.start = p;
1287 
1288     return NXT_DECLINED;
1289 }
1290 
1291 
1292 nxt_inline nxt_int_t
1293 nxt_conf_vldt_unsupported(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
1294     void *data)
1295 {
1296     return nxt_conf_vldt_error(vldt, "Unit is built without the \"%s\" "
1297                                      "option support.", data);
1298 }
1299 
1300 
1301 static nxt_int_t
1302 nxt_conf_vldt_var(nxt_conf_validation_t *vldt, nxt_str_t *name,
1303     nxt_str_t *value)
1304 {
1305     u_char  error[NXT_MAX_ERROR_STR];
1306 
1307     if (nxt_var_test(value, error) != NXT_OK) {
1308         return nxt_conf_vldt_error(vldt, "%s in the \"%V\" value.",
1309                                    error, name);
1310     }
1311 
1312     return NXT_OK;
1313 }
1314 
1315 
1316 typedef struct {
1317     nxt_mp_t      *pool;
1318     nxt_str_t     *type;
1319     nxt_lvlhsh_t  hash;
1320 } nxt_conf_vldt_mtypes_ctx_t;
1321 
1322 
1323 static nxt_int_t
1324 nxt_conf_vldt_mtypes(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
1325     void *data)
1326 {
1327     nxt_int_t                   ret;
1328     nxt_conf_vldt_mtypes_ctx_t  ctx;
1329 
1330     ctx.pool = nxt_mp_create(1024, 128, 256, 32);
1331     if (nxt_slow_path(ctx.pool == NULL)) {
1332         return NXT_ERROR;
1333     }
1334 
1335     nxt_lvlhsh_init(&ctx.hash);
1336 
1337     vldt->ctx = &ctx;
1338 
1339     ret = nxt_conf_vldt_object_iterator(vldt, value,
1340                                         &nxt_conf_vldt_mtypes_type);
1341 
1342     vldt->ctx = NULL;
1343 
1344     nxt_mp_destroy(ctx.pool);
1345 
1346     return ret;
1347 }
1348 
1349 
1350 static nxt_int_t
1351 nxt_conf_vldt_mtypes_type(nxt_conf_validation_t *vldt, nxt_str_t *name,
1352     nxt_conf_value_t *value)
1353 {
1354     nxt_int_t                   ret;
1355     nxt_conf_vldt_mtypes_ctx_t  *ctx;
1356 
1357     ret = nxt_conf_vldt_type(vldt, name, value,
1358                              NXT_CONF_VLDT_STRING|NXT_CONF_VLDT_ARRAY);
1359     if (ret != NXT_OK) {
1360         return ret;
1361     }
1362 
1363     ctx = vldt->ctx;
1364 
1365     ctx->type = nxt_mp_get(ctx->pool, sizeof(nxt_str_t));
1366     if (nxt_slow_path(ctx->type == NULL)) {
1367         return NXT_ERROR;
1368     }
1369 
1370     *ctx->type = *name;
1371 
1372     if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
1373         return nxt_conf_vldt_array_iterator(vldt, value,
1374                                             &nxt_conf_vldt_mtypes_extension);
1375     }
1376 
1377     /* NXT_CONF_STRING */
1378 
1379     return nxt_conf_vldt_mtypes_extension(vldt, value);
1380 }
1381 
1382 
1383 static nxt_int_t
1384 nxt_conf_vldt_mtypes_extension(nxt_conf_validation_t *vldt,
1385     nxt_conf_value_t *value)
1386 {
1387     nxt_str_t                   exten, *dup_type;
1388     nxt_conf_vldt_mtypes_ctx_t  *ctx;
1389 
1390     ctx = vldt->ctx;
1391 
1392     if (nxt_conf_type(value) != NXT_CONF_STRING) {
1393         return nxt_conf_vldt_error(vldt, "The \"%V\" MIME type array must "
1394                                    "contain only strings.", ctx->type);
1395     }
1396 
1397     nxt_conf_get_string(value, &exten);
1398 
1399     if (exten.length == 0) {
1400         return nxt_conf_vldt_error(vldt, "An empty file extension for "
1401                                          "the \"%V\" MIME type.", ctx->type);
1402     }
1403 
1404     dup_type = nxt_http_static_mtype_get(&ctx->hash, &exten);
1405 
1406     if (dup_type->length != 0) {
1407         return nxt_conf_vldt_error(vldt, "The \"%V\" file extension has been "
1408                                          "declared for \"%V\" and \"%V\" "
1409                                          "MIME types at the same time.",
1410                                          &exten, dup_type, ctx->type);
1411     }
1412 
1413     return nxt_http_static_mtypes_hash_add(ctx->pool, &ctx->hash, &exten,
1414                                            ctx->type);
1415 }
1416 
1417 
1418 static nxt_int_t
1419 nxt_conf_vldt_listener(nxt_conf_validation_t *vldt, nxt_str_t *name,
1420     nxt_conf_value_t *value)
1421 {
1422     nxt_int_t       ret;
1423     nxt_sockaddr_t  *sa;
1424 
1425     sa = nxt_sockaddr_parse(vldt->pool, name);
1426     if (nxt_slow_path(sa == NULL)) {
1427         return nxt_conf_vldt_error(vldt,
1428                                    "The listener address \"%V\" is invalid.",
1429                                    name);
1430     }
1431 
1432     ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT);
1433     if (ret != NXT_OK) {
1434         return ret;
1435     }
1436 
1437     return nxt_conf_vldt_object(vldt, value, nxt_conf_vldt_listener_members);
1438 }
1439 
1440 
1441 static nxt_int_t
1442 nxt_conf_vldt_action(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
1443     void *data)
1444 {
1445     nxt_uint_t              i;
1446     nxt_conf_value_t        *action;
1447     nxt_conf_vldt_object_t  *members;
1448 
1449     static struct {
1450         nxt_str_t               name;
1451         nxt_conf_vldt_object_t  *members;
1452 
1453     } actions[] = {
1454         { nxt_string("pass"), nxt_conf_vldt_pass_action_members },
1455         { nxt_string("return"), nxt_conf_vldt_return_action_members },
1456         { nxt_string("share"), nxt_conf_vldt_share_action_members },
1457         { nxt_string("proxy"), nxt_conf_vldt_proxy_action_members },
1458     };
1459 
1460     members = NULL;
1461 
1462     for (i = 0; i < nxt_nitems(actions); i++) {
1463         action = nxt_conf_get_object_member(value, &actions[i].name, NULL);
1464 
1465         if (action == NULL) {
1466             continue;
1467         }
1468 
1469         if (members != NULL) {
1470             return nxt_conf_vldt_error(vldt, "The \"action\" object must have "
1471                                        "just one of \"pass\", \"return\", "
1472                                        "\"share\", or \"proxy\" options set.");
1473         }
1474 
1475         members = actions[i].members;
1476     }
1477 
1478     if (members == NULL) {
1479         return nxt_conf_vldt_error(vldt, "The \"action\" object must have "
1480                                    "either \"pass\", \"return\", \"share\", "
1481                                    "or \"proxy\" option set.");
1482     }
1483 
1484     return nxt_conf_vldt_object(vldt, value, members);
1485 }
1486 
1487 
1488 static nxt_int_t
1489 nxt_conf_vldt_pass(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
1490     void *data)
1491 {
1492     nxt_str_t  pass;
1493     nxt_int_t  ret;
1494     nxt_str_t  segments[3];
1495 
1496     static nxt_str_t  targets_str = nxt_string("targets");
1497 
1498     nxt_conf_get_string(value, &pass);
1499 
1500     ret = nxt_http_pass_segments(vldt->pool, &pass, segments, 3);
1501 
1502     if (ret != NXT_OK) {
1503         if (ret == NXT_DECLINED) {
1504             return nxt_conf_vldt_error(vldt, "Request \"pass\" value \"%V\" "
1505                                        "is invalid.", &pass);
1506         }
1507 
1508         return NXT_ERROR;
1509     }
1510 
1511     if (nxt_str_eq(&segments[0], "applications", 12)) {
1512 
1513         if (segments[1].length == 0) {
1514             goto error;
1515         }
1516 
1517         value = nxt_conf_get_object_member(vldt->conf, &segments[0], NULL);
1518 
1519         if (value == NULL) {
1520             goto error;
1521         }
1522 
1523         value = nxt_conf_get_object_member(value, &segments[1], NULL);
1524 
1525         if (value == NULL) {
1526             goto error;
1527         }
1528 
1529         if (segments[2].length > 0) {
1530             value = nxt_conf_get_object_member(value, &targets_str, NULL);
1531 
1532             if (value == NULL) {
1533                 goto error;
1534             }
1535 
1536             value = nxt_conf_get_object_member(value, &segments[2], NULL);
1537 
1538             if (value == NULL) {
1539                 goto error;
1540             }
1541         }
1542 
1543         return NXT_OK;
1544     }
1545 
1546     if (nxt_str_eq(&segments[0], "upstreams", 9)) {
1547 
1548         if (segments[1].length == 0 || segments[2].length != 0) {
1549             goto error;
1550         }
1551 
1552         value = nxt_conf_get_object_member(vldt->conf, &segments[0], NULL);
1553 
1554         if (value == NULL) {
1555             goto error;
1556         }
1557 
1558         value = nxt_conf_get_object_member(value, &segments[1], NULL);
1559 
1560         if (value == NULL) {
1561             goto error;
1562         }
1563 
1564         return NXT_OK;
1565     }
1566 
1567     if (nxt_str_eq(&segments[0], "routes", 6)) {
1568 
1569         if (segments[2].length != 0) {
1570             goto error;
1571         }
1572 
1573         value = nxt_conf_get_object_member(vldt->conf, &segments[0], NULL);
1574 
1575         if (value == NULL) {
1576             goto error;
1577         }
1578 
1579         if (segments[1].length == 0) {
1580             if (nxt_conf_type(value) != NXT_CONF_ARRAY) {
1581                 goto error;
1582             }
1583 
1584             return NXT_OK;
1585         }
1586 
1587         if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
1588             goto error;
1589         }
1590 
1591         value = nxt_conf_get_object_member(value, &segments[1], NULL);
1592 
1593         if (value == NULL) {
1594             goto error;
1595         }
1596 
1597         return NXT_OK;
1598     }
1599 
1600 error:
1601 
1602     return nxt_conf_vldt_error(vldt, "Request \"pass\" points to invalid "
1603                                "location \"%V\".", &pass);
1604 }
1605 
1606 
1607 static nxt_int_t
1608 nxt_conf_vldt_return(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
1609     void *data)
1610 {
1611     int64_t  status;
1612 
1613     status = nxt_conf_get_number(value);
1614 
1615     if (status < NXT_HTTP_INVALID || status > NXT_HTTP_STATUS_MAX) {
1616         return nxt_conf_vldt_error(vldt, "The \"return\" value is out of "
1617                                    "allowed HTTP status code range 0-999.");
1618     }
1619 
1620     return NXT_OK;
1621 }
1622 
1623 
1624 static nxt_int_t
1625 nxt_conf_vldt_share(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
1626     void *data)
1627 {
1628     u_char     *p;
1629     nxt_str_t  name, temp;
1630 
1631     static nxt_str_t  uri = nxt_string("$uri");
1632 
1633     if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
1634         if (nxt_conf_array_elements_count(value) == 0) {
1635             return nxt_conf_vldt_error(vldt, "The \"share\" array "
1636                                        "must contain at least one element.");
1637         }
1638 
1639         return nxt_conf_vldt_array_iterator(vldt, value,
1640                                             &nxt_conf_vldt_share_element);
1641     }
1642 
1643     /* NXT_CONF_STRING */
1644 
1645     if (vldt->ver < 12600) {
1646         nxt_conf_get_string(value, &name);
1647 
1648         temp.length = name.length + uri.length;
1649 
1650         temp.start = nxt_mp_get(vldt->conf_pool, temp.length);
1651         if (nxt_slow_path(temp.start == NULL)) {
1652             return NXT_ERROR;
1653         }
1654 
1655         p = nxt_cpymem(temp.start, name.start, name.length);
1656         nxt_memcpy(p, uri.start, uri.length);
1657 
1658         nxt_conf_set_string(value, &temp);
1659     }
1660 
1661     return nxt_conf_vldt_share_element(vldt, value);
1662 }
1663 
1664 
1665 static nxt_int_t
1666 nxt_conf_vldt_share_element(nxt_conf_validation_t *vldt,
1667     nxt_conf_value_t *value)
1668 {
1669     nxt_str_t  str;
1670 
1671     static nxt_str_t  share = nxt_string("share");
1672 
1673     if (nxt_conf_type(value) != NXT_CONF_STRING) {
1674         return nxt_conf_vldt_error(vldt, "The \"share\" array must "
1675                                    "contain only string values.");
1676     }
1677 
1678     nxt_conf_get_string(value, &str);
1679 
1680     if (nxt_is_var(&str)) {
1681         return nxt_conf_vldt_var(vldt, &share, &str);
1682     }
1683 
1684     return NXT_OK;
1685 }
1686 
1687 
1688 static nxt_int_t
1689 nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
1690     void *data)
1691 {
1692     nxt_str_t       name;
1693     nxt_sockaddr_t  *sa;
1694 
1695     nxt_conf_get_string(value, &name);
1696 
1697     if (nxt_str_start(&name, "http://", 7)) {
1698         name.length -= 7;
1699         name.start += 7;
1700 
1701         sa = nxt_sockaddr_parse(vldt->pool, &name);
1702         if (sa != NULL) {
1703             return NXT_OK;
1704         }
1705     }
1706 
1707     return nxt_conf_vldt_error(vldt, "The \"proxy\" address is invalid \"%V\"",
1708                                &name);
1709 }
1710 
1711 
1712 static nxt_int_t
1713 nxt_conf_vldt_python(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
1714     void *data)
1715 {
1716     nxt_conf_value_t  *targets;
1717 
1718     static nxt_str_t  targets_str = nxt_string("targets");
1719 
1720     targets = nxt_conf_get_object_member(value, &targets_str, NULL);
1721 
1722     if (targets != NULL) {
1723         return nxt_conf_vldt_object(vldt, value, nxt_conf_vldt_python_members);
1724     }
1725 
1726     return nxt_conf_vldt_object(vldt, value,
1727                                 nxt_conf_vldt_python_notargets_members);
1728 }
1729 
1730 
1731 static nxt_int_t
1732 nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt,
1733     nxt_conf_value_t *value, void *data)
1734 {
1735     if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
1736         return nxt_conf_vldt_array_iterator(vldt, value,
1737                                             &nxt_conf_vldt_python_path_element);
1738     }
1739 
1740     /* NXT_CONF_STRING */
1741 
1742     return NXT_OK;
1743 }
1744 
1745 
1746 static nxt_int_t
1747 nxt_conf_vldt_python_path_element(nxt_conf_validation_t *vldt,
1748     nxt_conf_value_t *value)
1749 {
1750     if (nxt_conf_type(value) != NXT_CONF_STRING) {
1751         return nxt_conf_vldt_error(vldt, "The \"path\" array must contain "
1752                                    "only string values.");
1753     }
1754 
1755     return NXT_OK;
1756 }
1757 
1758 
1759 static nxt_int_t
1760 nxt_conf_vldt_python_protocol(nxt_conf_validation_t *vldt,
1761     nxt_conf_value_t *value, void *data)
1762 {
1763     nxt_str_t  proto;
1764 
1765     static const nxt_str_t  wsgi = nxt_string("wsgi");
1766     static const nxt_str_t  asgi = nxt_string("asgi");
1767 
1768     nxt_conf_get_string(value, &proto);
1769 
1770     if (nxt_strstr_eq(&proto, &wsgi) || nxt_strstr_eq(&proto, &asgi)) {
1771         return NXT_OK;
1772     }
1773 
1774     return nxt_conf_vldt_error(vldt, "The \"protocol\" can either be "
1775                                      "\"wsgi\" or \"asgi\".");
1776 }
1777 
1778 
1779 static nxt_int_t
1780 nxt_conf_vldt_threads(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
1781     void *data)
1782 {
1783     int64_t  threads;
1784 
1785     threads = nxt_conf_get_number(value);
1786 
1787     if (threads < 1) {
1788         return nxt_conf_vldt_error(vldt, "The \"threads\" number must be "
1789                                    "equal to or greater than 1.");
1790     }
1791 
1792     if (threads > NXT_INT32_T_MAX) {
1793         return nxt_conf_vldt_error(vldt, "The \"threads\" number must "
1794                                    "not exceed %d.", NXT_INT32_T_MAX);
1795     }
1796 
1797     return NXT_OK;
1798 }
1799 
1800 
1801 static nxt_int_t
1802 nxt_conf_vldt_thread_stack_size(nxt_conf_validation_t *vldt,
1803     nxt_conf_value_t *value, void *data)
1804 {
1805     int64_t  size, min_size;
1806 
1807     size = nxt_conf_get_number(value);
1808     min_size = sysconf(_SC_THREAD_STACK_MIN);
1809 
1810     if (size < min_size) {
1811         return nxt_conf_vldt_error(vldt, "The \"thread_stack_size\" number "
1812                                    "must be equal to or greater than %d.",
1813                                    min_size);
1814     }
1815 
1816     if ((size % nxt_pagesize) != 0) {
1817         return nxt_conf_vldt_error(vldt, "The \"thread_stack_size\" number "
1818                              "must be a multiple of the system page size (%d).",
1819                              nxt_pagesize);
1820     }
1821 
1822     return NXT_OK;
1823 }
1824 
1825 
1826 static nxt_int_t
1827 nxt_conf_vldt_routes(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
1828     void *data)
1829 {
1830     if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
1831         return nxt_conf_vldt_array_iterator(vldt, value,
1832                                             &nxt_conf_vldt_route);
1833     }
1834 
1835     /* NXT_CONF_OBJECT */
1836 
1837     return nxt_conf_vldt_object_iterator(vldt, value,
1838                                          &nxt_conf_vldt_routes_member);
1839 }
1840 
1841 
1842 static nxt_int_t
1843 nxt_conf_vldt_routes_member(nxt_conf_validation_t *vldt, nxt_str_t *name,
1844     nxt_conf_value_t *value)
1845 {
1846     nxt_int_t  ret;
1847 
1848     ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_ARRAY);
1849 
1850     if (ret != NXT_OK) {
1851         return ret;
1852     }
1853 
1854     return nxt_conf_vldt_array_iterator(vldt, value, &nxt_conf_vldt_route);
1855 }
1856 
1857 
1858 static nxt_int_t
1859 nxt_conf_vldt_route(nxt_conf_validation_t *vldt, nxt_conf_value_t *value)
1860 {
1861     if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
1862         return nxt_conf_vldt_error(vldt, "The \"routes\" array must contain "
1863                                    "only object values.");
1864     }
1865 
1866     return nxt_conf_vldt_object(vldt, value, nxt_conf_vldt_route_members);
1867 }
1868 
1869 
1870 static nxt_int_t
1871 nxt_conf_vldt_match_patterns(nxt_conf_validation_t *vldt,
1872     nxt_conf_value_t *value, void *data)
1873 {
1874     nxt_int_t  ret;
1875 
1876     vldt->ctx = data;
1877 
1878     if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
1879         ret = nxt_conf_vldt_array_iterator(vldt, value,
1880                                            &nxt_conf_vldt_match_pattern);
1881 
1882     } else {
1883         /* NXT_CONF_STRING */
1884         ret = nxt_conf_vldt_match_pattern(vldt, value);
1885     }
1886 
1887     vldt->ctx = NULL;
1888 
1889     return ret;
1890 }
1891 
1892 
1893 static nxt_int_t
1894 nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt,
1895     nxt_conf_value_t *value)
1896 {
1897     nxt_str_t        pattern;
1898     nxt_uint_t       i, first, last;
1899 #if (NXT_HAVE_REGEX)
1900     nxt_regex_t      *re;
1901     nxt_regex_err_t  err;
1902 #endif
1903 
1904     if (nxt_conf_type(value) != NXT_CONF_STRING) {
1905         return nxt_conf_vldt_error(vldt, "The \"match\" pattern for \"%s\" "
1906                                    "must be strings.", vldt->ctx);
1907     }
1908 
1909     nxt_conf_get_string(value, &pattern);
1910 
1911     if (pattern.length == 0) {
1912         return NXT_OK;
1913     }
1914 
1915     first = (pattern.start[0] == '!');
1916 
1917     if (first < pattern.length && pattern.start[first] == '~') {
1918 #if (NXT_HAVE_REGEX)
1919         pattern.start += first + 1;
1920         pattern.length -= first + 1;
1921 
1922         re = nxt_regex_compile(vldt->pool, &pattern, &err);
1923         if (nxt_slow_path(re == NULL)) {
1924             if (err.offset < pattern.length) {
1925                 return nxt_conf_vldt_error(vldt, "Invalid regular expression: "
1926                                            "%s at offset %d",
1927                                            err.msg, err.offset);
1928             }
1929 
1930             return nxt_conf_vldt_error(vldt, "Invalid regular expression: %s",
1931                                        err.msg);
1932         }
1933 
1934         return NXT_OK;
1935 #else
1936         return nxt_conf_vldt_error(vldt, "Unit is built without support of "
1937                                    "regular expressions: \"--no-regex\" "
1938                                    "./configure option was set.");
1939 #endif
1940     }
1941 
1942     last = pattern.length - 1;
1943 
1944     for (i = first; i < last; i++) {
1945         if (pattern.start[i] == '*' && pattern.start[i + 1] == '*') {
1946             return nxt_conf_vldt_error(vldt, "The \"match\" pattern must "
1947                                        "not contain double \"*\" markers.");
1948         }
1949     }
1950 
1951     return NXT_OK;
1952 }
1953 
1954 
1955 static nxt_int_t nxt_conf_vldt_match_encoded_patterns_sets(
1956     nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data)
1957 {
1958     if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
1959         return nxt_conf_vldt_array_iterator(vldt, value,
1960                                      &nxt_conf_vldt_match_encoded_patterns_set);
1961     }
1962 
1963     /* NXT_CONF_OBJECT */
1964 
1965     return nxt_conf_vldt_match_encoded_patterns_set(vldt, value);
1966 }
1967 
1968 
1969 static nxt_int_t nxt_conf_vldt_match_encoded_patterns_set(
1970     nxt_conf_validation_t *vldt, nxt_conf_value_t *value)
1971 {
1972     if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
1973         return nxt_conf_vldt_error(vldt, "The \"match\" pattern for "
1974                                    "\"arguments\" must be an object.");
1975     }
1976 
1977     return nxt_conf_vldt_object_iterator(vldt, value,
1978                               &nxt_conf_vldt_match_encoded_patterns_set_member);
1979 }
1980 
1981 
1982 static nxt_int_t
1983 nxt_conf_vldt_match_encoded_patterns_set_member(nxt_conf_validation_t *vldt,
1984     nxt_str_t *name, nxt_conf_value_t *value)
1985 {
1986     u_char  *p, *end;
1987 
1988     if (nxt_slow_path(name->length == 0)) {
1989         return nxt_conf_vldt_error(vldt, "The \"match\" pattern objects must "
1990                                    "not contain empty member names.");
1991     }
1992 
1993     p = nxt_mp_nget(vldt->pool, name->length);
1994     if (nxt_slow_path(p == NULL)) {
1995         return NXT_ERROR;
1996     }
1997 
1998     end = nxt_decode_uri(p, name->start, name->length);
1999     if (nxt_slow_path(end == NULL)) {
2000         return nxt_conf_vldt_error(vldt, "The \"match\" pattern for "
2001                                    "\"arguments\" is encoded but is invalid.");
2002     }
2003 
2004     return nxt_conf_vldt_match_encoded_patterns(vldt, value,
2005                                                 (void *) "arguments");
2006 }
2007 
2008 
2009 static nxt_int_t
2010 nxt_conf_vldt_match_encoded_patterns(nxt_conf_validation_t *vldt,
2011     nxt_conf_value_t *value, void *data)
2012 {
2013     nxt_int_t  ret;
2014 
2015     vldt->ctx = data;
2016 
2017     if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
2018         ret = nxt_conf_vldt_array_iterator(vldt, value,
2019                                           &nxt_conf_vldt_match_encoded_pattern);
2020 
2021     } else {
2022         /* NXT_CONF_STRING */
2023         ret = nxt_conf_vldt_match_encoded_pattern(vldt, value);
2024     }
2025 
2026     vldt->ctx = NULL;
2027 
2028     return ret;
2029 }
2030 
2031 
2032 static nxt_int_t
2033 nxt_conf_vldt_match_encoded_pattern(nxt_conf_validation_t *vldt,
2034     nxt_conf_value_t *value)
2035 {
2036     u_char     *p, *end;
2037     nxt_int_t  ret;
2038     nxt_str_t  pattern;
2039 
2040     if (nxt_conf_type(value) != NXT_CONF_STRING) {
2041         return nxt_conf_vldt_error(vldt, "The \"match\" pattern for \"%s\" "
2042                                    "must be a string.", vldt->ctx);
2043     }
2044 
2045     ret = nxt_conf_vldt_match_pattern(vldt, value);
2046     if (nxt_slow_path(ret != NXT_OK)) {
2047         return ret;
2048     }
2049 
2050     nxt_conf_get_string(value, &pattern);
2051 
2052     p = nxt_mp_nget(vldt->pool, pattern.length);
2053     if (nxt_slow_path(p == NULL)) {
2054         return NXT_ERROR;
2055     }
2056 
2057     end = nxt_decode_uri(p, pattern.start, pattern.length);
2058     if (nxt_slow_path(end == NULL)) {
2059         return nxt_conf_vldt_error(vldt, "The \"match\" pattern for \"%s\" "
2060                                    "is encoded but is invalid.", vldt->ctx);
2061     }
2062 
2063     return NXT_OK;
2064 }
2065 
2066 
2067 static nxt_int_t
2068 nxt_conf_vldt_match_addrs(nxt_conf_validation_t *vldt,
2069     nxt_conf_value_t *value, void *data)
2070 {
2071     if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
2072         return nxt_conf_vldt_array_iterator(vldt, value,
2073                                             &nxt_conf_vldt_match_addr);
2074     }
2075 
2076     return nxt_conf_vldt_match_addr(vldt, value);
2077 }
2078 
2079 
2080 static nxt_int_t
2081 nxt_conf_vldt_match_addr(nxt_conf_validation_t *vldt,
2082     nxt_conf_value_t *value)
2083 {
2084     nxt_http_route_addr_pattern_t  pattern;
2085 
2086     switch (nxt_http_route_addr_pattern_parse(vldt->pool, &pattern, value)) {
2087 
2088     case NXT_OK:
2089         return NXT_OK;
2090 
2091     case NXT_ADDR_PATTERN_PORT_ERROR:
2092         return nxt_conf_vldt_error(vldt, "The \"address\" port an invalid "
2093                                          "port.");
2094 
2095     case NXT_ADDR_PATTERN_CV_TYPE_ERROR:
2096         return nxt_conf_vldt_error(vldt, "The \"match\" pattern for "
2097                                          "\"address\" must be a string.");
2098 
2099     case NXT_ADDR_PATTERN_LENGTH_ERROR:
2100         return nxt_conf_vldt_error(vldt, "The \"address\" is too short.");
2101 
2102     case NXT_ADDR_PATTERN_FORMAT_ERROR:
2103         return nxt_conf_vldt_error(vldt, "The \"address\" format is invalid.");
2104 
2105     case NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR:
2106         return nxt_conf_vldt_error(vldt, "The \"address\" range is "
2107                                          "overlapping.");
2108 
2109     case NXT_ADDR_PATTERN_CIDR_ERROR:
2110         return nxt_conf_vldt_error(vldt, "The \"address\" has an invalid CIDR "
2111                                          "prefix.");
2112 
2113     case NXT_ADDR_PATTERN_NO_IPv6_ERROR:
2114         return nxt_conf_vldt_error(vldt, "The \"address\" does not support "
2115                                          "IPv6 with your configuration.");
2116 
2117     default:
2118         return nxt_conf_vldt_error(vldt, "The \"address\" has an unknown "
2119                                          "format.");
2120     }
2121 }
2122 
2123 
2124 static nxt_int_t
2125 nxt_conf_vldt_match_scheme_pattern(nxt_conf_validation_t *vldt,
2126     nxt_conf_value_t *value, void *data)
2127 {
2128     nxt_str_t  scheme;
2129 
2130     static const nxt_str_t  http = nxt_string("http");
2131     static const nxt_str_t  https = nxt_string("https");
2132 
2133     nxt_conf_get_string(value, &scheme);
2134 
2135     if (nxt_strcasestr_eq(&scheme, &http)
2136         || nxt_strcasestr_eq(&scheme, &https))
2137     {
2138         return NXT_OK;
2139     }
2140 
2141     return nxt_conf_vldt_error(vldt, "The \"scheme\" can either be "
2142                                      "\"http\" or \"https\".");
2143 }
2144 
2145 
2146 static nxt_int_t
2147 nxt_conf_vldt_match_patterns_sets(nxt_conf_validation_t *vldt,
2148     nxt_conf_value_t *value, void *data)
2149 {
2150     nxt_int_t  ret;
2151 
2152     vldt->ctx = data;
2153 
2154     if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
2155         ret = nxt_conf_vldt_array_iterator(vldt, value,
2156                                            &nxt_conf_vldt_match_patterns_set);
2157 
2158     } else {
2159         /* NXT_CONF_OBJECT */
2160         ret = nxt_conf_vldt_match_patterns_set(vldt, value);
2161     }
2162 
2163     vldt->ctx = NULL;
2164 
2165     return ret;
2166 }
2167 
2168 
2169 static nxt_int_t
2170 nxt_conf_vldt_match_patterns_set(nxt_conf_validation_t *vldt,
2171     nxt_conf_value_t *value)
2172 {
2173     if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
2174         return nxt_conf_vldt_error(vldt, "The \"match\" patterns for "
2175                                    "\"%s\" must be objects.", vldt->ctx);
2176     }
2177 
2178     return nxt_conf_vldt_object_iterator(vldt, value,
2179                                      &nxt_conf_vldt_match_patterns_set_member);
2180 }
2181 
2182 
2183 static nxt_int_t
2184 nxt_conf_vldt_match_patterns_set_member(nxt_conf_validation_t *vldt,
2185     nxt_str_t *name, nxt_conf_value_t *value)
2186 {
2187     if (name->length == 0) {
2188         return nxt_conf_vldt_error(vldt, "The \"match\" pattern objects must "
2189                                    "not contain empty member names.");
2190     }
2191 
2192     return nxt_conf_vldt_match_patterns(vldt, value, vldt->ctx);
2193 }
2194 
2195 
2196 #if (NXT_TLS)
2197 
2198 static nxt_int_t
2199 nxt_conf_vldt_certificate(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
2200     void *data)
2201 {
2202     if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
2203         if (nxt_conf_array_elements_count(value) == 0) {
2204             return nxt_conf_vldt_error(vldt, "The \"certificate\" array "
2205                                        "must contain at least one element.");
2206         }
2207 
2208         return nxt_conf_vldt_array_iterator(vldt, value,
2209                                             &nxt_conf_vldt_certificate_element);
2210     }
2211 
2212     /* NXT_CONF_STRING */
2213 
2214     return nxt_conf_vldt_certificate_element(vldt, value);
2215 }
2216 
2217 
2218 static nxt_int_t
2219 nxt_conf_vldt_certificate_element(nxt_conf_validation_t *vldt,
2220     nxt_conf_value_t *value)
2221 {
2222     nxt_str_t         name;
2223     nxt_conf_value_t  *cert;
2224 
2225     if (nxt_conf_type(value) != NXT_CONF_STRING) {
2226         return nxt_conf_vldt_error(vldt, "The \"certificate\" array must "
2227                                    "contain only string values.");
2228     }
2229 
2230     nxt_conf_get_string(value, &name);
2231 
2232     cert = nxt_cert_info_get(&name);
2233 
2234     if (cert == NULL) {
2235         return nxt_conf_vldt_error(vldt, "Certificate \"%V\" is not found.",
2236                                    &name);
2237     }
2238 
2239     return NXT_OK;
2240 }
2241 
2242 
2243 #if (NXT_HAVE_OPENSSL_CONF_CMD)
2244 
2245 static nxt_int_t
2246 nxt_conf_vldt_object_conf_commands(nxt_conf_validation_t *vldt,
2247     nxt_conf_value_t *value, void *data)
2248 {
2249     uint32_t          index;
2250     nxt_int_t         ret;
2251     nxt_str_t         name;
2252     nxt_conf_value_t  *member;
2253 
2254     index = 0;
2255 
2256     for ( ;; ) {
2257         member = nxt_conf_next_object_member(value, &name, &index);
2258 
2259         if (member == NULL) {
2260             break;
2261         }
2262 
2263         ret = nxt_conf_vldt_type(vldt, &name, member, NXT_CONF_VLDT_STRING);
2264         if (ret != NXT_OK) {
2265             return ret;
2266         }
2267     }
2268 
2269     return NXT_OK;
2270 }
2271 
2272 #endif
2273 
2274 #endif
2275 
2276 
2277 static nxt_int_t
2278 nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
2279     void *data)
2280 {
2281     nxt_str_t         name;
2282     nxt_conf_value_t  *apps, *app;
2283 
2284     static nxt_str_t  apps_str = nxt_string("applications");
2285 
2286     nxt_conf_get_string(value, &name);
2287 
2288     apps = nxt_conf_get_object_member(vldt->conf, &apps_str, NULL);
2289 
2290     if (nxt_slow_path(apps == NULL)) {
2291         goto error;
2292     }
2293 
2294     app = nxt_conf_get_object_member(apps, &name, NULL);
2295 
2296     if (nxt_slow_path(app == NULL)) {
2297         goto error;
2298     }
2299 
2300     return NXT_OK;
2301 
2302 error:
2303 
2304     return nxt_conf_vldt_error(vldt, "Listening socket is assigned for "
2305                                      "a non existing application \"%V\".",
2306                                      &name);
2307 }
2308 
2309 
2310 static nxt_int_t
2311 nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name,
2312     nxt_conf_value_t *value)
2313 {
2314     nxt_int_t              ret;
2315     nxt_str_t              type;
2316     nxt_thread_t           *thread;
2317     nxt_conf_value_t       *type_value;
2318     nxt_app_lang_module_t  *lang;
2319 
2320     static nxt_str_t  type_str = nxt_string("type");
2321 
2322     static struct {
2323         nxt_conf_vldt_handler_t  validator;
2324         nxt_conf_vldt_object_t   *members;
2325 
2326     } types[] = {
2327         { nxt_conf_vldt_object, nxt_conf_vldt_external_members },
2328         { nxt_conf_vldt_python, NULL },
2329         { nxt_conf_vldt_php,    NULL },
2330         { nxt_conf_vldt_object, nxt_conf_vldt_perl_members },
2331         { nxt_conf_vldt_object, nxt_conf_vldt_ruby_members },
2332         { nxt_conf_vldt_object, nxt_conf_vldt_java_members },
2333     };
2334 
2335     ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT);
2336 
2337     if (ret != NXT_OK) {
2338         return ret;
2339     }
2340 
2341     type_value = nxt_conf_get_object_member(value, &type_str, NULL);
2342 
2343     if (type_value == NULL) {
2344         return nxt_conf_vldt_error(vldt,
2345                            "Application must have the \"type\" property set.");
2346     }
2347 
2348     ret = nxt_conf_vldt_type(vldt, &type_str, type_value, NXT_CONF_VLDT_STRING);
2349 
2350     if (ret != NXT_OK) {
2351         return ret;
2352     }
2353 
2354     nxt_conf_get_string(type_value, &type);
2355 
2356     thread = nxt_thread();
2357 
2358     lang = nxt_app_lang_module(thread->runtime, &type);
2359     if (lang == NULL) {
2360         return nxt_conf_vldt_error(vldt,
2361                                    "The module to run \"%V\" is not found "
2362                                    "among the available application modules.",
2363                                    &type);
2364     }
2365 
2366     return types[lang->type].validator(vldt, value, types[lang->type].members);
2367 }
2368 
2369 
2370 static nxt_int_t
2371 nxt_conf_vldt_object(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
2372     void *data)
2373 {
2374     uint32_t                index;
2375     nxt_int_t               ret;
2376     nxt_str_t               name, var;
2377     nxt_conf_value_t        *member;
2378     nxt_conf_vldt_object_t  *vals;
2379 
2380     vals = data;
2381 
2382     for ( ;; ) {
2383         if (vals->name.length == 0) {
2384 
2385             if (vals->u.members != NULL) {
2386                 vals = vals->u.members;
2387                 continue;
2388             }
2389 
2390             break;
2391         }
2392 
2393         if (vals->flags & NXT_CONF_VLDT_REQUIRED) {
2394             member = nxt_conf_get_object_member(value, &vals->name, NULL);
2395 
2396             if (member == NULL) {
2397                 return nxt_conf_vldt_error(vldt, "Required parameter \"%V\" "
2398                                            "is missing.", &vals->name);
2399             }
2400         }
2401 
2402         vals++;
2403     }
2404 
2405     index = 0;
2406 
2407     for ( ;; ) {
2408         member = nxt_conf_next_object_member(value, &name, &index);
2409 
2410         if (member == NULL) {
2411             return NXT_OK;
2412         }
2413 
2414         vals = data;
2415 
2416         for ( ;; ) {
2417             if (vals->name.length == 0) {
2418 
2419                 if (vals->u.members != NULL) {
2420                     vals = vals->u.members;
2421                     continue;
2422                 }
2423 
2424                 return nxt_conf_vldt_error(vldt, "Unknown parameter \"%V\".",
2425                                            &name);
2426             }
2427 
2428             if (!nxt_strstr_eq(&vals->name, &name)) {
2429                 vals++;
2430                 continue;
2431             }
2432 
2433             if (vals->flags & NXT_CONF_VLDT_VAR
2434                 && nxt_conf_type(member) == NXT_CONF_STRING)
2435             {
2436                 nxt_conf_get_string(member, &var);
2437 
2438                 if (nxt_is_var(&var)) {
2439                     ret = nxt_conf_vldt_var(vldt, &name, &var);
2440                     if (ret != NXT_OK) {
2441                         return ret;
2442                     }
2443 
2444                     break;
2445                 }
2446            }
2447 
2448             ret = nxt_conf_vldt_type(vldt, &name, member, vals->type);
2449             if (ret != NXT_OK) {
2450                 return ret;
2451             }
2452 
2453             if (vals->validator != NULL) {
2454                 ret = vals->validator(vldt, member, vals->u.members);
2455 
2456                 if (ret != NXT_OK) {
2457                     return ret;
2458                 }
2459             }
2460 
2461             break;
2462         }
2463     }
2464 }
2465 
2466 
2467 typedef struct {
2468     int64_t  spare;
2469     int64_t  max;
2470     int64_t  idle_timeout;
2471 } nxt_conf_vldt_processes_conf_t;
2472 
2473 
2474 static nxt_conf_map_t  nxt_conf_vldt_processes_conf_map[] = {
2475     {
2476         nxt_string("spare"),
2477         NXT_CONF_MAP_INT64,
2478         offsetof(nxt_conf_vldt_processes_conf_t, spare),
2479     },
2480 
2481     {
2482         nxt_string("max"),
2483         NXT_CONF_MAP_INT64,
2484         offsetof(nxt_conf_vldt_processes_conf_t, max),
2485     },
2486 
2487     {
2488         nxt_string("idle_timeout"),
2489         NXT_CONF_MAP_INT64,
2490         offsetof(nxt_conf_vldt_processes_conf_t, idle_timeout),
2491     },
2492 };
2493 
2494 
2495 static nxt_int_t
2496 nxt_conf_vldt_processes(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
2497     void *data)
2498 {
2499     int64_t                         int_value;
2500     nxt_int_t                       ret;
2501     nxt_conf_vldt_processes_conf_t  proc;
2502 
2503     if (nxt_conf_type(value) == NXT_CONF_NUMBER) {
2504         int_value = nxt_conf_get_number(value);
2505 
2506         if (int_value < 1) {
2507             return nxt_conf_vldt_error(vldt, "The \"processes\" number must be "
2508                                        "equal to or greater than 1.");
2509         }
2510 
2511         if (int_value > NXT_INT32_T_MAX) {
2512             return nxt_conf_vldt_error(vldt, "The \"processes\" number must "
2513                                        "not exceed %d.", NXT_INT32_T_MAX);
2514         }
2515 
2516         return NXT_OK;
2517     }
2518 
2519     ret = nxt_conf_vldt_object(vldt, value, data);
2520     if (ret != NXT_OK) {
2521         return ret;
2522     }
2523 
2524     proc.spare = 0;
2525     proc.max = 1;
2526     proc.idle_timeout = 15;
2527 
2528     ret = nxt_conf_map_object(vldt->pool, value,
2529                               nxt_conf_vldt_processes_conf_map,
2530                               nxt_nitems(nxt_conf_vldt_processes_conf_map),
2531                               &proc);
2532     if (ret != NXT_OK) {
2533         return ret;
2534     }
2535 
2536     if (proc.spare < 0) {
2537         return nxt_conf_vldt_error(vldt, "The \"spare\" number must not be "
2538                                    "negative.");
2539     }
2540 
2541     if (proc.spare > NXT_INT32_T_MAX) {
2542         return nxt_conf_vldt_error(vldt, "The \"spare\" number must not "
2543                                    "exceed %d.", NXT_INT32_T_MAX);
2544     }
2545 
2546     if (proc.max < 1) {
2547         return nxt_conf_vldt_error(vldt, "The \"max\" number must be equal "
2548                                    "to or greater than 1.");
2549     }
2550 
2551     if (proc.max > NXT_INT32_T_MAX) {
2552         return nxt_conf_vldt_error(vldt, "The \"max\" number must not "
2553                                    "exceed %d.", NXT_INT32_T_MAX);
2554     }
2555 
2556     if (proc.max < proc.spare) {
2557         return nxt_conf_vldt_error(vldt, "The \"spare\" number must be "
2558                                    "less than or equal to \"max\".");
2559     }
2560 
2561     if (proc.idle_timeout < 0) {
2562         return nxt_conf_vldt_error(vldt, "The \"idle_timeout\" number must not "
2563                                    "be negative.");
2564     }
2565 
2566     if (proc.idle_timeout > NXT_INT32_T_MAX / 1000) {
2567         return nxt_conf_vldt_error(vldt, "The \"idle_timeout\" number must not "
2568                                    "exceed %d.", NXT_INT32_T_MAX / 1000);
2569     }
2570 
2571     return NXT_OK;
2572 }
2573 
2574 
2575 static nxt_int_t
2576 nxt_conf_vldt_object_iterator(nxt_conf_validation_t *vldt,
2577     nxt_conf_value_t *value, void *data)
2578 {
2579     uint32_t                index;
2580     nxt_int_t               ret;
2581     nxt_str_t               name;
2582     nxt_conf_value_t        *member;
2583     nxt_conf_vldt_member_t  validator;
2584 
2585     validator = (nxt_conf_vldt_member_t) data;
2586     index = 0;
2587 
2588     for ( ;; ) {
2589         member = nxt_conf_next_object_member(value, &name, &index);
2590 
2591         if (member == NULL) {
2592             return NXT_OK;
2593         }
2594 
2595         ret = validator(vldt, &name, member);
2596 
2597         if (ret != NXT_OK) {
2598             return ret;
2599         }
2600     }
2601 }
2602 
2603 
2604 static nxt_int_t
2605 nxt_conf_vldt_array_iterator(nxt_conf_validation_t *vldt,
2606     nxt_conf_value_t *value, void *data)
2607 {
2608     uint32_t                 index;
2609     nxt_int_t                ret;
2610     nxt_conf_value_t         *element;
2611     nxt_conf_vldt_element_t  validator;
2612 
2613     validator = (nxt_conf_vldt_element_t) data;
2614 
2615     for (index = 0; /* void */ ; index++) {
2616         element = nxt_conf_get_array_element(value, index);
2617 
2618         if (element == NULL) {
2619             return NXT_OK;
2620         }
2621 
2622         ret = validator(vldt, element);
2623 
2624         if (ret != NXT_OK) {
2625             return ret;
2626         }
2627     }
2628 }
2629 
2630 
2631 static nxt_int_t
2632 nxt_conf_vldt_environment(nxt_conf_validation_t *vldt, nxt_str_t *name,
2633     nxt_conf_value_t *value)
2634 {
2635     nxt_str_t  str;
2636 
2637     if (name->length == 0) {
2638         return nxt_conf_vldt_error(vldt,
2639                                    "The environment name must not be empty.");
2640     }
2641 
2642     if (nxt_memchr(name->start, '\0', name->length) != NULL) {
2643         return nxt_conf_vldt_error(vldt, "The environment name must not "
2644                                    "contain null character.");
2645     }
2646 
2647     if (nxt_memchr(name->start, '=', name->length) != NULL) {
2648         return nxt_conf_vldt_error(vldt, "The environment name must not "
2649                                    "contain '=' character.");
2650     }
2651 
2652     if (nxt_conf_type(value) != NXT_CONF_STRING) {
2653         return nxt_conf_vldt_error(vldt, "The \"%V\" environment value must be "
2654                                    "a string.", name);
2655     }
2656 
2657     nxt_conf_get_string(value, &str);
2658 
2659     if (nxt_memchr(str.start, '\0', str.length) != NULL) {
2660         return nxt_conf_vldt_error(vldt, "The \"%V\" environment value must "
2661                                    "not contain null character.", name);
2662     }
2663 
2664     return NXT_OK;
2665 }
2666 
2667 
2668 static nxt_int_t
2669 nxt_conf_vldt_targets_exclusive(nxt_conf_validation_t *vldt,
2670     nxt_conf_value_t *value, void *data)
2671 {
2672     return nxt_conf_vldt_error(vldt, "The \"%s\" option is mutually exclusive "
2673                                "with the \"targets\" object.", data);
2674 }
2675 
2676 
2677 static nxt_int_t
2678 nxt_conf_vldt_targets(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
2679     void *data)
2680 {
2681     nxt_int_t   ret;
2682     nxt_uint_t  n;
2683 
2684     n = nxt_conf_object_members_count(value);
2685 
2686     if (n > 254) {
2687         return nxt_conf_vldt_error(vldt, "The \"targets\" object must not "
2688                                    "contain more than 254 members.");
2689     }
2690 
2691     vldt->ctx = data;
2692 
2693     ret = nxt_conf_vldt_object_iterator(vldt, value, &nxt_conf_vldt_target);
2694 
2695     vldt->ctx = NULL;
2696 
2697     return ret;
2698 }
2699 
2700 
2701 static nxt_int_t
2702 nxt_conf_vldt_target(nxt_conf_validation_t *vldt, nxt_str_t *name,
2703     nxt_conf_value_t *value)
2704 {
2705     if (name->length == 0) {
2706         return nxt_conf_vldt_error(vldt,
2707                                    "The target name must not be empty.");
2708     }
2709 
2710     if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
2711         return nxt_conf_vldt_error(vldt, "The \"%V\" target must be "
2712                                    "an object.", name);
2713     }
2714 
2715     return nxt_conf_vldt_object(vldt, value, vldt->ctx);
2716 }
2717 
2718 
2719 static nxt_int_t
2720 nxt_conf_vldt_clone_namespaces(nxt_conf_validation_t *vldt,
2721     nxt_conf_value_t *value, void *data)
2722 {
2723     return nxt_conf_vldt_object(vldt, value, data);
2724 }
2725 
2726 
2727 static nxt_int_t
2728 nxt_conf_vldt_isolation(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
2729     void *data)
2730 {
2731     return nxt_conf_vldt_object(vldt, value, data);
2732 }
2733 
2734 
2735 #if (NXT_HAVE_CLONE_NEWUSER)
2736 
2737 typedef struct {
2738     nxt_int_t container;
2739     nxt_int_t host;
2740     nxt_int_t size;
2741 } nxt_conf_vldt_clone_procmap_conf_t;
2742 
2743 
2744 static nxt_conf_map_t nxt_conf_vldt_clone_procmap_conf_map[] = {
2745     {
2746         nxt_string("container"),
2747         NXT_CONF_MAP_INT32,
2748         offsetof(nxt_conf_vldt_clone_procmap_conf_t, container),
2749     },
2750 
2751     {
2752         nxt_string("host"),
2753         NXT_CONF_MAP_INT32,
2754         offsetof(nxt_conf_vldt_clone_procmap_conf_t, host),
2755     },
2756 
2757     {
2758         nxt_string("size"),
2759         NXT_CONF_MAP_INT32,
2760         offsetof(nxt_conf_vldt_clone_procmap_conf_t, size),
2761     },
2762 
2763 };
2764 
2765 
2766 static nxt_int_t
2767 nxt_conf_vldt_clone_procmap(nxt_conf_validation_t *vldt, const char *mapfile,
2768         nxt_conf_value_t *value)
2769 {
2770     nxt_int_t                           ret;
2771     nxt_conf_vldt_clone_procmap_conf_t  procmap;
2772 
2773     procmap.container = -1;
2774     procmap.host = -1;
2775     procmap.size = -1;
2776 
2777     ret = nxt_conf_map_object(vldt->pool, value,
2778                               nxt_conf_vldt_clone_procmap_conf_map,
2779                               nxt_nitems(nxt_conf_vldt_clone_procmap_conf_map),
2780                               &procmap);
2781     if (ret != NXT_OK) {
2782         return ret;
2783     }
2784 
2785     if (procmap.container == -1) {
2786         return nxt_conf_vldt_error(vldt, "The %s requires the "
2787                 "\"container\" field set.", mapfile);
2788     }
2789 
2790     if (procmap.host == -1) {
2791         return nxt_conf_vldt_error(vldt, "The %s requires the "
2792                 "\"host\" field set.", mapfile);
2793     }
2794 
2795     if (procmap.size == -1) {
2796         return nxt_conf_vldt_error(vldt, "The %s requires the "
2797                 "\"size\" field set.", mapfile);
2798     }
2799 
2800     return NXT_OK;
2801 }
2802 
2803 
2804 static nxt_int_t
2805 nxt_conf_vldt_clone_uidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value)
2806 {
2807     nxt_int_t  ret;
2808 
2809     if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
2810         return nxt_conf_vldt_error(vldt, "The \"uidmap\" array "
2811                                    "must contain only object values.");
2812     }
2813 
2814     ret = nxt_conf_vldt_object(vldt, value,
2815                                (void *) nxt_conf_vldt_app_procmap_members);
2816     if (nxt_slow_path(ret != NXT_OK)) {
2817         return ret;
2818     }
2819 
2820     return nxt_conf_vldt_clone_procmap(vldt, "uid_map", value);
2821 }
2822 
2823 
2824 static nxt_int_t
2825 nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value)
2826 {
2827     nxt_int_t ret;
2828 
2829     if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
2830         return nxt_conf_vldt_error(vldt, "The \"gidmap\" array "
2831                                    "must contain only object values.");
2832     }
2833 
2834     ret = nxt_conf_vldt_object(vldt, value,
2835                                (void *) nxt_conf_vldt_app_procmap_members);
2836     if (nxt_slow_path(ret != NXT_OK)) {
2837         return ret;
2838     }
2839 
2840     return nxt_conf_vldt_clone_procmap(vldt, "gid_map", value);
2841 }
2842 
2843 #endif
2844 
2845 
2846 static nxt_int_t
2847 nxt_conf_vldt_argument(nxt_conf_validation_t *vldt, nxt_conf_value_t *value)
2848 {
2849     nxt_str_t  str;
2850 
2851     if (nxt_conf_type(value) != NXT_CONF_STRING) {
2852         return nxt_conf_vldt_error(vldt, "The \"arguments\" array "
2853                                    "must contain only string values.");
2854     }
2855 
2856     nxt_conf_get_string(value, &str);
2857 
2858     if (nxt_memchr(str.start, '\0', str.length) != NULL) {
2859         return nxt_conf_vldt_error(vldt, "The \"arguments\" array must not "
2860                                    "contain strings with null character.");
2861     }
2862 
2863     return NXT_OK;
2864 }
2865 
2866 
2867 static nxt_int_t
2868 nxt_conf_vldt_php(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
2869     void *data)
2870 {
2871     nxt_conf_value_t  *targets;
2872 
2873     static nxt_str_t  targets_str = nxt_string("targets");
2874 
2875     targets = nxt_conf_get_object_member(value, &targets_str, NULL);
2876 
2877     if (targets != NULL) {
2878         return nxt_conf_vldt_object(vldt, value, nxt_conf_vldt_php_members);
2879     }
2880 
2881     return nxt_conf_vldt_object(vldt, value,
2882                                 nxt_conf_vldt_php_notargets_members);
2883 }
2884 
2885 
2886 static nxt_int_t
2887 nxt_conf_vldt_php_option(nxt_conf_validation_t *vldt, nxt_str_t *name,
2888     nxt_conf_value_t *value)
2889 {
2890     if (name->length == 0) {
2891         return nxt_conf_vldt_error(vldt,
2892                                    "The PHP option name must not be empty.");
2893     }
2894 
2895     if (nxt_conf_type(value) != NXT_CONF_STRING) {
2896         return nxt_conf_vldt_error(vldt, "The \"%V\" PHP option must be "
2897                                    "a string.", name);
2898     }
2899 
2900     return NXT_OK;
2901 }
2902 
2903 
2904 static nxt_int_t
2905 nxt_conf_vldt_java_classpath(nxt_conf_validation_t *vldt,
2906     nxt_conf_value_t *value)
2907 {
2908     nxt_str_t  str;
2909 
2910     if (nxt_conf_type(value) != NXT_CONF_STRING) {
2911         return nxt_conf_vldt_error(vldt, "The \"classpath\" array "
2912                                    "must contain only string values.");
2913     }
2914 
2915     nxt_conf_get_string(value, &str);
2916 
2917     if (nxt_memchr(str.start, '\0', str.length) != NULL) {
2918         return nxt_conf_vldt_error(vldt, "The \"classpath\" array must not "
2919                                    "contain strings with null character.");
2920     }
2921 
2922     return NXT_OK;
2923 }
2924 
2925 
2926 static nxt_int_t
2927 nxt_conf_vldt_java_option(nxt_conf_validation_t *vldt, nxt_conf_value_t *value)
2928 {
2929     nxt_str_t  str;
2930 
2931     if (nxt_conf_type(value) != NXT_CONF_STRING) {
2932         return nxt_conf_vldt_error(vldt, "The \"options\" array "
2933                                    "must contain only string values.");
2934     }
2935 
2936     nxt_conf_get_string(value, &str);
2937 
2938     if (nxt_memchr(str.start, '\0', str.length) != NULL) {
2939         return nxt_conf_vldt_error(vldt, "The \"options\" array must not "
2940                                    "contain strings with null character.");
2941     }
2942 
2943     return NXT_OK;
2944 }
2945 
2946 
2947 static nxt_int_t
2948 nxt_conf_vldt_upstream(nxt_conf_validation_t *vldt, nxt_str_t *name,
2949     nxt_conf_value_t *value)
2950 {
2951     nxt_int_t         ret;
2952     nxt_conf_value_t  *conf;
2953 
2954     static nxt_str_t  servers = nxt_string("servers");
2955 
2956     ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT);
2957 
2958     if (ret != NXT_OK) {
2959         return ret;
2960     }
2961 
2962     ret = nxt_conf_vldt_object(vldt, value, nxt_conf_vldt_upstream_members);
2963 
2964     if (ret != NXT_OK) {
2965         return ret;
2966     }
2967 
2968     conf = nxt_conf_get_object_member(value, &servers, NULL);
2969     if (conf == NULL) {
2970         return nxt_conf_vldt_error(vldt, "The \"%V\" upstream must contain "
2971                                    "\"servers\" object value.", name);
2972     }
2973 
2974     return NXT_OK;
2975 }
2976 
2977 
2978 static nxt_int_t
2979 nxt_conf_vldt_server(nxt_conf_validation_t *vldt, nxt_str_t *name,
2980     nxt_conf_value_t *value)
2981 {
2982     nxt_int_t       ret;
2983     nxt_sockaddr_t  *sa;
2984 
2985     ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT);
2986 
2987     if (ret != NXT_OK) {
2988         return ret;
2989     }
2990 
2991     sa = nxt_sockaddr_parse(vldt->pool, name);
2992 
2993     if (sa == NULL) {
2994         return nxt_conf_vldt_error(vldt, "The \"%V\" is not valid "
2995                                    "server address.", name);
2996     }
2997 
2998     return nxt_conf_vldt_object(vldt, value,
2999                                 nxt_conf_vldt_upstream_server_members);
3000 }
3001 
3002 
3003 static nxt_int_t
3004 nxt_conf_vldt_server_weight(nxt_conf_validation_t *vldt,
3005     nxt_conf_value_t *value, void *data)
3006 {
3007     double  num_value;
3008 
3009     num_value = nxt_conf_get_number(value);
3010 
3011     if (num_value < 0) {
3012         return nxt_conf_vldt_error(vldt, "The \"weight\" number must be "
3013                                    "positive.");
3014     }
3015 
3016     if (num_value > 1000000) {
3017         return nxt_conf_vldt_error(vldt, "The \"weight\" number must "
3018                                    "not exceed 1,000,000");
3019     }
3020 
3021     return NXT_OK;
3022 }
3023