xref: /unit/src/nxt_conf_validation.c (revision 510:4979fe09d9cd)
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_router.h>
10 
11 
12 typedef enum {
13     NXT_CONF_VLDT_NULL    = 1 << NXT_CONF_NULL,
14     NXT_CONF_VLDT_BOOLEAN = 1 << NXT_CONF_BOOLEAN,
15     NXT_CONF_VLDT_INTEGER = 1 << NXT_CONF_INTEGER,
16     NXT_CONF_VLDT_NUMBER  = 1 << NXT_CONF_NUMBER,
17     NXT_CONF_VLDT_STRING  = 1 << NXT_CONF_STRING,
18     NXT_CONF_VLDT_ARRAY   = 1 << NXT_CONF_ARRAY,
19     NXT_CONF_VLDT_OBJECT  = 1 << NXT_CONF_OBJECT,
20 } nxt_conf_vldt_type_t;
21 
22 
23 typedef struct {
24     nxt_str_t             name;
25     nxt_conf_vldt_type_t  type;
26     nxt_int_t             (*validator)(nxt_conf_validation_t *vldt,
27                                        nxt_conf_value_t *value, void *data);
28     void                  *data;
29 } nxt_conf_vldt_object_t;
30 
31 
32 #define NXT_CONF_VLDT_NEXT(f)  { nxt_null_string, 0, NULL, (f) }
33 #define NXT_CONF_VLDT_END      { nxt_null_string, 0, NULL, NULL }
34 
35 
36 typedef nxt_int_t (*nxt_conf_vldt_member_t)(nxt_conf_validation_t *vldt,
37                                             nxt_str_t *name,
38                                             nxt_conf_value_t *value);
39 
40 typedef nxt_int_t (*nxt_conf_vldt_system_t)(nxt_conf_validation_t *vldt,
41                                             char *name);
42 
43 
44 static nxt_int_t nxt_conf_vldt_type(nxt_conf_validation_t *vldt,
45     nxt_str_t *name, nxt_conf_value_t *value, nxt_conf_vldt_type_t type);
46 static nxt_int_t nxt_conf_vldt_error(nxt_conf_validation_t *vldt,
47     const char *fmt, ...);
48 
49 static nxt_int_t nxt_conf_vldt_listener(nxt_conf_validation_t *vldt,
50     nxt_str_t *name, nxt_conf_value_t *value);
51 static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt,
52     nxt_conf_value_t *value, void *data);
53 static nxt_int_t nxt_conf_vldt_app(nxt_conf_validation_t *vldt,
54     nxt_str_t *name, nxt_conf_value_t *value);
55 static nxt_int_t nxt_conf_vldt_object(nxt_conf_validation_t *vldt,
56     nxt_conf_value_t *value, void *data);
57 static nxt_int_t nxt_conf_vldt_processes(nxt_conf_validation_t *vldt,
58     nxt_conf_value_t *value, void *data);
59 static nxt_int_t nxt_conf_vldt_object_iterator(nxt_conf_validation_t *vldt,
60     nxt_conf_value_t *value, void *data);
61 static nxt_int_t nxt_conf_vldt_system(nxt_conf_validation_t *vldt,
62     nxt_conf_value_t *value, void *data);
63 static nxt_int_t nxt_conf_vldt_user(nxt_conf_validation_t *vldt, char *name);
64 static nxt_int_t nxt_conf_vldt_group(nxt_conf_validation_t *vldt, char *name);
65 
66 
67 static nxt_conf_vldt_object_t  nxt_conf_vldt_root_members[] = {
68     { nxt_string("listeners"),
69       NXT_CONF_VLDT_OBJECT,
70       &nxt_conf_vldt_object_iterator,
71       (void *) &nxt_conf_vldt_listener },
72 
73     { nxt_string("applications"),
74       NXT_CONF_VLDT_OBJECT,
75       &nxt_conf_vldt_object_iterator,
76       (void *) &nxt_conf_vldt_app },
77 
78     NXT_CONF_VLDT_END
79 };
80 
81 
82 static nxt_conf_vldt_object_t  nxt_conf_vldt_listener_members[] = {
83     { nxt_string("application"),
84       NXT_CONF_VLDT_STRING,
85       &nxt_conf_vldt_app_name,
86       NULL },
87 
88     NXT_CONF_VLDT_END
89 };
90 
91 
92 static nxt_conf_vldt_object_t  nxt_conf_vldt_app_limits_members[] = {
93     { nxt_string("timeout"),
94       NXT_CONF_VLDT_INTEGER,
95       NULL,
96       NULL },
97 
98     { nxt_string("reschedule_timeout"),
99       NXT_CONF_VLDT_INTEGER,
100       NULL,
101       NULL },
102 
103     { nxt_string("requests"),
104       NXT_CONF_VLDT_INTEGER,
105       NULL,
106       NULL },
107 
108     NXT_CONF_VLDT_END
109 };
110 
111 
112 static nxt_conf_vldt_object_t  nxt_conf_vldt_app_processes_members[] = {
113     { nxt_string("spare"),
114       NXT_CONF_VLDT_INTEGER,
115       NULL,
116       NULL },
117 
118     { nxt_string("max"),
119       NXT_CONF_VLDT_INTEGER,
120       NULL,
121       NULL },
122 
123     { nxt_string("idle_timeout"),
124       NXT_CONF_VLDT_INTEGER,
125       NULL,
126       NULL },
127 
128     NXT_CONF_VLDT_END
129 };
130 
131 
132 static nxt_conf_vldt_object_t  nxt_conf_vldt_common_members[] = {
133     { nxt_string("type"),
134       NXT_CONF_VLDT_STRING,
135       NULL,
136       NULL },
137 
138     { nxt_string("limits"),
139       NXT_CONF_VLDT_OBJECT,
140       &nxt_conf_vldt_object,
141       (void *) &nxt_conf_vldt_app_limits_members },
142 
143     { nxt_string("processes"),
144       NXT_CONF_VLDT_INTEGER | NXT_CONF_VLDT_OBJECT,
145       &nxt_conf_vldt_processes,
146       (void *) &nxt_conf_vldt_app_processes_members },
147 
148     { nxt_string("user"),
149       NXT_CONF_VLDT_STRING,
150       nxt_conf_vldt_system,
151       (void *) &nxt_conf_vldt_user },
152 
153     { nxt_string("group"),
154       NXT_CONF_VLDT_STRING,
155       nxt_conf_vldt_system,
156       (void *) &nxt_conf_vldt_group },
157 
158     { nxt_string("working_directory"),
159       NXT_CONF_VLDT_STRING,
160       NULL,
161       NULL },
162 
163     NXT_CONF_VLDT_END
164 };
165 
166 
167 static nxt_conf_vldt_object_t  nxt_conf_vldt_python_members[] = {
168     { nxt_string("home"),
169       NXT_CONF_VLDT_STRING,
170       NULL,
171       NULL },
172 
173     { nxt_string("path"),
174       NXT_CONF_VLDT_STRING,
175       NULL,
176       NULL },
177 
178     { nxt_string("module"),
179       NXT_CONF_VLDT_STRING,
180       NULL,
181       NULL },
182 
183     NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members)
184 };
185 
186 
187 static nxt_conf_vldt_object_t  nxt_conf_vldt_php_members[] = {
188     { nxt_string("root"),
189       NXT_CONF_VLDT_STRING,
190       NULL,
191       NULL },
192 
193     { nxt_string("script"),
194       NXT_CONF_VLDT_STRING,
195       NULL,
196       NULL },
197 
198     { nxt_string("index"),
199       NXT_CONF_VLDT_STRING,
200       NULL,
201       NULL },
202 
203     NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members)
204 };
205 
206 
207 static nxt_conf_vldt_object_t  nxt_conf_vldt_go_members[] = {
208     { nxt_string("executable"),
209       NXT_CONF_VLDT_STRING,
210       NULL,
211       NULL },
212 
213     NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members)
214 };
215 
216 
217 static nxt_conf_vldt_object_t  nxt_conf_vldt_perl_members[] = {
218     { nxt_string("script"),
219       NXT_CONF_VLDT_STRING,
220       NULL,
221       NULL },
222 
223     NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members)
224 };
225 
226 
227 nxt_int_t
228 nxt_conf_validate(nxt_conf_validation_t *vldt)
229 {
230     nxt_int_t  ret;
231 
232     ret = nxt_conf_vldt_type(vldt, NULL, vldt->conf, NXT_CONF_VLDT_OBJECT);
233 
234     if (ret != NXT_OK) {
235         return ret;
236     }
237 
238     return nxt_conf_vldt_object(vldt, vldt->conf, nxt_conf_vldt_root_members);
239 }
240 
241 
242 #define NXT_CONF_VLDT_ANY_TYPE                                                \
243     "either a null, a boolean, an integer, "                                  \
244     "a number, a string, an array, or an object"
245 
246 
247 static nxt_int_t
248 nxt_conf_vldt_type(nxt_conf_validation_t *vldt, nxt_str_t *name,
249     nxt_conf_value_t *value, nxt_conf_vldt_type_t type)
250 {
251     u_char      *p;
252     nxt_str_t   expected;
253     nxt_bool_t  serial;
254     nxt_uint_t  value_type, n, t;
255     u_char      buf[sizeof(NXT_CONF_VLDT_ANY_TYPE) - 1];
256 
257     static nxt_str_t  type_name[] = {
258         nxt_string("a null"),
259         nxt_string("a boolean"),
260         nxt_string("an integer"),
261         nxt_string("a number"),
262         nxt_string("a string"),
263         nxt_string("an array"),
264         nxt_string("an object"),
265     };
266 
267     value_type = nxt_conf_type(value);
268 
269     if ((1 << value_type) & type) {
270         return NXT_OK;
271     }
272 
273     p = buf;
274 
275     n = __builtin_popcount(type);
276 
277     if (n > 1) {
278         p = nxt_cpymem(p, "either ", 7);
279     }
280 
281     serial = (n > 2);
282 
283     for ( ;; ) {
284         t = __builtin_ffs(type) - 1;
285 
286         p = nxt_cpymem(p, type_name[t].start, type_name[t].length);
287 
288         n--;
289 
290         if (n == 0) {
291             break;
292         }
293 
294         if (n > 1 || serial) {
295             *p++ = ',';
296         }
297 
298         if (n == 1) {
299             p = nxt_cpymem(p, " or", 3);
300         }
301 
302         *p++ = ' ';
303 
304         type = type & ~(1 << t);
305     }
306 
307     expected.length = p - buf;
308     expected.start = buf;
309 
310     if (name == NULL) {
311         return nxt_conf_vldt_error(vldt,
312                                    "The configuration must be %V, but not %V.",
313                                    &expected, &type_name[value_type]);
314     }
315 
316     return nxt_conf_vldt_error(vldt,
317                                "The \"%V\" value must be %V, but not %V.",
318                                name, &expected, &type_name[value_type]);
319 }
320 
321 
322 static nxt_int_t
323 nxt_conf_vldt_error(nxt_conf_validation_t *vldt, const char *fmt, ...)
324 {
325     u_char   *p, *end;
326     size_t   size;
327     va_list  args;
328     u_char   error[NXT_MAX_ERROR_STR];
329 
330     va_start(args, fmt);
331     end = nxt_vsprintf(error, error + NXT_MAX_ERROR_STR, fmt, args);
332     va_end(args);
333 
334     size = end - error;
335 
336     p = nxt_mp_nget(vldt->pool, size);
337     if (p == NULL) {
338         return NXT_ERROR;
339     }
340 
341     nxt_memcpy(p, error, size);
342 
343     vldt->error.length = size;
344     vldt->error.start = p;
345 
346     return NXT_DECLINED;
347 }
348 
349 
350 static nxt_int_t
351 nxt_conf_vldt_listener(nxt_conf_validation_t *vldt, nxt_str_t *name,
352     nxt_conf_value_t *value)
353 {
354     nxt_int_t  ret;
355 
356     ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT);
357 
358     if (ret != NXT_OK) {
359         return ret;
360     }
361 
362     return nxt_conf_vldt_object(vldt, value, nxt_conf_vldt_listener_members);
363 }
364 
365 
366 static nxt_int_t
367 nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
368     void *data)
369 {
370     nxt_str_t         name;
371     nxt_conf_value_t  *apps, *app;
372 
373     static nxt_str_t  apps_str = nxt_string("applications");
374 
375     nxt_conf_get_string(value, &name);
376 
377     apps = nxt_conf_get_object_member(vldt->conf, &apps_str, NULL);
378 
379     if (nxt_slow_path(apps == NULL)) {
380         goto error;
381     }
382 
383     app = nxt_conf_get_object_member(apps, &name, NULL);
384 
385     if (nxt_slow_path(app == NULL)) {
386         goto error;
387     }
388 
389     return NXT_OK;
390 
391 error:
392 
393     return nxt_conf_vldt_error(vldt, "Listening socket is assigned for "
394                                      "a non existing application \"%V\".",
395                                      &name);
396 }
397 
398 
399 static nxt_int_t
400 nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name,
401     nxt_conf_value_t *value)
402 {
403     nxt_int_t              ret;
404     nxt_str_t              type;
405     nxt_thread_t           *thread;
406     nxt_conf_value_t       *type_value;
407     nxt_app_lang_module_t  *lang;
408 
409     static nxt_str_t  type_str = nxt_string("type");
410 
411     static void  *members[] = {
412         nxt_conf_vldt_python_members,
413         nxt_conf_vldt_php_members,
414         nxt_conf_vldt_go_members,
415         nxt_conf_vldt_perl_members,
416     };
417 
418     ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT);
419 
420     if (ret != NXT_OK) {
421         return ret;
422     }
423 
424     type_value = nxt_conf_get_object_member(value, &type_str, NULL);
425 
426     if (type_value == NULL) {
427         return nxt_conf_vldt_error(vldt,
428                            "Application must have the \"type\" property set.");
429     }
430 
431     ret = nxt_conf_vldt_type(vldt, &type_str, type_value, NXT_CONF_VLDT_STRING);
432 
433     if (ret != NXT_OK) {
434         return ret;
435     }
436 
437     nxt_conf_get_string(type_value, &type);
438 
439     thread = nxt_thread();
440 
441     lang = nxt_app_lang_module(thread->runtime, &type);
442     if (lang == NULL) {
443         return nxt_conf_vldt_error(vldt,
444                                    "The module to run \"%V\" is not found "
445                                    "among the available application modules.",
446                                    &type);
447     }
448 
449     return nxt_conf_vldt_object(vldt, value, members[lang->type]);
450 }
451 
452 
453 static nxt_int_t
454 nxt_conf_vldt_object(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
455     void *data)
456 {
457     uint32_t                index;
458     nxt_int_t               ret;
459     nxt_str_t               name;
460     nxt_conf_value_t        *member;
461     nxt_conf_vldt_object_t  *vals;
462 
463     index = 0;
464 
465     for ( ;; ) {
466         member = nxt_conf_next_object_member(value, &name, &index);
467 
468         if (member == NULL) {
469             return NXT_OK;
470         }
471 
472         vals = data;
473 
474         for ( ;; ) {
475             if (vals->name.length == 0) {
476 
477                 if (vals->data != NULL) {
478                     vals = vals->data;
479                     continue;
480                 }
481 
482                 return nxt_conf_vldt_error(vldt, "Unknown parameter \"%V\".",
483                                            &name);
484             }
485 
486             if (!nxt_strstr_eq(&vals->name, &name)) {
487                 vals++;
488                 continue;
489             }
490 
491             ret = nxt_conf_vldt_type(vldt, &name, member, vals->type);
492 
493             if (ret != NXT_OK) {
494                 return ret;
495             }
496 
497             if (vals->validator != NULL) {
498                 ret = vals->validator(vldt, member, vals->data);
499 
500                 if (ret != NXT_OK) {
501                     return ret;
502                 }
503             }
504 
505             break;
506         }
507     }
508 }
509 
510 
511 typedef struct {
512     int64_t  spare;
513     int64_t  max;
514     int64_t  idle_timeout;
515 } nxt_conf_vldt_processes_conf_t;
516 
517 
518 static nxt_conf_map_t  nxt_conf_vldt_processes_conf_map[] = {
519     {
520         nxt_string("spare"),
521         NXT_CONF_MAP_INT64,
522         offsetof(nxt_conf_vldt_processes_conf_t, spare),
523     },
524 
525     {
526         nxt_string("max"),
527         NXT_CONF_MAP_INT64,
528         offsetof(nxt_conf_vldt_processes_conf_t, max),
529     },
530 
531     {
532         nxt_string("idle_timeout"),
533         NXT_CONF_MAP_INT64,
534         offsetof(nxt_conf_vldt_processes_conf_t, idle_timeout),
535     },
536 };
537 
538 
539 static nxt_int_t
540 nxt_conf_vldt_processes(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
541     void *data)
542 {
543     int64_t                         int_value;
544     nxt_int_t                       ret;
545     nxt_conf_vldt_processes_conf_t  proc;
546 
547     static nxt_str_t                max_str = nxt_string("max");
548 
549     if (nxt_conf_type(value) == NXT_CONF_INTEGER) {
550         int_value = nxt_conf_get_integer(value);
551 
552         if (int_value < 1) {
553             return nxt_conf_vldt_error(vldt, "The \"processes\" number must be "
554                                        "equal to or greater than 1.");
555         }
556 
557         if (int_value > NXT_INT32_T_MAX) {
558             return nxt_conf_vldt_error(vldt, "The \"processes\" number must "
559                                        "not exceed %d.", NXT_INT32_T_MAX);
560         }
561 
562         return NXT_OK;
563     }
564 
565     ret = nxt_conf_vldt_object(vldt, value, data);
566     if (ret != NXT_OK) {
567         return ret;
568     }
569 
570     proc.spare = 1;
571     proc.max = 1;
572     proc.idle_timeout = 15;
573 
574     ret = nxt_conf_map_object(vldt->pool, value,
575                               nxt_conf_vldt_processes_conf_map,
576                               nxt_nitems(nxt_conf_vldt_processes_conf_map),
577                               &proc);
578     if (ret != NXT_OK) {
579         return ret;
580     }
581 
582     if (proc.spare < 0) {
583         return nxt_conf_vldt_error(vldt, "The \"spare\" number must not be "
584                                    "negative.");
585     }
586 
587     if (proc.spare > NXT_INT32_T_MAX) {
588         return nxt_conf_vldt_error(vldt, "The \"spare\" number must not "
589                                    "not exceed %d.", NXT_INT32_T_MAX);
590     }
591 
592     if (nxt_conf_get_object_member(value, &max_str, NULL) != NULL) {
593 
594         if (proc.max < 1) {
595             return nxt_conf_vldt_error(vldt, "The \"max\" number must be equal "
596                                        "to or greater than 1.");
597         }
598 
599         if (proc.max > NXT_INT32_T_MAX) {
600             return nxt_conf_vldt_error(vldt, "The \"max\" number must not "
601                                        "not exceed %d.", NXT_INT32_T_MAX);
602         }
603 
604         if (proc.max < proc.spare) {
605             return nxt_conf_vldt_error(vldt, "The \"spare\" number must be "
606                                        "lower than \"max\".");
607         }
608     }
609 
610     if (proc.idle_timeout < 0) {
611         return nxt_conf_vldt_error(vldt, "The \"idle_timeout\" number must not "
612                                    "be negative.");
613     }
614 
615     if (proc.idle_timeout > NXT_INT32_T_MAX / 1000) {
616         return nxt_conf_vldt_error(vldt, "The \"idle_timeout\" number must not "
617                                    "not exceed %d.", NXT_INT32_T_MAX / 1000);
618     }
619 
620     return NXT_OK;
621 }
622 
623 
624 static nxt_int_t
625 nxt_conf_vldt_object_iterator(nxt_conf_validation_t *vldt,
626     nxt_conf_value_t *value, void *data)
627 {
628     uint32_t                index;
629     nxt_int_t               ret;
630     nxt_str_t               name;
631     nxt_conf_value_t        *member;
632     nxt_conf_vldt_member_t  validator;
633 
634     validator = (nxt_conf_vldt_member_t) data;
635     index = 0;
636 
637     for ( ;; ) {
638         member = nxt_conf_next_object_member(value, &name, &index);
639 
640         if (member == NULL) {
641             return NXT_OK;
642         }
643 
644         ret = validator(vldt, &name, member);
645 
646         if (ret != NXT_OK) {
647             return ret;
648         }
649     }
650 }
651 
652 
653 static nxt_int_t
654 nxt_conf_vldt_system(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
655     void *data)
656 {
657     size_t                  length;
658     nxt_str_t               name;
659     nxt_conf_vldt_system_t  validator;
660     char                    string[32];
661 
662     /* The cast is required by Sun C. */
663     validator = (nxt_conf_vldt_system_t) data;
664 
665     nxt_conf_get_string(value, &name);
666 
667     length = name.length + 1;
668     length = nxt_min(length, sizeof(string));
669 
670     nxt_cpystrn((u_char *) string, name.start, length);
671 
672     return validator(vldt, string);
673 }
674 
675 
676 static nxt_int_t
677 nxt_conf_vldt_user(nxt_conf_validation_t *vldt, char *user)
678 {
679     struct passwd  *pwd;
680 
681     nxt_errno = 0;
682 
683     pwd = getpwnam(user);
684 
685     if (pwd != NULL) {
686         return NXT_OK;
687     }
688 
689     if (nxt_errno == 0) {
690         return nxt_conf_vldt_error(vldt, "User \"%s\" is not found.", user);
691     }
692 
693     return NXT_ERROR;
694 }
695 
696 
697 static nxt_int_t
698 nxt_conf_vldt_group(nxt_conf_validation_t *vldt, char *group)
699 {
700     struct group  *grp;
701 
702     nxt_errno = 0;
703 
704     grp = getgrnam(group);
705 
706     if (grp != NULL) {
707         return NXT_OK;
708     }
709 
710     if (nxt_errno == 0) {
711         return nxt_conf_vldt_error(vldt, "Group \"%s\" is not found.", group);
712     }
713 
714     return NXT_ERROR;
715 }
716 
717 
718