blob: c8b640f228dc3cca36e8e607fee7a1008bbfbc1b [file] [log] [blame]
Thierry Fourniere726b142016-02-11 17:57:57 +01001/*
2 * Lua safe functions
3 *
4 * Copyright 2015-2016 Thierry Fournier <tfournier@arpalert.org>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 *
12 * All the functions in this file runs with a Lua stack, and can
Thierry Fournierfb0b5462016-01-21 09:28:58 +010013 * return with a longjmp. All of these function must be launched
14 * in an environment able to catch a longjmp, otherwise a
15 * critical error can be raised.
16 */
17#include <lauxlib.h>
18#include <lua.h>
19#include <lualib.h>
20
Thierry Fournierb1f46562016-01-21 09:46:15 +010021#include <common/time.h>
Thierry Fournierf61aa632016-02-19 20:56:00 +010022#include <common/uri_auth.h>
Thierry Fournierb1f46562016-01-21 09:46:15 +010023
Thierry Fournier49d48422016-02-19 12:09:29 +010024#include <types/hlua.h>
Thierry Fournierf61aa632016-02-19 20:56:00 +010025#include <types/proxy.h>
Thierry Fournier49d48422016-02-19 12:09:29 +010026
Thierry Fournier8b0d6e12016-03-16 18:29:13 +010027#include <proto/dumpstats.h>
Thierry Fournier94ed1c12016-02-24 08:06:32 +010028#include <proto/proto_http.h>
Thierry Fournierf61aa632016-02-19 20:56:00 +010029#include <proto/proxy.h>
30#include <proto/server.h>
Thierry Fournier94ed1c12016-02-24 08:06:32 +010031
Thierry Fournier1de16592016-01-27 09:49:07 +010032/* Contains the class reference of the concat object. */
33static int class_concat_ref;
Thierry Fournierf61aa632016-02-19 20:56:00 +010034static int class_proxy_ref;
Thierry Fournier1de16592016-01-27 09:49:07 +010035
Thierry Fournierf61aa632016-02-19 20:56:00 +010036#define STATS_LEN (MAX((int)ST_F_TOTAL_FIELDS, (int)INF_TOTAL_FIELDS))
Thierry Fourniereea77c02016-03-18 08:47:13 +010037
38static struct field stats[STATS_LEN];
39
Thierry Fournier8b0d6e12016-03-16 18:29:13 +010040/* This function gets a struct field and convert it in Lua
41 * variable. The variable is pushed at the top of the stak.
42 */
43int hlua_fcn_pushfield(lua_State *L, struct field *field)
44{
45 /* The lua_Integer is always signed. Its length depends on
46 * compilation opions, so the followinfg code is conditionned
47 * by some macros. Windows maros are not supported.
48 * If the number cannot be represented as integer, we try to
49 * convert to float.
50 */
51 switch (field_format(field, 0)) {
52
53 case FF_EMPTY:
54 lua_pushnil(L);
55 return 1;
56
57 case FF_S32:
58 /* S32 is always supported. */
59 lua_pushinteger(L, field->u.s32);
60 return 1;
61
62 case FF_U32:
63#if (LUA_MAXINTEGER == LLONG_MAX || ((LUA_MAXINTEGER == LONG_MAX) && (__WORDSIZE == 64)))
64 /* 64 bits case, U32 is always supported */
65 lua_pushinteger(L, field->u.u32);
66#else
67 /* 32 bits case, U32 is supported until INT_MAX. */
68 if (field->u.u32 > INT_MAX)
69 lua_pushnumber(L, (lua_Number)field->u.u32);
70 else
71 lua_pushinteger(L, field->u.u32);
72#endif
73 return 1;
74
75 case FF_S64:
76#if (LUA_MAXINTEGER == LLONG_MAX || ((LUA_MAXINTEGER == LONG_MAX) && (__WORDSIZE == 64)))
77 /* 64 bits case, S64 is always supported */
78 lua_pushinteger(L, field->u.s64);
79#else
80 /* 64 bits case, S64 is supported beetween INT_MIN and INT_MAX */
81 if (field->u.s64 < INT_MIN || field->u.s64 > INT_MAX)
82 lua_pushnumber(L, (lua_Number)field->u.s64);
83 else
84 lua_pushinteger(L, (int)field->u.s64);
85#endif
86 return 1;
87
88 case FF_U64:
89#if (LUA_MAXINTEGER == LLONG_MAX || ((LUA_MAXINTEGER == LONG_MAX) && (__WORDSIZE == 64)))
90 /* 64 bits case, U64 is supported until LLONG_MAX */
91 if (field->u.u64 > LLONG_MAX)
92 lua_pushnumber(L, (lua_Number)field->u.u64);
93 else
94 lua_pushinteger(L, field->u.u64);
95#else
96 /* 64 bits case, U64 is supported until INT_MAX */
97 if (field->u.u64 > INT_MAX)
98 lua_pushnumber(L, (lua_Number)field->u.u64);
99 else
100 lua_pushinteger(L, (int)field->u.u64);
101#endif
102 return 1;
103
104 case FF_STR:
105 lua_pushstring(L, field->u.str);
106 return 1;
107
108 default:
109 break;
110 }
111
112 /* Default case, never reached. */
113 lua_pushnil(L);
114 return 1;
115}
116
Thierry Fournier94ed1c12016-02-24 08:06:32 +0100117/* Some string are started or terminated by blank chars,
118 * this function removes the spaces, tabs, \r and
119 * \n at the begin and at the end of the string "str", and
120 * push the result in the lua stack.
121 * Returns a pointer to the Lua internal copy of the string.
122 */
123const char *hlua_pushstrippedstring(lua_State *L, const char *str)
124{
125 const char *p;
126 const char *e;
127
128 for (p = str; HTTP_IS_LWS(*p); p++);
129 for (e = p + strlen(p) - 1; e > p && HTTP_IS_LWS(*e); e--);
130
131 return lua_pushlstring(L, p, e - p);
132}
133
Thierry Fournierddd89882016-02-22 19:52:08 +0100134/* The three following functions are useful for adding entries
135 * in a table. These functions takes a string and respectively an
136 * integer, a string or a function and add it to the table in the
137 * top of the stack.
138 *
139 * These functions throws an error if no more stack size is
140 * available.
141 */
142void hlua_class_const_int(lua_State *L, const char *name, int value)
143{
Thierry Fournierddd89882016-02-22 19:52:08 +0100144 lua_pushstring(L, name);
145 lua_pushinteger(L, value);
146 lua_rawset(L, -3);
147}
148void hlua_class_const_str(lua_State *L, const char *name, const char *value)
149{
Thierry Fournierddd89882016-02-22 19:52:08 +0100150 lua_pushstring(L, name);
151 lua_pushstring(L, value);
152 lua_rawset(L, -3);
153}
154void hlua_class_function(lua_State *L, const char *name, int (*function)(lua_State *L))
155{
Thierry Fournierddd89882016-02-22 19:52:08 +0100156 lua_pushstring(L, name);
157 lua_pushcclosure(L, function, 0);
158 lua_rawset(L, -3);
159}
160
161/* This function returns a string containg the HAProxy object name. */
162int hlua_dump_object(struct lua_State *L)
163{
164 const char *name = (const char *)lua_tostring(L, lua_upvalueindex(1));
165 lua_pushfstring(L, "HAProxy class %s", name);
166 return 1;
167}
168
Thierry Fournier45e78d72016-02-19 18:34:46 +0100169/* This function register a table as metatable and. It names
170 * the metatable, and returns the associated reference.
171 * The original table is poped from the top of the stack.
172 * "name" is the referenced class name.
173 */
174int hlua_register_metatable(struct lua_State *L, char *name)
175{
176 /* Check the type of the top element. it must be
177 * a table.
178 */
179 if (lua_type(L, -1) != LUA_TTABLE)
180 luaL_error(L, "hlua_register_metatable() requires a type Table "
181 "in the top of the stack");
182
183 /* Add the __tostring function which identify the
184 * created object.
185 */
186 lua_pushstring(L, "__tostring");
187 lua_pushstring(L, name);
188 lua_pushcclosure(L, hlua_dump_object, 1);
189 lua_rawset(L, -3);
190
191 /* Register a named entry for the table. The table
192 * reference is copyed first because the function
193 * lua_setfield() pop the entry.
194 */
195 lua_pushvalue(L, -1);
196 lua_setfield(L, LUA_REGISTRYINDEX, name);
197
198 /* Creates the reference of the object. The
199 * function luaL_ref pop the top of the stack.
200 */
201 return luaL_ref(L, LUA_REGISTRYINDEX);
202}
203
Thierry Fournier9e7e3ea2016-01-27 09:55:30 +0100204/* Return an object of the expected type, or throws an error. */
205void *hlua_checkudata(lua_State *L, int ud, int class_ref)
206{
207 void *p;
Thierry Fournier53518272016-01-27 10:34:09 +0100208 int ret;
Thierry Fournier9e7e3ea2016-01-27 09:55:30 +0100209
210 /* Check if the stack entry is an array. */
211 if (!lua_istable(L, ud))
Thierry Fournier53518272016-01-27 10:34:09 +0100212 luaL_argerror(L, ud, NULL);
213
214 /* pop the metatable of the referencecd object. */
215 if (!lua_getmetatable(L, ud))
216 luaL_argerror(L, ud, NULL);
217
218 /* pop the expected metatable. */
219 lua_rawgeti(L, LUA_REGISTRYINDEX, class_ref);
220
Thierry Fournier9e7e3ea2016-01-27 09:55:30 +0100221 /* Check if the metadata have the expected type. */
Thierry Fournier53518272016-01-27 10:34:09 +0100222 ret = lua_rawequal(L, -1, -2);
223 lua_pop(L, 2);
224 if (!ret)
225 luaL_argerror(L, ud, NULL);
226
Thierry Fournier9e7e3ea2016-01-27 09:55:30 +0100227 /* Push on the stack at the entry [0] of the table. */
228 lua_rawgeti(L, ud, 0);
Thierry Fournier53518272016-01-27 10:34:09 +0100229
Thierry Fournier9e7e3ea2016-01-27 09:55:30 +0100230 /* Check if this entry is userdata. */
231 p = lua_touserdata(L, -1);
232 if (!p)
Thierry Fournier53518272016-01-27 10:34:09 +0100233 luaL_argerror(L, ud, NULL);
234
Thierry Fournier9e7e3ea2016-01-27 09:55:30 +0100235 /* Remove the entry returned by lua_rawgeti(). */
236 lua_pop(L, 1);
Thierry Fournier53518272016-01-27 10:34:09 +0100237
Thierry Fournier9e7e3ea2016-01-27 09:55:30 +0100238 /* Return the associated struct. */
239 return p;
240}
241
Thierry Fournierb1f46562016-01-21 09:46:15 +0100242/* This function return the current date at epoch format in milliseconds. */
243int hlua_now(lua_State *L)
244{
245 lua_newtable(L);
246 lua_pushstring(L, "sec");
247 lua_pushinteger(L, now.tv_sec);
248 lua_rawset(L, -3);
249 lua_pushstring(L, "usec");
250 lua_pushinteger(L, now.tv_usec);
251 lua_rawset(L, -3);
252 return 1;
253}
254
Thierry Fournier1550d5d2016-01-21 09:35:41 +0100255/* This functions expects a Lua string as HTTP date, parse it and
256 * returns an integer containing the epoch format of the date, or
257 * nil if the parsing fails.
258 */
259static int hlua_parse_date(lua_State *L, int (*fcn)(const char *, int, struct tm*))
260{
261 const char *str;
262 size_t len;
263 struct tm tm;
264 time_t time;
265
266 str = luaL_checklstring(L, 1, &len);
267
268 if (!fcn(str, len, &tm)) {
269 lua_pushnil(L);
270 return 1;
271 }
272
273 /* This function considers the content of the broken-down time
274 * is exprimed in the UTC timezone. timegm don't care about
275 * the gnu variable tm_gmtoff. If gmtoff is set, or if you know
276 * the timezone from the broken-down time, it must be fixed
277 * after the conversion.
278 */
279 time = timegm(&tm);
280 if (time == -1) {
281 lua_pushnil(L);
282 return 1;
283 }
284
285 lua_pushinteger(L, (int)time);
286 return 1;
287}
288static int hlua_http_date(lua_State *L)
289{
290 return hlua_parse_date(L, parse_http_date);
291}
292static int hlua_imf_date(lua_State *L)
293{
294 return hlua_parse_date(L, parse_imf_date);
295}
296static int hlua_rfc850_date(lua_State *L)
297{
298 return hlua_parse_date(L, parse_rfc850_date);
299}
300static int hlua_asctime_date(lua_State *L)
301{
302 return hlua_parse_date(L, parse_asctime_date);
303}
304
Thierry Fourniereea77c02016-03-18 08:47:13 +0100305static int hlua_get_info(lua_State *L)
306{
307 int i;
308
309 stats_fill_info(stats, STATS_LEN);
310
311 lua_newtable(L);
312 for (i=0; i<INF_TOTAL_FIELDS; i++) {
313 lua_pushstring(L, info_field_names[i]);
314 hlua_fcn_pushfield(L, &stats[i]);
315 lua_settable(L, -3);
316 }
317 return 1;
318}
319
Thierry Fournier49d48422016-02-19 12:09:29 +0100320static struct hlua_concat *hlua_check_concat(lua_State *L, int ud)
Thierry Fournier1de16592016-01-27 09:49:07 +0100321{
Thierry Fournier49d48422016-02-19 12:09:29 +0100322 return (struct hlua_concat *)(hlua_checkudata(L, ud, class_concat_ref));
Thierry Fournier1de16592016-01-27 09:49:07 +0100323}
324
325static int hlua_concat_add(lua_State *L)
326{
Thierry Fournier49d48422016-02-19 12:09:29 +0100327 struct hlua_concat *b;
328 char *buffer;
329 char *new;
Thierry Fournier1de16592016-01-27 09:49:07 +0100330 const char *str;
331 size_t l;
332
333 /* First arg must be a concat object. */
334 b = hlua_check_concat(L, 1);
335
336 /* Second arg must be a string. */
337 str = luaL_checklstring(L, 2, &l);
338
Thierry Fournier49d48422016-02-19 12:09:29 +0100339 /* Get the buffer. */
340 lua_rawgeti(L, 1, 1);
341 buffer = lua_touserdata(L, -1);
342 lua_pop(L, 1);
343
344 /* Update the buffer size if it s required. The old buffer
345 * is crushed by the new in the object array, so it will
346 * be deleted by the GC.
347 * Note that in the first loop, the "new" variable is only
348 * used as a flag.
349 */
350 new = NULL;
351 while (b->size - b->len < l) {
352 b->size += HLUA_CONCAT_BLOCSZ;
353 new = buffer;
354 }
355 if (new) {
356 new = lua_newuserdata(L, b->size);
357 memcpy(new, buffer, b->len);
358 lua_rawseti(L, 1, 1);
359 buffer = new;
360 }
361
362 /* Copy string, and update metadata. */
363 memcpy(buffer + b->len, str, l);
364 b->len += l;
Thierry Fournier1de16592016-01-27 09:49:07 +0100365 return 0;
366}
367
368static int hlua_concat_dump(lua_State *L)
369{
Thierry Fournier49d48422016-02-19 12:09:29 +0100370 struct hlua_concat *b;
371 char *buffer;
Thierry Fournier1de16592016-01-27 09:49:07 +0100372
373 /* First arg must be a concat object. */
374 b = hlua_check_concat(L, 1);
375
Thierry Fournier49d48422016-02-19 12:09:29 +0100376 /* Get the buffer. */
377 lua_rawgeti(L, 1, 1);
378 buffer = lua_touserdata(L, -1);
379 lua_pop(L, 1);
380
Thierry Fournier1de16592016-01-27 09:49:07 +0100381 /* Push the soncatenated strng in the stack. */
Thierry Fournier49d48422016-02-19 12:09:29 +0100382 lua_pushlstring(L, buffer, b->len);
Thierry Fournier1de16592016-01-27 09:49:07 +0100383 return 1;
384}
385
386int hlua_concat_new(lua_State *L)
387{
Thierry Fournier49d48422016-02-19 12:09:29 +0100388 struct hlua_concat *b;
Thierry Fournier1de16592016-01-27 09:49:07 +0100389
390 lua_newtable(L);
Thierry Fournier49d48422016-02-19 12:09:29 +0100391 b = (struct hlua_concat *)lua_newuserdata(L, sizeof(*b));
392 b->size = HLUA_CONCAT_BLOCSZ;
393 b->len = 0;
Thierry Fournier1de16592016-01-27 09:49:07 +0100394 lua_rawseti(L, -2, 0);
Thierry Fournier49d48422016-02-19 12:09:29 +0100395 lua_newuserdata(L, HLUA_CONCAT_BLOCSZ);
396 lua_rawseti(L, -2, 1);
Thierry Fournier1de16592016-01-27 09:49:07 +0100397
398 lua_rawgeti(L, LUA_REGISTRYINDEX, class_concat_ref);
399 lua_setmetatable(L, -2);
400
Thierry Fournier1de16592016-01-27 09:49:07 +0100401 return 1;
402}
403
404static int concat_tostring(lua_State *L)
405{
406 const void *ptr = lua_topointer(L, 1);
407 lua_pushfstring(L, "Concat object: %p", ptr);
408 return 1;
409}
410
411static int hlua_concat_init(lua_State *L)
412{
413 /* Creates the buffered concat object. */
414 lua_newtable(L);
415
416 lua_pushstring(L, "__tostring");
417 lua_pushcclosure(L, concat_tostring, 0);
418 lua_settable(L, -3);
419
420 lua_pushstring(L, "__index"); /* Creates the index entry. */
421 lua_newtable(L); /* The "__index" content. */
422
423 lua_pushstring(L, "add");
424 lua_pushcclosure(L, hlua_concat_add, 0);
425 lua_settable(L, -3);
426
427 lua_pushstring(L, "dump");
428 lua_pushcclosure(L, hlua_concat_dump, 0);
429 lua_settable(L, -3);
430
431 lua_settable(L, -3); /* Sets the __index entry. */
432 class_concat_ref = luaL_ref(L, LUA_REGISTRYINDEX);
433
434 return 1;
435}
436
Thierry Fournierf61aa632016-02-19 20:56:00 +0100437int hlua_fcn_new_proxy(lua_State *L, struct proxy *px)
438{
439 lua_newtable(L);
440
441 /* Pop a class sesison metatable and affect it to the userdata. */
442 lua_rawgeti(L, LUA_REGISTRYINDEX, class_proxy_ref);
443 lua_setmetatable(L, -2);
444
445 lua_pushlightuserdata(L, px);
446 lua_rawseti(L, -2, 0);
447 return 1;
448}
449
450static struct proxy *hlua_check_proxy(lua_State *L, int ud)
451{
452 return (struct proxy *)(hlua_checkudata(L, ud, class_proxy_ref));
453}
454
455int hlua_proxy_pause(lua_State *L)
456{
457 struct proxy *px;
458
459 px = hlua_check_proxy(L, 1);
460 pause_proxy(px);
461 return 0;
462}
463
464int hlua_proxy_resume(lua_State *L)
465{
466 struct proxy *px;
467
468 px = hlua_check_proxy(L, 1);
469 resume_proxy(px);
470 return 0;
471}
472
473int hlua_proxy_stop(lua_State *L)
474{
475 struct proxy *px;
476
477 px = hlua_check_proxy(L, 1);
478 stop_proxy(px);
479 return 0;
480}
481
482int hlua_proxy_get_cap(lua_State *L)
483{
484 struct proxy *px;
485 const char *str;
486
487 px = hlua_check_proxy(L, 1);
488 str = proxy_cap_str(px->cap);
489 lua_pushstring(L, str);
490 return 1;
491}
492
493int hlua_proxy_get_stats(lua_State *L)
494{
495 struct proxy *px;
496 int i;
497
498 px = hlua_check_proxy(L, 1);
499 if (px->cap & PR_CAP_BE)
500 stats_fill_be_stats(px, ST_SHLGNDS, stats, STATS_LEN);
501 else
502 stats_fill_fe_stats(px, stats, STATS_LEN);
503 lua_newtable(L);
504 for (i=0; i<ST_F_TOTAL_FIELDS; i++) {
505 lua_pushstring(L, stat_field_names[i]);
506 hlua_fcn_pushfield(L, &stats[i]);
507 lua_settable(L, -3);
508 }
509 return 1;
510}
511
512int hlua_proxy_get_mode(lua_State *L)
513{
514 struct proxy *px;
515 const char *str;
516
517 px = hlua_check_proxy(L, 1);
518 str = proxy_mode_str(px->mode);
519 lua_pushstring(L, str);
520 return 1;
521}
522
523int hlua_proxy_shut_bcksess(lua_State *L)
524{
525 struct proxy *px;
526
527 px = hlua_check_proxy(L, 1);
528 srv_shutdown_backup_streams(px, SF_ERR_KILLED);
529 return 0;
530}
531
Thierry Fournier3d4a6752016-02-19 20:53:30 +0100532int hlua_fcn_post_init(lua_State *L)
533{
Thierry Fournierf61aa632016-02-19 20:56:00 +0100534 struct proxy *px;
535
536 /* get core array. */
537 if (lua_getglobal(L, "core") != LUA_TTABLE)
538 lua_error(L);
539
540 /* Create proxies entry. */
541 lua_pushstring(L, "proxies");
542 lua_newtable(L);
543
544 /* List all proxies. */
545 for (px = proxy; px; px = px->next) {
546 lua_pushstring(L, px->id);
547 hlua_fcn_new_proxy(L, px);
548 lua_settable(L, -3);
549 }
550
551 /* push "proxies" in "core" */
552 lua_settable(L, -3);
553
Thierry Fournier3d4a6752016-02-19 20:53:30 +0100554 return 1;
555}
556
Thierry Fournierfb0b5462016-01-21 09:28:58 +0100557int hlua_fcn_reg_core_fcn(lua_State *L)
558{
Thierry Fournier1de16592016-01-27 09:49:07 +0100559 if (!hlua_concat_init(L))
560 return 0;
561
Thierry Fournier4f99b272016-02-22 08:40:02 +0100562 hlua_class_function(L, "now", hlua_now);
563 hlua_class_function(L, "http_date", hlua_http_date);
564 hlua_class_function(L, "imf_date", hlua_imf_date);
565 hlua_class_function(L, "rfc850_date", hlua_rfc850_date);
566 hlua_class_function(L, "asctime_date", hlua_asctime_date);
567 hlua_class_function(L, "concat", hlua_concat_new);
Thierry Fourniereea77c02016-03-18 08:47:13 +0100568 hlua_class_function(L, "get_info", hlua_get_info);
Thierry Fournier4f99b272016-02-22 08:40:02 +0100569
Thierry Fournierf61aa632016-02-19 20:56:00 +0100570 /* Create proxy object. */
571 lua_newtable(L);
572 lua_pushstring(L, "__index");
573 lua_newtable(L);
574 hlua_class_function(L, "pause", hlua_proxy_pause);
575 hlua_class_function(L, "resume", hlua_proxy_resume);
576 hlua_class_function(L, "stop", hlua_proxy_stop);
577 hlua_class_function(L, "shut_bcksess", hlua_proxy_shut_bcksess);
578 hlua_class_function(L, "get_cap", hlua_proxy_get_cap);
579 hlua_class_function(L, "get_mode", hlua_proxy_get_mode);
580 hlua_class_function(L, "get_stats", hlua_proxy_get_stats);
581 lua_settable(L, -3); /* -> META["__index"] = TABLE */
582 class_proxy_ref = hlua_register_metatable(L, CLASS_PROXY);
583
Thierry Fournier1550d5d2016-01-21 09:35:41 +0100584 return 5;
Thierry Fournierfb0b5462016-01-21 09:28:58 +0100585}