forked from haproxy/haproxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhlua.c
11779 lines (9973 loc) · 319 KB
/
hlua.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Lua unsafe core engine
*
* Copyright 2015-2016 Thierry Fournier <tfournier@arpalert.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#define _GNU_SOURCE
#include <ctype.h>
#include <setjmp.h>
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503
#error "Requires Lua 5.3 or later."
#endif
#include <import/ebpttree.h>
#include <haproxy/api.h>
#include <haproxy/applet.h>
#include <haproxy/arg.h>
#include <haproxy/auth.h>
#include <haproxy/cfgparse.h>
#include <haproxy/channel.h>
#include <haproxy/cli.h>
#include <haproxy/connection.h>
#include <haproxy/filters.h>
#include <haproxy/h1.h>
#include <haproxy/hlua.h>
#include <haproxy/hlua_fcn.h>
#include <haproxy/http_ana.h>
#include <haproxy/http_fetch.h>
#include <haproxy/http_htx.h>
#include <haproxy/http_rules.h>
#include <haproxy/log.h>
#include <haproxy/map.h>
#include <haproxy/obj_type.h>
#include <haproxy/pattern.h>
#include <haproxy/payload.h>
#include <haproxy/proxy.h>
#include <haproxy/regex.h>
#include <haproxy/sample.h>
#include <haproxy/server.h>
#include <haproxy/session.h>
#include <haproxy/stats-t.h>
#include <haproxy/stream.h>
#include <haproxy/stream_interface.h>
#include <haproxy/task.h>
#include <haproxy/tcp_rules.h>
#include <haproxy/thread.h>
#include <haproxy/tools.h>
#include <haproxy/vars.h>
#include <haproxy/xref.h>
/* Lua uses longjmp to perform yield or throwing errors. This
* macro is used only for identifying the function that can
* not return because a longjmp is executed.
* __LJMP marks a prototype of hlua file that can use longjmp.
* WILL_LJMP() marks an lua function that will use longjmp.
* MAY_LJMP() marks an lua function that may use longjmp.
*/
#define __LJMP
#define WILL_LJMP(func) do { func; my_unreachable(); } while(0)
#define MAY_LJMP(func) func
/* This couple of function executes securely some Lua calls outside of
* the lua runtime environment. Each Lua call can return a longjmp
* if it encounter a memory error.
*
* Lua documentation extract:
*
* If an error happens outside any protected environment, Lua calls
* a panic function (see lua_atpanic) and then calls abort, thus
* exiting the host application. Your panic function can avoid this
* exit by never returning (e.g., doing a long jump to your own
* recovery point outside Lua).
*
* The panic function runs as if it were a message handler (see
* §2.3); in particular, the error message is at the top of the
* stack. However, there is no guarantee about stack space. To push
* anything on the stack, the panic function must first check the
* available space (see §4.2).
*
* We must check all the Lua entry point. This includes:
* - The include/proto/hlua.h exported functions
* - the task wrapper function
* - The action wrapper function
* - The converters wrapper function
* - The sample-fetch wrapper functions
*
* It is tolerated that the initialisation function returns an abort.
* Before each Lua abort, an error message is written on stderr.
*
* The macro SET_SAFE_LJMP initialise the longjmp. The Macro
* RESET_SAFE_LJMP reset the longjmp. These function must be macro
* because they must be exists in the program stack when the longjmp
* is called.
*
* Note that the Lua processing is not really thread safe. It provides
* heavy system which consists to add our own lock function in the Lua
* code and recompile the library. This system will probably not accepted
* by maintainers of various distribs.
*
* Our main execution point of the Lua is the function lua_resume(). A
* quick looking on the Lua sources displays a lua_lock() a the start
* of function and a lua_unlock() at the end of the function. So I
* conclude that the Lua thread safe mode just perform a mutex around
* all execution. So I prefer to do this in the HAProxy code, it will be
* easier for distro maintainers.
*
* Note that the HAProxy lua functions rounded by the macro SET_SAFE_LJMP
* and RESET_SAFE_LJMP manipulates the Lua stack, so it will be careful
* to set mutex around these functions.
*/
__decl_spinlock(hlua_global_lock);
THREAD_LOCAL jmp_buf safe_ljmp_env;
static int hlua_panic_safe(lua_State *L) { return 0; }
static int hlua_panic_ljmp(lua_State *L) { WILL_LJMP(longjmp(safe_ljmp_env, 1)); return 0; }
/* This is the chained list of struct hlua_function referenced
* for haproxy action, sample-fetches, converters, cli and
* applet bindings. It is used for a post-initialisation control.
*/
static struct list referenced_functions = LIST_HEAD_INIT(referenced_functions);
/* This variable is used only during initialization to identify the Lua state
* currently being initialized. 0 is the common lua state, 1 to n are the Lua
* states dedicated to each thread (in this case hlua_state_id==tid+1).
*/
static int hlua_state_id;
/* This is a NULL-terminated list of lua file which are referenced to load per thread */
static char **per_thread_load = NULL;
lua_State *hlua_init_state(int thread_id);
/* This function takes the Lua global lock. Keep this function's visibility
* global so that it can appear in stack dumps and performance profiles!
*/
void lua_take_global_lock()
{
HA_SPIN_LOCK(LUA_LOCK, &hlua_global_lock);
}
static inline void lua_drop_global_lock()
{
HA_SPIN_UNLOCK(LUA_LOCK, &hlua_global_lock);
}
#define SET_SAFE_LJMP_L(__L, __HLUA) \
({ \
int ret; \
if ((__HLUA)->state_id == 0) \
lua_take_global_lock(); \
if (setjmp(safe_ljmp_env) != 0) { \
lua_atpanic(__L, hlua_panic_safe); \
ret = 0; \
if ((__HLUA)->state_id == 0) \
lua_drop_global_lock(); \
} else { \
lua_atpanic(__L, hlua_panic_ljmp); \
ret = 1; \
} \
ret; \
})
/* If we are the last function catching Lua errors, we
* must reset the panic function.
*/
#define RESET_SAFE_LJMP_L(__L, __HLUA) \
do { \
lua_atpanic(__L, hlua_panic_safe); \
if ((__HLUA)->state_id == 0) \
lua_drop_global_lock(); \
} while(0)
#define SET_SAFE_LJMP(__HLUA) \
SET_SAFE_LJMP_L((__HLUA)->T, __HLUA)
#define RESET_SAFE_LJMP(__HLUA) \
RESET_SAFE_LJMP_L((__HLUA)->T, __HLUA)
#define SET_SAFE_LJMP_PARENT(__HLUA) \
SET_SAFE_LJMP_L(hlua_states[(__HLUA)->state_id], __HLUA)
#define RESET_SAFE_LJMP_PARENT(__HLUA) \
RESET_SAFE_LJMP_L(hlua_states[(__HLUA)->state_id], __HLUA)
/* Applet status flags */
#define APPLET_DONE 0x01 /* applet processing is done. */
/* unused: 0x02 */
#define APPLET_HDR_SENT 0x04 /* Response header sent. */
/* unused: 0x08, 0x10 */
#define APPLET_HTTP11 0x20 /* Last chunk sent. */
#define APPLET_RSP_SENT 0x40 /* The response was fully sent */
/* The main Lua execution context. The 0 index is the
* common state shared by all threads.
*/
static lua_State *hlua_states[MAX_THREADS + 1];
#define HLUA_FLT_CB_FINAL 0x00000001
#define HLUA_FLT_CB_RETVAL 0x00000002
#define HLUA_FLT_CB_ARG_CHN 0x00000004
#define HLUA_FLT_CB_ARG_HTTP_MSG 0x00000008
#define HLUA_FLT_CTX_FL_PAYLOAD 0x00000001
struct hlua_reg_filter {
char *name;
int flt_ref[MAX_THREADS + 1];
int fun_ref[MAX_THREADS + 1];
struct list l;
};
struct hlua_flt_config {
struct hlua_reg_filter *reg;
int ref[MAX_THREADS + 1];
char **args;
};
struct hlua_flt_ctx {
int ref; /* ref to the filter lua object */
struct hlua *hlua[2]; /* lua runtime context (0: request, 1: response) */
unsigned int cur_off[2]; /* current offset (0: request, 1: response) */
unsigned int cur_len[2]; /* current forwardable length (0: request, 1: response) */
unsigned int flags; /* HLUA_FLT_CTX_FL_* */
};
DECLARE_STATIC_POOL(pool_head_hlua_flt_ctx, "hlua_flt_ctx", sizeof(struct hlua_flt_ctx));
static int hlua_filter_from_payload(struct filter *filter);
/* This is the chained list of struct hlua_flt referenced
* for haproxy filters. It is used for a post-initialisation control.
*/
static struct list referenced_filters = LIST_HEAD_INIT(referenced_filters);
/* This is the memory pool containing struct lua for applets
* (including cli).
*/
DECLARE_STATIC_POOL(pool_head_hlua, "hlua", sizeof(struct hlua));
/* Used for Socket connection. */
static struct proxy *socket_proxy;
static struct server *socket_tcp;
#ifdef USE_OPENSSL
static struct server *socket_ssl;
#endif
/* List head of the function called at the initialisation time. */
struct list hlua_init_functions[MAX_THREADS + 1];
/* The following variables contains the reference of the different
* Lua classes. These references are useful for identify metadata
* associated with an object.
*/
static int class_txn_ref;
static int class_socket_ref;
static int class_channel_ref;
static int class_fetches_ref;
static int class_converters_ref;
static int class_http_ref;
static int class_http_msg_ref;
static int class_map_ref;
static int class_applet_tcp_ref;
static int class_applet_http_ref;
static int class_txn_reply_ref;
/* Global Lua execution timeout. By default Lua, execution linked
* with stream (actions, sample-fetches and converters) have a
* short timeout. Lua linked with tasks doesn't have a timeout
* because a task may remain alive during all the haproxy execution.
*/
static unsigned int hlua_timeout_session = 4000; /* session timeout. */
static unsigned int hlua_timeout_task = TICK_ETERNITY; /* task timeout. */
static unsigned int hlua_timeout_applet = 4000; /* applet timeout. */
/* Interrupts the Lua processing each "hlua_nb_instruction" instructions.
* it is used for preventing infinite loops.
*
* I test the scheer with an infinite loop containing one incrementation
* and one test. I run this loop between 10 seconds, I raise a ceil of
* 710M loops from one interrupt each 9000 instructions, so I fix the value
* to one interrupt each 10 000 instructions.
*
* configured | Number of
* instructions | loops executed
* between two | in milions
* forced yields |
* ---------------+---------------
* 10 | 160
* 500 | 670
* 1000 | 680
* 5000 | 700
* 7000 | 700
* 8000 | 700
* 9000 | 710 <- ceil
* 10000 | 710
* 100000 | 710
* 1000000 | 710
*
*/
static unsigned int hlua_nb_instruction = 10000;
/* Descriptor for the memory allocation state. The limit is pre-initialised to
* 0 until it is replaced by "tune.lua.maxmem" during the config parsing, or it
* is replaced with ~0 during post_init after everything was loaded. This way
* it is guaranteed that if limit is ~0 the boot is complete and that if it's
* zero it's not yet limited and proper accounting is required.
*/
struct hlua_mem_allocator {
size_t allocated;
size_t limit;
};
static struct hlua_mem_allocator hlua_global_allocator THREAD_ALIGNED(64);
/* These functions converts types between HAProxy internal args or
* sample and LUA types. Another function permits to check if the
* LUA stack contains arguments according with an required ARG_T
* format.
*/
static int hlua_arg2lua(lua_State *L, const struct arg *arg);
static int hlua_lua2arg(lua_State *L, int ud, struct arg *arg);
__LJMP static int hlua_lua2arg_check(lua_State *L, int first, struct arg *argp,
uint64_t mask, struct proxy *p);
static int hlua_smp2lua(lua_State *L, struct sample *smp);
static int hlua_smp2lua_str(lua_State *L, struct sample *smp);
static int hlua_lua2smp(lua_State *L, int ud, struct sample *smp);
__LJMP static int hlua_http_get_headers(lua_State *L, struct http_msg *msg);
struct prepend_path {
struct list l;
char *type;
char *path;
};
static struct list prepend_path_list = LIST_HEAD_INIT(prepend_path_list);
#define SEND_ERR(__be, __fmt, __args...) \
do { \
send_log(__be, LOG_ERR, __fmt, ## __args); \
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) \
ha_alert(__fmt, ## __args); \
} while (0)
static inline struct hlua_function *new_hlua_function()
{
struct hlua_function *fcn;
int i;
fcn = calloc(1, sizeof(*fcn));
if (!fcn)
return NULL;
LIST_APPEND(&referenced_functions, &fcn->l);
for (i = 0; i < MAX_THREADS + 1; i++)
fcn->function_ref[i] = -1;
return fcn;
}
static inline void release_hlua_function(struct hlua_function *fcn)
{
if (!fcn)
return;
if (fcn->name)
ha_free(&fcn->name);
LIST_DELETE(&fcn->l);
ha_free(&fcn);
}
/* If the common state is set, the stack id is 0, otherwise it is the tid + 1 */
static inline int fcn_ref_to_stack_id(struct hlua_function *fcn)
{
if (fcn->function_ref[0] == -1)
return tid + 1;
return 0;
}
/* Create a new registered filter. Only its name is filled */
static inline struct hlua_reg_filter *new_hlua_reg_filter(const char *name)
{
struct hlua_reg_filter *reg_flt;
int i;
reg_flt = calloc(1, sizeof(*reg_flt));
if (!reg_flt)
return NULL;
reg_flt->name = strdup(name);
if (!reg_flt->name) {
free(reg_flt);
return NULL;
}
LIST_APPEND(&referenced_filters, ®_flt->l);
for (i = 0; i < MAX_THREADS + 1; i++) {
reg_flt->flt_ref[i] = -1;
reg_flt->fun_ref[i] = -1;
}
return reg_flt;
}
/* Release a registered filter */
static inline void release_hlua_reg_filter(struct hlua_reg_filter *reg_flt)
{
if (!reg_flt)
return;
if (reg_flt->name)
ha_free(®_flt->name);
LIST_DELETE(®_flt->l);
ha_free(®_flt);
}
/* If the common state is set, the stack id is 0, otherwise it is the tid + 1 */
static inline int reg_flt_to_stack_id(struct hlua_reg_filter *reg_flt)
{
if (reg_flt->fun_ref[0] == -1)
return tid + 1;
return 0;
}
/* Used to check an Lua function type in the stack. It creates and
* returns a reference of the function. This function throws an
* error if the rgument is not a "function".
*/
__LJMP unsigned int hlua_checkfunction(lua_State *L, int argno)
{
if (!lua_isfunction(L, argno)) {
const char *msg = lua_pushfstring(L, "function expected, got %s", luaL_typename(L, argno));
WILL_LJMP(luaL_argerror(L, argno, msg));
}
lua_pushvalue(L, argno);
return luaL_ref(L, LUA_REGISTRYINDEX);
}
/* Used to check an Lua table type in the stack. It creates and
* returns a reference of the table. This function throws an
* error if the rgument is not a "table".
*/
__LJMP unsigned int hlua_checktable(lua_State *L, int argno)
{
if (!lua_istable(L, argno)) {
const char *msg = lua_pushfstring(L, "table expected, got %s", luaL_typename(L, argno));
WILL_LJMP(luaL_argerror(L, argno, msg));
}
lua_pushvalue(L, argno);
return luaL_ref(L, LUA_REGISTRYINDEX);
}
/* Return the string that is of the top of the stack. */
const char *hlua_get_top_error_string(lua_State *L)
{
if (lua_gettop(L) < 1)
return "unknown error";
if (lua_type(L, -1) != LUA_TSTRING)
return "unknown error";
return lua_tostring(L, -1);
}
__LJMP const char *hlua_traceback(lua_State *L, const char* sep)
{
lua_Debug ar;
int level = 0;
struct buffer *msg = get_trash_chunk();
while (lua_getstack(L, level++, &ar)) {
/* Add separator */
if (b_data(msg))
chunk_appendf(msg, "%s", sep);
/* Fill fields:
* 'S': fills in the fields source, short_src, linedefined, lastlinedefined, and what;
* 'l': fills in the field currentline;
* 'n': fills in the field name and namewhat;
* 't': fills in the field istailcall;
*/
lua_getinfo(L, "Slnt", &ar);
/* Append code localisation */
if (ar.currentline > 0)
chunk_appendf(msg, "%s:%d: ", ar.short_src, ar.currentline);
else
chunk_appendf(msg, "%s: ", ar.short_src);
/*
* Get function name
*
* if namewhat is no empty, name is defined.
* what contains "Lua" for Lua function, "C" for C function,
* or "main" for main code.
*/
if (*ar.namewhat != '\0' && ar.name != NULL) /* is there a name from code? */
chunk_appendf(msg, "in %s '%s'", ar.namewhat, ar.name); /* use it */
else if (*ar.what == 'm') /* "main", the code is not executed in a function */
chunk_appendf(msg, "in main chunk");
else if (*ar.what != 'C') /* for Lua functions, use <file:line> */
chunk_appendf(msg, "in function line %d", ar.linedefined);
else /* nothing left... */
chunk_appendf(msg, "?");
/* Display tailed call */
if (ar.istailcall)
chunk_appendf(msg, " ...");
}
return msg->area;
}
/* This function check the number of arguments available in the
* stack. If the number of arguments available is not the same
* then <nb> an error is thrown.
*/
__LJMP static inline void check_args(lua_State *L, int nb, char *fcn)
{
if (lua_gettop(L) == nb)
return;
WILL_LJMP(luaL_error(L, "'%s' needs %d arguments", fcn, nb));
}
/* This function pushes an error string prefixed by the file name
* and the line number where the error is encountered.
*/
static int hlua_pusherror(lua_State *L, const char *fmt, ...)
{
va_list argp;
va_start(argp, fmt);
luaL_where(L, 1);
lua_pushvfstring(L, fmt, argp);
va_end(argp);
lua_concat(L, 2);
return 1;
}
/* This functions is used with sample fetch and converters. It
* converts the HAProxy configuration argument in a lua stack
* values.
*
* It takes an array of "arg", and each entry of the array is
* converted and pushed in the LUA stack.
*/
static int hlua_arg2lua(lua_State *L, const struct arg *arg)
{
switch (arg->type) {
case ARGT_SINT:
case ARGT_TIME:
case ARGT_SIZE:
lua_pushinteger(L, arg->data.sint);
break;
case ARGT_STR:
lua_pushlstring(L, arg->data.str.area, arg->data.str.data);
break;
case ARGT_IPV4:
case ARGT_IPV6:
case ARGT_MSK4:
case ARGT_MSK6:
case ARGT_FE:
case ARGT_BE:
case ARGT_TAB:
case ARGT_SRV:
case ARGT_USR:
case ARGT_MAP:
default:
lua_pushnil(L);
break;
}
return 1;
}
/* This function take one entry in an LUA stack at the index "ud",
* and try to convert it in an HAProxy argument entry. This is useful
* with sample fetch wrappers. The input arguments are given to the
* lua wrapper and converted as arg list by the function.
*/
static int hlua_lua2arg(lua_State *L, int ud, struct arg *arg)
{
switch (lua_type(L, ud)) {
case LUA_TNUMBER:
case LUA_TBOOLEAN:
arg->type = ARGT_SINT;
arg->data.sint = lua_tointeger(L, ud);
break;
case LUA_TSTRING:
arg->type = ARGT_STR;
arg->data.str.area = (char *)lua_tolstring(L, ud, &arg->data.str.data);
/* We don't know the actual size of the underlying allocation, so be conservative. */
arg->data.str.size = arg->data.str.data+1; /* count the terminating null byte */
arg->data.str.head = 0;
break;
case LUA_TUSERDATA:
case LUA_TNIL:
case LUA_TTABLE:
case LUA_TFUNCTION:
case LUA_TTHREAD:
case LUA_TLIGHTUSERDATA:
arg->type = ARGT_SINT;
arg->data.sint = 0;
break;
}
return 1;
}
/* the following functions are used to convert a struct sample
* in Lua type. This useful to convert the return of the
* fetches or converters.
*/
static int hlua_smp2lua(lua_State *L, struct sample *smp)
{
switch (smp->data.type) {
case SMP_T_SINT:
case SMP_T_BOOL:
lua_pushinteger(L, smp->data.u.sint);
break;
case SMP_T_BIN:
case SMP_T_STR:
lua_pushlstring(L, smp->data.u.str.area, smp->data.u.str.data);
break;
case SMP_T_METH:
switch (smp->data.u.meth.meth) {
case HTTP_METH_OPTIONS: lua_pushstring(L, "OPTIONS"); break;
case HTTP_METH_GET: lua_pushstring(L, "GET"); break;
case HTTP_METH_HEAD: lua_pushstring(L, "HEAD"); break;
case HTTP_METH_POST: lua_pushstring(L, "POST"); break;
case HTTP_METH_PUT: lua_pushstring(L, "PUT"); break;
case HTTP_METH_DELETE: lua_pushstring(L, "DELETE"); break;
case HTTP_METH_TRACE: lua_pushstring(L, "TRACE"); break;
case HTTP_METH_CONNECT: lua_pushstring(L, "CONNECT"); break;
case HTTP_METH_OTHER:
lua_pushlstring(L, smp->data.u.meth.str.area, smp->data.u.meth.str.data);
break;
default:
lua_pushnil(L);
break;
}
break;
case SMP_T_IPV4:
case SMP_T_IPV6:
case SMP_T_ADDR: /* This type is never used to qualify a sample. */
if (sample_casts[smp->data.type][SMP_T_STR] &&
sample_casts[smp->data.type][SMP_T_STR](smp))
lua_pushlstring(L, smp->data.u.str.area, smp->data.u.str.data);
else
lua_pushnil(L);
break;
default:
lua_pushnil(L);
break;
}
return 1;
}
/* the following functions are used to convert a struct sample
* in Lua strings. This is useful to convert the return of the
* fetches or converters.
*/
static int hlua_smp2lua_str(lua_State *L, struct sample *smp)
{
switch (smp->data.type) {
case SMP_T_BIN:
case SMP_T_STR:
lua_pushlstring(L, smp->data.u.str.area, smp->data.u.str.data);
break;
case SMP_T_METH:
switch (smp->data.u.meth.meth) {
case HTTP_METH_OPTIONS: lua_pushstring(L, "OPTIONS"); break;
case HTTP_METH_GET: lua_pushstring(L, "GET"); break;
case HTTP_METH_HEAD: lua_pushstring(L, "HEAD"); break;
case HTTP_METH_POST: lua_pushstring(L, "POST"); break;
case HTTP_METH_PUT: lua_pushstring(L, "PUT"); break;
case HTTP_METH_DELETE: lua_pushstring(L, "DELETE"); break;
case HTTP_METH_TRACE: lua_pushstring(L, "TRACE"); break;
case HTTP_METH_CONNECT: lua_pushstring(L, "CONNECT"); break;
case HTTP_METH_OTHER:
lua_pushlstring(L, smp->data.u.meth.str.area, smp->data.u.meth.str.data);
break;
default:
lua_pushstring(L, "");
break;
}
break;
case SMP_T_SINT:
case SMP_T_BOOL:
case SMP_T_IPV4:
case SMP_T_IPV6:
case SMP_T_ADDR: /* This type is never used to qualify a sample. */
if (sample_casts[smp->data.type][SMP_T_STR] &&
sample_casts[smp->data.type][SMP_T_STR](smp))
lua_pushlstring(L, smp->data.u.str.area, smp->data.u.str.data);
else
lua_pushstring(L, "");
break;
default:
lua_pushstring(L, "");
break;
}
return 1;
}
/* the following functions are used to convert an Lua type in a
* struct sample. This is useful to provide data from a converter
* to the LUA code.
*/
static int hlua_lua2smp(lua_State *L, int ud, struct sample *smp)
{
switch (lua_type(L, ud)) {
case LUA_TNUMBER:
smp->data.type = SMP_T_SINT;
smp->data.u.sint = lua_tointeger(L, ud);
break;
case LUA_TBOOLEAN:
smp->data.type = SMP_T_BOOL;
smp->data.u.sint = lua_toboolean(L, ud);
break;
case LUA_TSTRING:
smp->data.type = SMP_T_STR;
smp->flags |= SMP_F_CONST;
smp->data.u.str.area = (char *)lua_tolstring(L, ud, &smp->data.u.str.data);
/* We don't know the actual size of the underlying allocation, so be conservative. */
smp->data.u.str.size = smp->data.u.str.data+1; /* count the terminating null byte */
smp->data.u.str.head = 0;
break;
case LUA_TUSERDATA:
case LUA_TNIL:
case LUA_TTABLE:
case LUA_TFUNCTION:
case LUA_TTHREAD:
case LUA_TLIGHTUSERDATA:
case LUA_TNONE:
default:
smp->data.type = SMP_T_BOOL;
smp->data.u.sint = 0;
break;
}
return 1;
}
/* This function check the "argp" built by another conversion function
* is in accord with the expected argp defined by the "mask". The function
* returns true or false. It can be adjust the types if there compatibles.
*
* This function assumes that the argp argument contains ARGM_NBARGS + 1
* entries and that there is at least one stop at the last position.
*/
__LJMP int hlua_lua2arg_check(lua_State *L, int first, struct arg *argp,
uint64_t mask, struct proxy *p)
{
int min_arg;
int idx;
struct proxy *px;
struct userlist *ul;
struct my_regex *reg;
const char *msg = NULL;
char *sname, *pname, *err = NULL;
idx = 0;
min_arg = ARGM(mask);
mask >>= ARGM_BITS;
while (1) {
struct buffer tmp = BUF_NULL;
/* Check for mandatory arguments. */
if (argp[idx].type == ARGT_STOP) {
if (idx < min_arg) {
/* If miss other argument than the first one, we return an error. */
if (idx > 0) {
msg = "Mandatory argument expected";
goto error;
}
/* If first argument have a certain type, some default values
* may be used. See the function smp_resolve_args().
*/
switch (mask & ARGT_MASK) {
case ARGT_FE:
if (!(p->cap & PR_CAP_FE)) {
msg = "Mandatory argument expected";
goto error;
}
argp[idx].data.prx = p;
argp[idx].type = ARGT_FE;
argp[idx+1].type = ARGT_STOP;
break;
case ARGT_BE:
if (!(p->cap & PR_CAP_BE)) {
msg = "Mandatory argument expected";
goto error;
}
argp[idx].data.prx = p;
argp[idx].type = ARGT_BE;
argp[idx+1].type = ARGT_STOP;
break;
case ARGT_TAB:
argp[idx].data.prx = p;
argp[idx].type = ARGT_TAB;
argp[idx+1].type = ARGT_STOP;
break;
default:
msg = "Mandatory argument expected";
goto error;
break;
}
}
break;
}
/* Check for exceed the number of required argument. */
if ((mask & ARGT_MASK) == ARGT_STOP &&
argp[idx].type != ARGT_STOP) {
msg = "Last argument expected";
goto error;
}
if ((mask & ARGT_MASK) == ARGT_STOP &&
argp[idx].type == ARGT_STOP) {
break;
}
/* Convert some argument types. All string in argp[] are for not
* duplicated yet.
*/
switch (mask & ARGT_MASK) {
case ARGT_SINT:
if (argp[idx].type != ARGT_SINT) {
msg = "integer expected";
goto error;
}
argp[idx].type = ARGT_SINT;
break;
case ARGT_TIME:
if (argp[idx].type != ARGT_SINT) {
msg = "integer expected";
goto error;
}
argp[idx].type = ARGT_TIME;
break;
case ARGT_SIZE:
if (argp[idx].type != ARGT_SINT) {
msg = "integer expected";
goto error;
}
argp[idx].type = ARGT_SIZE;
break;
case ARGT_FE:
if (argp[idx].type != ARGT_STR) {
msg = "string expected";
goto error;
}
argp[idx].data.prx = proxy_fe_by_name(argp[idx].data.str.area);
if (!argp[idx].data.prx) {
msg = "frontend doesn't exist";
goto error;
}
argp[idx].type = ARGT_FE;
break;
case ARGT_BE:
if (argp[idx].type != ARGT_STR) {
msg = "string expected";
goto error;
}
argp[idx].data.prx = proxy_be_by_name(argp[idx].data.str.area);
if (!argp[idx].data.prx) {
msg = "backend doesn't exist";
goto error;
}
argp[idx].type = ARGT_BE;
break;
case ARGT_TAB:
if (argp[idx].type != ARGT_STR) {
msg = "string expected";
goto error;
}
argp[idx].data.t = stktable_find_by_name(argp[idx].data.str.area);
if (!argp[idx].data.t) {
msg = "table doesn't exist";
goto error;
}
argp[idx].type = ARGT_TAB;
break;
case ARGT_SRV:
if (argp[idx].type != ARGT_STR) {
msg = "string expected";
goto error;
}
sname = strrchr(argp[idx].data.str.area, '/');
if (sname) {
*sname++ = '\0';
pname = argp[idx].data.str.area;
px = proxy_be_by_name(pname);
if (!px) {
msg = "backend doesn't exist";
goto error;
}
}
else {
sname = argp[idx].data.str.area;
px = p;
}
argp[idx].data.srv = findserver(px, sname);
if (!argp[idx].data.srv) {
msg = "server doesn't exist";
goto error;
}
argp[idx].type = ARGT_SRV;
break;
case ARGT_IPV4:
if (argp[idx].type != ARGT_STR) {
msg = "string expected";
goto error;
}
if (inet_pton(AF_INET, argp[idx].data.str.area, &argp[idx].data.ipv4)) {
msg = "invalid IPv4 address";
goto error;
}
argp[idx].type = ARGT_IPV4;
break;
case ARGT_MSK4:
if (argp[idx].type == ARGT_SINT)
len2mask4(argp[idx].data.sint, &argp[idx].data.ipv4);
else if (argp[idx].type == ARGT_STR) {
if (!str2mask(argp[idx].data.str.area, &argp[idx].data.ipv4)) {
msg = "invalid IPv4 mask";
goto error;
}
}
else {
msg = "integer or string expected";
goto error;
}
argp[idx].type = ARGT_MSK4;
break;
case ARGT_IPV6:
if (argp[idx].type != ARGT_STR) {
msg = "string expected";
goto error;
}
if (inet_pton(AF_INET6, argp[idx].data.str.area, &argp[idx].data.ipv6)) {
msg = "invalid IPv6 address";
goto error;
}
argp[idx].type = ARGT_IPV6;
break;
case ARGT_MSK6:
if (argp[idx].type == ARGT_SINT)
len2mask6(argp[idx].data.sint, &argp[idx].data.ipv6);
else if (argp[idx].type == ARGT_STR) {
if (!str2mask6(argp[idx].data.str.area, &argp[idx].data.ipv6)) {
msg = "invalid IPv6 mask";
goto error;
}
}
else {
msg = "integer or string expected";