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