blob: 2cdcf76616db3a9a643612b09a657dfae3e7cb97 [file] [log] [blame]
Willy Tarreau36b9e222018-11-11 15:19:52 +01001#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <netdb.h>
5#include <ctype.h>
6#include <pwd.h>
7#include <grp.h>
8#include <errno.h>
9#include <sys/types.h>
10#include <sys/stat.h>
11#include <fcntl.h>
12#include <unistd.h>
13
14#include <common/cfgparse.h>
15#include <proto/compression.h>
16
17/*
18 * parse a line in a <global> section. Returns the error code, 0 if OK, or
19 * any combination of :
20 * - ERR_ABORT: must abort ASAP
21 * - ERR_FATAL: we can continue parsing but not start the service
22 * - ERR_WARN: a warning has been emitted
23 * - ERR_ALERT: an alert has been emitted
24 * Only the two first ones can stop processing, the two others are just
25 * indicators.
26 */
27int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
28{
29 int err_code = 0;
30 char *errmsg = NULL;
31
32 if (!strcmp(args[0], "global")) { /* new section */
33 /* no option, nothing special to do */
34 alertif_too_many_args(0, file, linenum, args, &err_code);
35 goto out;
36 }
37 else if (!strcmp(args[0], "daemon")) {
38 if (alertif_too_many_args(0, file, linenum, args, &err_code))
39 goto out;
40 global.mode |= MODE_DAEMON;
41 }
42 else if (!strcmp(args[0], "master-worker")) {
43 if (alertif_too_many_args(1, file, linenum, args, &err_code))
44 goto out;
45 if (*args[1]) {
46 if (!strcmp(args[1], "no-exit-on-failure")) {
47 global.tune.options |= GTUNE_NOEXIT_ONFAILURE;
48 } else {
49 ha_alert("parsing [%s:%d] : '%s' only supports 'no-exit-on-failure' option.\n", file, linenum, args[0]);
50 err_code |= ERR_ALERT | ERR_FATAL;
51 goto out;
52 }
53 }
54 global.mode |= MODE_MWORKER;
55 }
56 else if (!strcmp(args[0], "debug")) {
57 if (alertif_too_many_args(0, file, linenum, args, &err_code))
58 goto out;
59 global.mode |= MODE_DEBUG;
60 }
61 else if (!strcmp(args[0], "noepoll")) {
62 if (alertif_too_many_args(0, file, linenum, args, &err_code))
63 goto out;
64 global.tune.options &= ~GTUNE_USE_EPOLL;
65 }
66 else if (!strcmp(args[0], "nokqueue")) {
67 if (alertif_too_many_args(0, file, linenum, args, &err_code))
68 goto out;
69 global.tune.options &= ~GTUNE_USE_KQUEUE;
70 }
71 else if (!strcmp(args[0], "nopoll")) {
72 if (alertif_too_many_args(0, file, linenum, args, &err_code))
73 goto out;
74 global.tune.options &= ~GTUNE_USE_POLL;
75 }
Willy Tarreaubeb859a2018-11-22 18:07:59 +010076 else if (!strcmp(args[0], "busy-polling")) { /* "no busy-polling" or "busy-polling" */
77 if (alertif_too_many_args(0, file, linenum, args, &err_code))
78 goto out;
79 if (kwm == KWM_NO)
80 global.tune.options &= ~GTUNE_BUSY_POLLING;
81 else
82 global.tune.options |= GTUNE_BUSY_POLLING;
83 }
Willy Tarreau36b9e222018-11-11 15:19:52 +010084 else if (!strcmp(args[0], "nosplice")) {
85 if (alertif_too_many_args(0, file, linenum, args, &err_code))
86 goto out;
87 global.tune.options &= ~GTUNE_USE_SPLICE;
88 }
89 else if (!strcmp(args[0], "nogetaddrinfo")) {
90 if (alertif_too_many_args(0, file, linenum, args, &err_code))
91 goto out;
92 global.tune.options &= ~GTUNE_USE_GAI;
93 }
94 else if (!strcmp(args[0], "noreuseport")) {
95 if (alertif_too_many_args(0, file, linenum, args, &err_code))
96 goto out;
97 global.tune.options &= ~GTUNE_USE_REUSEPORT;
98 }
99 else if (!strcmp(args[0], "quiet")) {
100 if (alertif_too_many_args(0, file, linenum, args, &err_code))
101 goto out;
102 global.mode |= MODE_QUIET;
103 }
104 else if (!strcmp(args[0], "tune.runqueue-depth")) {
105 if (alertif_too_many_args(1, file, linenum, args, &err_code))
106 goto out;
107 if (global.tune.runqueue_depth != 0) {
108 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
109 err_code |= ERR_ALERT;
110 goto out;
111 }
112 if (*(args[1]) == 0) {
113 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
114 err_code |= ERR_ALERT | ERR_FATAL;
115 goto out;
116 }
117 global.tune.runqueue_depth = atol(args[1]);
118
119 }
120 else if (!strcmp(args[0], "tune.maxpollevents")) {
121 if (alertif_too_many_args(1, file, linenum, args, &err_code))
122 goto out;
123 if (global.tune.maxpollevents != 0) {
124 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
125 err_code |= ERR_ALERT;
126 goto out;
127 }
128 if (*(args[1]) == 0) {
129 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
130 err_code |= ERR_ALERT | ERR_FATAL;
131 goto out;
132 }
133 global.tune.maxpollevents = atol(args[1]);
134 }
135 else if (!strcmp(args[0], "tune.maxaccept")) {
136 if (alertif_too_many_args(1, file, linenum, args, &err_code))
137 goto out;
138 if (global.tune.maxaccept != 0) {
139 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
140 err_code |= ERR_ALERT;
141 goto out;
142 }
143 if (*(args[1]) == 0) {
144 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
145 err_code |= ERR_ALERT | ERR_FATAL;
146 goto out;
147 }
148 global.tune.maxaccept = atol(args[1]);
149 }
150 else if (!strcmp(args[0], "tune.chksize")) {
151 if (alertif_too_many_args(1, file, linenum, args, &err_code))
152 goto out;
153 if (*(args[1]) == 0) {
154 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
155 err_code |= ERR_ALERT | ERR_FATAL;
156 goto out;
157 }
158 global.tune.chksize = atol(args[1]);
159 }
160 else if (!strcmp(args[0], "tune.recv_enough")) {
161 if (alertif_too_many_args(1, file, linenum, args, &err_code))
162 goto out;
163 if (*(args[1]) == 0) {
164 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
165 err_code |= ERR_ALERT | ERR_FATAL;
166 goto out;
167 }
168 global.tune.recv_enough = atol(args[1]);
169 }
170 else if (!strcmp(args[0], "tune.buffers.limit")) {
171 if (alertif_too_many_args(1, file, linenum, args, &err_code))
172 goto out;
173 if (*(args[1]) == 0) {
174 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
175 err_code |= ERR_ALERT | ERR_FATAL;
176 goto out;
177 }
178 global.tune.buf_limit = atol(args[1]);
179 if (global.tune.buf_limit) {
180 if (global.tune.buf_limit < 3)
181 global.tune.buf_limit = 3;
182 if (global.tune.buf_limit <= global.tune.reserved_bufs)
183 global.tune.buf_limit = global.tune.reserved_bufs + 1;
184 }
185 }
186 else if (!strcmp(args[0], "tune.buffers.reserve")) {
187 if (alertif_too_many_args(1, file, linenum, args, &err_code))
188 goto out;
189 if (*(args[1]) == 0) {
190 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
191 err_code |= ERR_ALERT | ERR_FATAL;
192 goto out;
193 }
194 global.tune.reserved_bufs = atol(args[1]);
195 if (global.tune.reserved_bufs < 2)
196 global.tune.reserved_bufs = 2;
197 if (global.tune.buf_limit && global.tune.buf_limit <= global.tune.reserved_bufs)
198 global.tune.buf_limit = global.tune.reserved_bufs + 1;
199 }
200 else if (!strcmp(args[0], "tune.bufsize")) {
201 if (alertif_too_many_args(1, file, linenum, args, &err_code))
202 goto out;
203 if (*(args[1]) == 0) {
204 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
205 err_code |= ERR_ALERT | ERR_FATAL;
206 goto out;
207 }
208 global.tune.bufsize = atol(args[1]);
Willy Tarreauc77d3642018-12-12 06:19:42 +0100209 /* round it up to support a two-pointer alignment at the end */
210 global.tune.bufsize = (global.tune.bufsize + 2 * sizeof(void *) - 1) & -(2 * sizeof(void *));
Willy Tarreau36b9e222018-11-11 15:19:52 +0100211 if (global.tune.bufsize <= 0) {
212 ha_alert("parsing [%s:%d] : '%s' expects a positive integer argument.\n", file, linenum, args[0]);
213 err_code |= ERR_ALERT | ERR_FATAL;
214 goto out;
215 }
216 }
217 else if (!strcmp(args[0], "tune.maxrewrite")) {
218 if (alertif_too_many_args(1, file, linenum, args, &err_code))
219 goto out;
220 if (*(args[1]) == 0) {
221 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
222 err_code |= ERR_ALERT | ERR_FATAL;
223 goto out;
224 }
225 global.tune.maxrewrite = atol(args[1]);
226 if (global.tune.maxrewrite < 0) {
227 ha_alert("parsing [%s:%d] : '%s' expects a positive integer argument.\n", file, linenum, args[0]);
228 err_code |= ERR_ALERT | ERR_FATAL;
229 goto out;
230 }
231 }
232 else if (!strcmp(args[0], "tune.idletimer")) {
233 unsigned int idle;
234 const char *res;
235
236 if (alertif_too_many_args(1, file, linenum, args, &err_code))
237 goto out;
238 if (*(args[1]) == 0) {
239 ha_alert("parsing [%s:%d] : '%s' expects a timer value between 0 and 65535 ms.\n", file, linenum, args[0]);
240 err_code |= ERR_ALERT | ERR_FATAL;
241 goto out;
242 }
243
244 res = parse_time_err(args[1], &idle, TIME_UNIT_MS);
245 if (res) {
246 ha_alert("parsing [%s:%d]: unexpected character '%c' in argument to <%s>.\n",
247 file, linenum, *res, args[0]);
248 err_code |= ERR_ALERT | ERR_FATAL;
249 goto out;
250 }
251
252 if (idle > 65535) {
253 ha_alert("parsing [%s:%d] : '%s' expects a timer value between 0 and 65535 ms.\n", file, linenum, args[0]);
254 err_code |= ERR_ALERT | ERR_FATAL;
255 goto out;
256 }
257 global.tune.idle_timer = idle;
258 }
259 else if (!strcmp(args[0], "tune.rcvbuf.client")) {
260 if (alertif_too_many_args(1, file, linenum, args, &err_code))
261 goto out;
262 if (global.tune.client_rcvbuf != 0) {
263 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
264 err_code |= ERR_ALERT;
265 goto out;
266 }
267 if (*(args[1]) == 0) {
268 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
269 err_code |= ERR_ALERT | ERR_FATAL;
270 goto out;
271 }
272 global.tune.client_rcvbuf = atol(args[1]);
273 }
274 else if (!strcmp(args[0], "tune.rcvbuf.server")) {
275 if (alertif_too_many_args(1, file, linenum, args, &err_code))
276 goto out;
277 if (global.tune.server_rcvbuf != 0) {
278 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
279 err_code |= ERR_ALERT;
280 goto out;
281 }
282 if (*(args[1]) == 0) {
283 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
284 err_code |= ERR_ALERT | ERR_FATAL;
285 goto out;
286 }
287 global.tune.server_rcvbuf = atol(args[1]);
288 }
289 else if (!strcmp(args[0], "tune.sndbuf.client")) {
290 if (alertif_too_many_args(1, file, linenum, args, &err_code))
291 goto out;
292 if (global.tune.client_sndbuf != 0) {
293 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
294 err_code |= ERR_ALERT;
295 goto out;
296 }
297 if (*(args[1]) == 0) {
298 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
299 err_code |= ERR_ALERT | ERR_FATAL;
300 goto out;
301 }
302 global.tune.client_sndbuf = atol(args[1]);
303 }
304 else if (!strcmp(args[0], "tune.sndbuf.server")) {
305 if (alertif_too_many_args(1, file, linenum, args, &err_code))
306 goto out;
307 if (global.tune.server_sndbuf != 0) {
308 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
309 err_code |= ERR_ALERT;
310 goto out;
311 }
312 if (*(args[1]) == 0) {
313 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
314 err_code |= ERR_ALERT | ERR_FATAL;
315 goto out;
316 }
317 global.tune.server_sndbuf = atol(args[1]);
318 }
319 else if (!strcmp(args[0], "tune.pipesize")) {
320 if (alertif_too_many_args(1, file, linenum, args, &err_code))
321 goto out;
322 if (*(args[1]) == 0) {
323 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
324 err_code |= ERR_ALERT | ERR_FATAL;
325 goto out;
326 }
327 global.tune.pipesize = atol(args[1]);
328 }
329 else if (!strcmp(args[0], "tune.http.cookielen")) {
330 if (alertif_too_many_args(1, file, linenum, args, &err_code))
331 goto out;
332 if (*(args[1]) == 0) {
333 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
334 err_code |= ERR_ALERT | ERR_FATAL;
335 goto out;
336 }
337 global.tune.cookie_len = atol(args[1]) + 1;
338 }
339 else if (!strcmp(args[0], "tune.http.logurilen")) {
340 if (alertif_too_many_args(1, file, linenum, args, &err_code))
341 goto out;
342 if (*(args[1]) == 0) {
343 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
344 err_code |= ERR_ALERT | ERR_FATAL;
345 goto out;
346 }
347 global.tune.requri_len = atol(args[1]) + 1;
348 }
349 else if (!strcmp(args[0], "tune.http.maxhdr")) {
350 if (alertif_too_many_args(1, file, linenum, args, &err_code))
351 goto out;
352 if (*(args[1]) == 0) {
353 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
354 err_code |= ERR_ALERT | ERR_FATAL;
355 goto out;
356 }
357 global.tune.max_http_hdr = atoi(args[1]);
358 if (global.tune.max_http_hdr < 1 || global.tune.max_http_hdr > 32767) {
359 ha_alert("parsing [%s:%d] : '%s' expects a numeric value between 1 and 32767\n",
360 file, linenum, args[0]);
361 err_code |= ERR_ALERT | ERR_FATAL;
362 goto out;
363 }
364 }
365 else if (!strcmp(args[0], "tune.comp.maxlevel")) {
366 if (alertif_too_many_args(1, file, linenum, args, &err_code))
367 goto out;
368 if (*args[1]) {
369 global.tune.comp_maxlevel = atoi(args[1]);
370 if (global.tune.comp_maxlevel < 1 || global.tune.comp_maxlevel > 9) {
371 ha_alert("parsing [%s:%d] : '%s' expects a numeric value between 1 and 9\n",
372 file, linenum, args[0]);
373 err_code |= ERR_ALERT | ERR_FATAL;
374 goto out;
375 }
376 } else {
377 ha_alert("parsing [%s:%d] : '%s' expects a numeric value between 1 and 9\n",
378 file, linenum, args[0]);
379 err_code |= ERR_ALERT | ERR_FATAL;
380 goto out;
381 }
382 }
383 else if (!strcmp(args[0], "tune.pattern.cache-size")) {
384 if (*args[1]) {
385 global.tune.pattern_cache = atoi(args[1]);
386 if (global.tune.pattern_cache < 0) {
387 ha_alert("parsing [%s:%d] : '%s' expects a positive numeric value\n",
388 file, linenum, args[0]);
389 err_code |= ERR_ALERT | ERR_FATAL;
390 goto out;
391 }
392 } else {
393 ha_alert("parsing [%s:%d] : '%s' expects a positive numeric value\n",
394 file, linenum, args[0]);
395 err_code |= ERR_ALERT | ERR_FATAL;
396 goto out;
397 }
398 }
399 else if (!strcmp(args[0], "uid")) {
400 if (alertif_too_many_args(1, file, linenum, args, &err_code))
401 goto out;
402 if (global.uid != 0) {
403 ha_alert("parsing [%s:%d] : user/uid already specified. Continuing.\n", file, linenum);
404 err_code |= ERR_ALERT;
405 goto out;
406 }
407 if (*(args[1]) == 0) {
408 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
409 err_code |= ERR_ALERT | ERR_FATAL;
410 goto out;
411 }
412 if (strl2irc(args[1], strlen(args[1]), &global.uid) != 0) {
413 ha_warning("parsing [%s:%d] : uid: string '%s' is not a number.\n | You might want to use the 'user' parameter to use a system user name.\n", file, linenum, args[1]);
414 err_code |= ERR_WARN;
415 goto out;
416 }
417
418 }
419 else if (!strcmp(args[0], "gid")) {
420 if (alertif_too_many_args(1, file, linenum, args, &err_code))
421 goto out;
422 if (global.gid != 0) {
423 ha_alert("parsing [%s:%d] : group/gid already specified. Continuing.\n", file, linenum);
424 err_code |= ERR_ALERT;
425 goto out;
426 }
427 if (*(args[1]) == 0) {
428 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
429 err_code |= ERR_ALERT | ERR_FATAL;
430 goto out;
431 }
432 if (strl2irc(args[1], strlen(args[1]), &global.gid) != 0) {
433 ha_warning("parsing [%s:%d] : gid: string '%s' is not a number.\n | You might want to use the 'group' parameter to use a system group name.\n", file, linenum, args[1]);
434 err_code |= ERR_WARN;
435 goto out;
436 }
437 }
438 else if (!strcmp(args[0], "external-check")) {
439 if (alertif_too_many_args(0, file, linenum, args, &err_code))
440 goto out;
441 global.external_check = 1;
442 }
443 /* user/group name handling */
444 else if (!strcmp(args[0], "user")) {
445 struct passwd *ha_user;
446 if (alertif_too_many_args(1, file, linenum, args, &err_code))
447 goto out;
448 if (global.uid != 0) {
449 ha_alert("parsing [%s:%d] : user/uid already specified. Continuing.\n", file, linenum);
450 err_code |= ERR_ALERT;
451 goto out;
452 }
453 errno = 0;
454 ha_user = getpwnam(args[1]);
455 if (ha_user != NULL) {
456 global.uid = (int)ha_user->pw_uid;
457 }
458 else {
459 ha_alert("parsing [%s:%d] : cannot find user id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno));
460 err_code |= ERR_ALERT | ERR_FATAL;
461 }
462 }
463 else if (!strcmp(args[0], "group")) {
464 struct group *ha_group;
465 if (alertif_too_many_args(1, file, linenum, args, &err_code))
466 goto out;
467 if (global.gid != 0) {
468 ha_alert("parsing [%s:%d] : gid/group was already specified. Continuing.\n", file, linenum);
469 err_code |= ERR_ALERT;
470 goto out;
471 }
472 errno = 0;
473 ha_group = getgrnam(args[1]);
474 if (ha_group != NULL) {
475 global.gid = (int)ha_group->gr_gid;
476 }
477 else {
478 ha_alert("parsing [%s:%d] : cannot find group id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno));
479 err_code |= ERR_ALERT | ERR_FATAL;
480 }
481 }
482 /* end of user/group name handling*/
483 else if (!strcmp(args[0], "nbproc")) {
484 if (alertif_too_many_args(1, file, linenum, args, &err_code))
485 goto out;
486 if (*(args[1]) == 0) {
487 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
488 err_code |= ERR_ALERT | ERR_FATAL;
489 goto out;
490 }
491 global.nbproc = atol(args[1]);
492 if (global.nbproc < 1 || global.nbproc > LONGBITS) {
493 ha_alert("parsing [%s:%d] : '%s' must be between 1 and %d (was %d).\n",
494 file, linenum, args[0], LONGBITS, global.nbproc);
495 err_code |= ERR_ALERT | ERR_FATAL;
496 goto out;
497 }
498 }
499 else if (!strcmp(args[0], "nbthread")) {
500 if (alertif_too_many_args(1, file, linenum, args, &err_code))
501 goto out;
502 if (*(args[1]) == 0) {
503 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
504 err_code |= ERR_ALERT | ERR_FATAL;
505 goto out;
506 }
507 global.nbthread = parse_nbthread(args[1], &errmsg);
508 if (!global.nbthread) {
509 ha_alert("parsing [%s:%d] : '%s' %s.\n",
510 file, linenum, args[0], errmsg);
511 err_code |= ERR_ALERT | ERR_FATAL;
512 goto out;
513 }
514 }
515 else if (!strcmp(args[0], "maxconn")) {
516 if (alertif_too_many_args(1, file, linenum, args, &err_code))
517 goto out;
518 if (global.maxconn != 0) {
519 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
520 err_code |= ERR_ALERT;
521 goto out;
522 }
523 if (*(args[1]) == 0) {
524 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
525 err_code |= ERR_ALERT | ERR_FATAL;
526 goto out;
527 }
528 global.maxconn = atol(args[1]);
529#ifdef SYSTEM_MAXCONN
530 if (global.maxconn > DEFAULT_MAXCONN && cfg_maxconn <= DEFAULT_MAXCONN) {
531 ha_alert("parsing [%s:%d] : maxconn value %d too high for this system.\nLimiting to %d. Please use '-n' to force the value.\n", file, linenum, global.maxconn, DEFAULT_MAXCONN);
532 global.maxconn = DEFAULT_MAXCONN;
533 err_code |= ERR_ALERT;
534 }
535#endif /* SYSTEM_MAXCONN */
536 }
537 else if (!strcmp(args[0], "ssl-server-verify")) {
538 if (alertif_too_many_args(1, file, linenum, args, &err_code))
539 goto out;
540 if (*(args[1]) == 0) {
541 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
542 err_code |= ERR_ALERT | ERR_FATAL;
543 goto out;
544 }
545 if (strcmp(args[1],"none") == 0)
546 global.ssl_server_verify = SSL_SERVER_VERIFY_NONE;
547 else if (strcmp(args[1],"required") == 0)
548 global.ssl_server_verify = SSL_SERVER_VERIFY_REQUIRED;
549 else {
550 ha_alert("parsing [%s:%d] : '%s' expects 'none' or 'required' as argument.\n", file, linenum, args[0]);
551 err_code |= ERR_ALERT | ERR_FATAL;
552 goto out;
553 }
554 }
555 else if (!strcmp(args[0], "maxconnrate")) {
556 if (alertif_too_many_args(1, file, linenum, args, &err_code))
557 goto out;
558 if (global.cps_lim != 0) {
559 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
560 err_code |= ERR_ALERT;
561 goto out;
562 }
563 if (*(args[1]) == 0) {
564 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
565 err_code |= ERR_ALERT | ERR_FATAL;
566 goto out;
567 }
568 global.cps_lim = atol(args[1]);
569 }
570 else if (!strcmp(args[0], "maxsessrate")) {
571 if (alertif_too_many_args(1, file, linenum, args, &err_code))
572 goto out;
573 if (global.sps_lim != 0) {
574 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
575 err_code |= ERR_ALERT;
576 goto out;
577 }
578 if (*(args[1]) == 0) {
579 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
580 err_code |= ERR_ALERT | ERR_FATAL;
581 goto out;
582 }
583 global.sps_lim = atol(args[1]);
584 }
585 else if (!strcmp(args[0], "maxsslrate")) {
586 if (alertif_too_many_args(1, file, linenum, args, &err_code))
587 goto out;
588 if (global.ssl_lim != 0) {
589 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
590 err_code |= ERR_ALERT;
591 goto out;
592 }
593 if (*(args[1]) == 0) {
594 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
595 err_code |= ERR_ALERT | ERR_FATAL;
596 goto out;
597 }
598 global.ssl_lim = atol(args[1]);
599 }
600 else if (!strcmp(args[0], "maxcomprate")) {
601 if (alertif_too_many_args(1, file, linenum, args, &err_code))
602 goto out;
603 if (*(args[1]) == 0) {
604 ha_alert("parsing [%s:%d] : '%s' expects an integer argument in kb/s.\n", file, linenum, args[0]);
605 err_code |= ERR_ALERT | ERR_FATAL;
606 goto out;
607 }
608 global.comp_rate_lim = atoi(args[1]) * 1024;
609 }
610 else if (!strcmp(args[0], "maxpipes")) {
611 if (alertif_too_many_args(1, file, linenum, args, &err_code))
612 goto out;
613 if (global.maxpipes != 0) {
614 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
615 err_code |= ERR_ALERT;
616 goto out;
617 }
618 if (*(args[1]) == 0) {
619 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
620 err_code |= ERR_ALERT | ERR_FATAL;
621 goto out;
622 }
623 global.maxpipes = atol(args[1]);
624 }
625 else if (!strcmp(args[0], "maxzlibmem")) {
626 if (alertif_too_many_args(1, file, linenum, args, &err_code))
627 goto out;
628 if (*(args[1]) == 0) {
629 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
630 err_code |= ERR_ALERT | ERR_FATAL;
631 goto out;
632 }
633 global.maxzlibmem = atol(args[1]) * 1024L * 1024L;
634 }
635 else if (!strcmp(args[0], "maxcompcpuusage")) {
636 if (alertif_too_many_args(1, file, linenum, args, &err_code))
637 goto out;
638 if (*(args[1]) == 0) {
639 ha_alert("parsing [%s:%d] : '%s' expects an integer argument between 0 and 100.\n", file, linenum, args[0]);
640 err_code |= ERR_ALERT | ERR_FATAL;
641 goto out;
642 }
643 compress_min_idle = 100 - atoi(args[1]);
644 if (compress_min_idle > 100) {
645 ha_alert("parsing [%s:%d] : '%s' expects an integer argument between 0 and 100.\n", file, linenum, args[0]);
646 err_code |= ERR_ALERT | ERR_FATAL;
647 goto out;
648 }
649 }
650
651 else if (!strcmp(args[0], "ulimit-n")) {
652 if (alertif_too_many_args(1, file, linenum, args, &err_code))
653 goto out;
654 if (global.rlimit_nofile != 0) {
655 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
656 err_code |= ERR_ALERT;
657 goto out;
658 }
659 if (*(args[1]) == 0) {
660 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
661 err_code |= ERR_ALERT | ERR_FATAL;
662 goto out;
663 }
664 global.rlimit_nofile = atol(args[1]);
665 }
666 else if (!strcmp(args[0], "chroot")) {
667 if (alertif_too_many_args(1, file, linenum, args, &err_code))
668 goto out;
669 if (global.chroot != NULL) {
670 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
671 err_code |= ERR_ALERT;
672 goto out;
673 }
674 if (*(args[1]) == 0) {
675 ha_alert("parsing [%s:%d] : '%s' expects a directory as an argument.\n", file, linenum, args[0]);
676 err_code |= ERR_ALERT | ERR_FATAL;
677 goto out;
678 }
679 global.chroot = strdup(args[1]);
680 }
681 else if (!strcmp(args[0], "description")) {
682 int i, len=0;
683 char *d;
684
685 if (!*args[1]) {
686 ha_alert("parsing [%s:%d]: '%s' expects a string argument.\n",
687 file, linenum, args[0]);
688 err_code |= ERR_ALERT | ERR_FATAL;
689 goto out;
690 }
691
692 for (i = 1; *args[i]; i++)
693 len += strlen(args[i]) + 1;
694
695 if (global.desc)
696 free(global.desc);
697
698 global.desc = d = calloc(1, len);
699
700 d += snprintf(d, global.desc + len - d, "%s", args[1]);
701 for (i = 2; *args[i]; i++)
702 d += snprintf(d, global.desc + len - d, " %s", args[i]);
703 }
704 else if (!strcmp(args[0], "node")) {
705 int i;
706 char c;
707
708 if (alertif_too_many_args(1, file, linenum, args, &err_code))
709 goto out;
710
711 for (i=0; args[1][i]; i++) {
712 c = args[1][i];
713 if (!isupper((unsigned char)c) && !islower((unsigned char)c) &&
714 !isdigit((unsigned char)c) && c != '_' && c != '-' && c != '.')
715 break;
716 }
717
718 if (!i || args[1][i]) {
719 ha_alert("parsing [%s:%d]: '%s' requires valid node name - non-empty string"
720 " with digits(0-9), letters(A-Z, a-z), dot(.), hyphen(-) or underscode(_).\n",
721 file, linenum, args[0]);
722 err_code |= ERR_ALERT | ERR_FATAL;
723 goto out;
724 }
725
726 if (global.node)
727 free(global.node);
728
729 global.node = strdup(args[1]);
730 }
731 else if (!strcmp(args[0], "pidfile")) {
732 if (alertif_too_many_args(1, file, linenum, args, &err_code))
733 goto out;
734 if (global.pidfile != NULL) {
735 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
736 err_code |= ERR_ALERT;
737 goto out;
738 }
739 if (*(args[1]) == 0) {
740 ha_alert("parsing [%s:%d] : '%s' expects a file name as an argument.\n", file, linenum, args[0]);
741 err_code |= ERR_ALERT | ERR_FATAL;
742 goto out;
743 }
744 global.pidfile = strdup(args[1]);
745 }
746 else if (!strcmp(args[0], "unix-bind")) {
747 int cur_arg = 1;
748 while (*(args[cur_arg])) {
749 if (!strcmp(args[cur_arg], "prefix")) {
750 if (global.unix_bind.prefix != NULL) {
751 ha_alert("parsing [%s:%d] : unix-bind '%s' already specified. Continuing.\n", file, linenum, args[cur_arg]);
752 err_code |= ERR_ALERT;
753 cur_arg += 2;
754 continue;
755 }
756
757 if (*(args[cur_arg+1]) == 0) {
758 ha_alert("parsing [%s:%d] : unix_bind '%s' expects a path as an argument.\n", file, linenum, args[cur_arg]);
759 err_code |= ERR_ALERT | ERR_FATAL;
760 goto out;
761 }
762 global.unix_bind.prefix = strdup(args[cur_arg+1]);
763 cur_arg += 2;
764 continue;
765 }
766
767 if (!strcmp(args[cur_arg], "mode")) {
768
769 global.unix_bind.ux.mode = strtol(args[cur_arg + 1], NULL, 8);
770 cur_arg += 2;
771 continue;
772 }
773
774 if (!strcmp(args[cur_arg], "uid")) {
775
776 global.unix_bind.ux.uid = atol(args[cur_arg + 1 ]);
777 cur_arg += 2;
778 continue;
779 }
780
781 if (!strcmp(args[cur_arg], "gid")) {
782
783 global.unix_bind.ux.gid = atol(args[cur_arg + 1 ]);
784 cur_arg += 2;
785 continue;
786 }
787
788 if (!strcmp(args[cur_arg], "user")) {
789 struct passwd *user;
790
791 user = getpwnam(args[cur_arg + 1]);
792 if (!user) {
793 ha_alert("parsing [%s:%d] : '%s' : '%s' unknown user.\n",
794 file, linenum, args[0], args[cur_arg + 1 ]);
795 err_code |= ERR_ALERT | ERR_FATAL;
796 goto out;
797 }
798
799 global.unix_bind.ux.uid = user->pw_uid;
800 cur_arg += 2;
801 continue;
802 }
803
804 if (!strcmp(args[cur_arg], "group")) {
805 struct group *group;
806
807 group = getgrnam(args[cur_arg + 1]);
808 if (!group) {
809 ha_alert("parsing [%s:%d] : '%s' : '%s' unknown group.\n",
810 file, linenum, args[0], args[cur_arg + 1 ]);
811 err_code |= ERR_ALERT | ERR_FATAL;
812 goto out;
813 }
814
815 global.unix_bind.ux.gid = group->gr_gid;
816 cur_arg += 2;
817 continue;
818 }
819
820 ha_alert("parsing [%s:%d] : '%s' only supports the 'prefix', 'mode', 'uid', 'gid', 'user' and 'group' options.\n",
821 file, linenum, args[0]);
822 err_code |= ERR_ALERT | ERR_FATAL;
823 goto out;
824 }
825 }
826 else if (!strcmp(args[0], "log")) { /* "no log" or "log ..." */
827 if (!parse_logsrv(args, &global.logsrvs, (kwm == KWM_NO), &errmsg)) {
828 ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
829 err_code |= ERR_ALERT | ERR_FATAL;
830 goto out;
831 }
832 }
833 else if (!strcmp(args[0], "log-send-hostname")) { /* set the hostname in syslog header */
834 char *name;
835
836 if (global.log_send_hostname != NULL) {
837 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
838 err_code |= ERR_ALERT;
839 goto out;
840 }
841
842 if (*(args[1]))
843 name = args[1];
844 else
845 name = hostname;
846
847 free(global.log_send_hostname);
848 global.log_send_hostname = strdup(name);
849 }
850 else if (!strcmp(args[0], "server-state-base")) { /* path base where HAProxy can find server state files */
851 if (global.server_state_base != NULL) {
852 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
853 err_code |= ERR_ALERT;
854 goto out;
855 }
856
857 if (!*(args[1])) {
858 ha_alert("parsing [%s:%d] : '%s' expects one argument: a directory path.\n", file, linenum, args[0]);
859 err_code |= ERR_FATAL;
860 goto out;
861 }
862
863 global.server_state_base = strdup(args[1]);
864 }
865 else if (!strcmp(args[0], "server-state-file")) { /* path to the file where HAProxy can load the server states */
866 if (global.server_state_file != NULL) {
867 ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
868 err_code |= ERR_ALERT;
869 goto out;
870 }
871
872 if (!*(args[1])) {
873 ha_alert("parsing [%s:%d] : '%s' expect one argument: a file path.\n", file, linenum, args[0]);
874 err_code |= ERR_FATAL;
875 goto out;
876 }
877
878 global.server_state_file = strdup(args[1]);
879 }
880 else if (!strcmp(args[0], "log-tag")) { /* tag to report to syslog */
881 if (alertif_too_many_args(1, file, linenum, args, &err_code))
882 goto out;
883 if (*(args[1]) == 0) {
884 ha_alert("parsing [%s:%d] : '%s' expects a tag for use in syslog.\n", file, linenum, args[0]);
885 err_code |= ERR_ALERT | ERR_FATAL;
886 goto out;
887 }
888 chunk_destroy(&global.log_tag);
889 chunk_initstr(&global.log_tag, strdup(args[1]));
890 }
891 else if (!strcmp(args[0], "spread-checks")) { /* random time between checks (0-50) */
892 if (alertif_too_many_args(1, file, linenum, args, &err_code))
893 goto out;
894 if (global.spread_checks != 0) {
895 ha_alert("parsing [%s:%d]: spread-checks already specified. Continuing.\n", file, linenum);
896 err_code |= ERR_ALERT;
897 goto out;
898 }
899 if (*(args[1]) == 0) {
900 ha_alert("parsing [%s:%d]: '%s' expects an integer argument (0..50).\n", file, linenum, args[0]);
901 err_code |= ERR_ALERT | ERR_FATAL;
902 goto out;
903 }
904 global.spread_checks = atol(args[1]);
905 if (global.spread_checks < 0 || global.spread_checks > 50) {
906 ha_alert("parsing [%s:%d]: 'spread-checks' needs a positive value in range 0..50.\n", file, linenum);
907 err_code |= ERR_ALERT | ERR_FATAL;
908 }
909 }
910 else if (!strcmp(args[0], "max-spread-checks")) { /* maximum time between first and last check */
911 const char *err;
912 unsigned int val;
913
914 if (alertif_too_many_args(1, file, linenum, args, &err_code))
915 goto out;
916 if (*(args[1]) == 0) {
917 ha_alert("parsing [%s:%d]: '%s' expects an integer argument (0..50).\n", file, linenum, args[0]);
918 err_code |= ERR_ALERT | ERR_FATAL;
919 goto out;
920 }
921
922 err = parse_time_err(args[1], &val, TIME_UNIT_MS);
923 if (err) {
924 ha_alert("parsing [%s:%d]: unsupported character '%c' in '%s' (wants an integer delay).\n", file, linenum, *err, args[0]);
925 err_code |= ERR_ALERT | ERR_FATAL;
926 }
927 global.max_spread_checks = val;
928 if (global.max_spread_checks < 0) {
929 ha_alert("parsing [%s:%d]: '%s' needs a positive delay in milliseconds.\n",file, linenum, args[0]);
930 err_code |= ERR_ALERT | ERR_FATAL;
931 }
932 }
933 else if (strcmp(args[0], "cpu-map") == 0) {
934 /* map a process list to a CPU set */
935#ifdef USE_CPU_AFFINITY
936 char *slash;
937 unsigned long proc = 0, thread = 0, cpus;
938 int i, j, n, autoinc;
939
940 if (!*args[1] || !*args[2]) {
941 ha_alert("parsing [%s:%d] : %s expects a process number "
942 " ('all', 'odd', 'even', a number from 1 to %d or a range), "
943 " followed by a list of CPU ranges with numbers from 0 to %d.\n",
944 file, linenum, args[0], LONGBITS, LONGBITS - 1);
945 err_code |= ERR_ALERT | ERR_FATAL;
946 goto out;
947 }
948
949 if ((slash = strchr(args[1], '/')) != NULL)
950 *slash = 0;
951
Willy Tarreauc9a82e42019-01-26 13:25:14 +0100952 if (parse_process_number(args[1], &proc, LONGBITS, &autoinc, &errmsg)) {
Willy Tarreau36b9e222018-11-11 15:19:52 +0100953 ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
954 err_code |= ERR_ALERT | ERR_FATAL;
955 goto out;
956 }
957
958 if (slash) {
Willy Tarreauc9a82e42019-01-26 13:25:14 +0100959 if (parse_process_number(slash+1, &thread, MAX_THREADS, NULL, &errmsg)) {
Willy Tarreau36b9e222018-11-11 15:19:52 +0100960 ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
961 err_code |= ERR_ALERT | ERR_FATAL;
962 goto out;
963 }
964 *slash = '/';
965
966 if (autoinc && atleast2(proc) && atleast2(thread)) {
967 ha_alert("parsing [%s:%d] : %s : '%s' : unable to automatically bind "
968 "a process range _AND_ a thread range\n",
969 file, linenum, args[0], args[1]);
970 err_code |= ERR_ALERT | ERR_FATAL;
971 goto out;
972 }
973 }
974
975 if (parse_cpu_set((const char **)args+2, &cpus, &errmsg)) {
976 ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
977 err_code |= ERR_ALERT | ERR_FATAL;
978 goto out;
979 }
980
981 if (autoinc &&
982 my_popcountl(proc) != my_popcountl(cpus) &&
983 my_popcountl(thread) != my_popcountl(cpus)) {
984 ha_alert("parsing [%s:%d] : %s : PROC/THREAD range and CPU sets "
985 "must have the same size to be automatically bound\n",
986 file, linenum, args[0]);
987 err_code |= ERR_ALERT | ERR_FATAL;
988 goto out;
989 }
990
991 for (i = n = 0; i < LONGBITS; i++) {
992 /* No mapping for this process */
993 if (!(proc & (1UL << i)))
994 continue;
995
996 /* Mapping at the process level */
997 if (!thread) {
998 if (!autoinc)
999 global.cpu_map.proc[i] = cpus;
1000 else {
1001 n += my_ffsl(cpus >> n);
1002 global.cpu_map.proc[i] = (1UL << (n-1));
1003 }
1004 continue;
1005 }
1006
1007 /* Mapping at the thread level */
1008 for (j = 0; j < MAX_THREADS; j++) {
1009 /* Np mapping for this thread */
1010 if (!(thread & (1UL << j)))
1011 continue;
1012
1013 if (!autoinc)
1014 global.cpu_map.thread[i][j] = cpus;
1015 else {
1016 n += my_ffsl(cpus >> n);
1017 global.cpu_map.thread[i][j] = (1UL << (n-1));
1018 }
1019 }
1020 }
1021#else
1022 ha_alert("parsing [%s:%d] : '%s' is not enabled, please check build options for USE_CPU_AFFINITY.\n",
1023 file, linenum, args[0]);
1024 err_code |= ERR_ALERT | ERR_FATAL;
1025 goto out;
1026#endif /* ! USE_CPU_AFFINITY */
1027 }
1028 else if (strcmp(args[0], "setenv") == 0 || strcmp(args[0], "presetenv") == 0) {
1029 if (alertif_too_many_args(3, file, linenum, args, &err_code))
1030 goto out;
1031
1032 if (*(args[2]) == 0) {
1033 ha_alert("parsing [%s:%d]: '%s' expects a name and a value.\n", file, linenum, args[0]);
1034 err_code |= ERR_ALERT | ERR_FATAL;
1035 goto out;
1036 }
1037
1038 /* "setenv" overwrites, "presetenv" only sets if not yet set */
1039 if (setenv(args[1], args[2], (args[0][0] == 's')) != 0) {
1040 ha_alert("parsing [%s:%d]: '%s' failed on variable '%s' : %s.\n", file, linenum, args[0], args[1], strerror(errno));
1041 err_code |= ERR_ALERT | ERR_FATAL;
1042 goto out;
1043 }
1044 }
1045 else if (!strcmp(args[0], "unsetenv")) {
1046 int arg;
1047
1048 if (*(args[1]) == 0) {
1049 ha_alert("parsing [%s:%d]: '%s' expects at least one variable name.\n", file, linenum, args[0]);
1050 err_code |= ERR_ALERT | ERR_FATAL;
1051 goto out;
1052 }
1053
1054 for (arg = 1; *args[arg]; arg++) {
1055 if (unsetenv(args[arg]) != 0) {
1056 ha_alert("parsing [%s:%d]: '%s' failed on variable '%s' : %s.\n", file, linenum, args[0], args[arg], strerror(errno));
1057 err_code |= ERR_ALERT | ERR_FATAL;
1058 goto out;
1059 }
1060 }
1061 }
1062 else if (!strcmp(args[0], "resetenv")) {
1063 extern char **environ;
1064 char **env = environ;
1065
1066 /* args contain variable names to keep, one per argument */
1067 while (*env) {
1068 int arg;
1069
1070 /* look for current variable in among all those we want to keep */
1071 for (arg = 1; *args[arg]; arg++) {
1072 if (strncmp(*env, args[arg], strlen(args[arg])) == 0 &&
1073 (*env)[strlen(args[arg])] == '=')
1074 break;
1075 }
1076
1077 /* delete this variable */
1078 if (!*args[arg]) {
1079 char *delim = strchr(*env, '=');
1080
1081 if (!delim || delim - *env >= trash.size) {
1082 ha_alert("parsing [%s:%d]: '%s' failed to unset invalid variable '%s'.\n", file, linenum, args[0], *env);
1083 err_code |= ERR_ALERT | ERR_FATAL;
1084 goto out;
1085 }
1086
1087 memcpy(trash.area, *env, delim - *env);
1088 trash.area[delim - *env] = 0;
1089
1090 if (unsetenv(trash.area) != 0) {
1091 ha_alert("parsing [%s:%d]: '%s' failed to unset variable '%s' : %s.\n", file, linenum, args[0], *env, strerror(errno));
1092 err_code |= ERR_ALERT | ERR_FATAL;
1093 goto out;
1094 }
1095 }
1096 else
1097 env++;
1098 }
1099 }
1100 else {
1101 struct cfg_kw_list *kwl;
1102 int index;
1103 int rc;
1104
1105 list_for_each_entry(kwl, &cfg_keywords.list, list) {
1106 for (index = 0; kwl->kw[index].kw != NULL; index++) {
1107 if (kwl->kw[index].section != CFG_GLOBAL)
1108 continue;
1109 if (strcmp(kwl->kw[index].kw, args[0]) == 0) {
1110 rc = kwl->kw[index].parse(args, CFG_GLOBAL, NULL, NULL, file, linenum, &errmsg);
1111 if (rc < 0) {
1112 ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg);
1113 err_code |= ERR_ALERT | ERR_FATAL;
1114 }
1115 else if (rc > 0) {
1116 ha_warning("parsing [%s:%d] : %s\n", file, linenum, errmsg);
1117 err_code |= ERR_WARN;
1118 goto out;
1119 }
1120 goto out;
1121 }
1122 }
1123 }
1124
1125 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "global");
1126 err_code |= ERR_ALERT | ERR_FATAL;
1127 }
1128
1129 out:
1130 free(errmsg);
1131 return err_code;
1132}
1133