aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThijs Schreijer <thijs@thijsschreijer.nl>2023-11-09 23:03:21 +0100
committerThijs Schreijer <thijs@thijsschreijer.nl>2023-11-15 19:17:57 +0100
commitd45768de3e6f7b28bfecf4d19b192ccac9ce5dc2 (patch)
tree2d4f86ec87eb87a77f6663924aaaa9286756ce3e /src
parentd4222ce6da2a2d7179fc79f9d0cc65fd6c09a686 (diff)
downloadluasystem-d45768de3e6f7b28bfecf4d19b192ccac9ce5dc2.tar.gz
luasystem-d45768de3e6f7b28bfecf4d19b192ccac9ce5dc2.tar.bz2
luasystem-d45768de3e6f7b28bfecf4d19b192ccac9ce5dc2.zip
feat(*): add environment variable and random functions
Diffstat (limited to 'src')
-rw-r--r--src/Makefile4
-rw-r--r--src/compat.h27
-rw-r--r--src/core.c14
-rw-r--r--src/environment.c173
-rw-r--r--src/random.c117
-rw-r--r--src/time.c87
6 files changed, 391 insertions, 31 deletions
diff --git a/src/Makefile b/src/Makefile
index 10fc31a..119f95e 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -85,7 +85,7 @@ PLATFORM_win32?=Release
85CDIR_win32?=bin/lua/$(LUA_VERSION)/$(PLATFORM_win32) 85CDIR_win32?=bin/lua/$(LUA_VERSION)/$(PLATFORM_win32)
86LDIR_win32?=bin/lua/$(LUA_VERSION)/$(PLATFORM_win32)/lua 86LDIR_win32?=bin/lua/$(LUA_VERSION)/$(PLATFORM_win32)/lua
87LUALIB_win32?=$(LUAPREFIX_win32)/lib/lua/$(LUA_VERSION)/$(PLATFORM_win32) 87LUALIB_win32?=$(LUAPREFIX_win32)/lib/lua/$(LUA_VERSION)/$(PLATFORM_win32)
88LUALIBNAME_win32?=lua$(subst .,,$(LUA_VERSION)).lib 88LUALIBNAME_win32?=lua$(subst .,,$(LUA_VERSION)).lib
89 89
90 90
91# prefix: /usr/local /usr /opt/local /sw 91# prefix: /usr/local /usr /opt/local /sw
@@ -217,7 +217,7 @@ LUALIB= $(LUALIB_$(PLAT))
217#------ 217#------
218# Objects 218# Objects
219# 219#
220OBJS=core.$(O) compat.$(O) time.$(O) 220OBJS=core.$(O) compat.$(O) time.$(O) environment.$(O) random.$(O)
221 221
222#------ 222#------
223# Targets 223# Targets
diff --git a/src/compat.h b/src/compat.h
index f523fd9..5aca6df 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -8,4 +8,31 @@
8void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup); 8void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup);
9#endif 9#endif
10 10
11// Windows doesn't have ssize_t, so we define it here
12#ifdef _WIN32
13#if SIZE_MAX == UINT_MAX
14typedef int ssize_t; /* common 32 bit case */
15#define SSIZE_MIN INT_MIN
16#define SSIZE_MAX INT_MAX
17#elif SIZE_MAX == ULONG_MAX
18typedef long ssize_t; /* linux 64 bits */
19#define SSIZE_MIN LONG_MIN
20#define SSIZE_MAX LONG_MAX
21#elif SIZE_MAX == ULLONG_MAX
22typedef long long ssize_t; /* windows 64 bits */
23#define SSIZE_MIN LLONG_MIN
24#define SSIZE_MAX LLONG_MAX
25#elif SIZE_MAX == USHRT_MAX
26typedef short ssize_t; /* is this even possible? */
27#define SSIZE_MIN SHRT_MIN
28#define SSIZE_MAX SHRT_MAX
29#elif SIZE_MAX == UINTMAX_MAX
30typedef intmax_t ssize_t; /* last resort, chux suggestion */
31#define SSIZE_MIN INTMAX_MIN
32#define SSIZE_MAX INTMAX_MAX
33#else
34#error platform has exotic SIZE_MAX
35#endif
36#endif
37
11#endif 38#endif
diff --git a/src/core.c b/src/core.c
index 6c46981..fffedd1 100644
--- a/src/core.c
+++ b/src/core.c
@@ -1,3 +1,6 @@
1/// Platform independent system calls for Lua.
2// @module system
3
1#include <lua.h> 4#include <lua.h>
2#include <lauxlib.h> 5#include <lauxlib.h>
3 6
@@ -10,6 +13,8 @@
10#endif 13#endif
11 14
12void time_open(lua_State *L); 15void time_open(lua_State *L);
16void environment_open(lua_State *L);
17void random_open(lua_State *L);
13 18
14/*------------------------------------------------------------------------- 19/*-------------------------------------------------------------------------
15 * Initializes all library modules. 20 * Initializes all library modules.
@@ -19,6 +24,15 @@ LUAEXPORT int luaopen_system_core(lua_State *L) {
19 lua_pushstring(L, "_VERSION"); 24 lua_pushstring(L, "_VERSION");
20 lua_pushstring(L, LUASYSTEM_VERSION); 25 lua_pushstring(L, LUASYSTEM_VERSION);
21 lua_rawset(L, -3); 26 lua_rawset(L, -3);
27 lua_pushstring(L, "windows");
28#ifdef _WIN32
29 lua_pushboolean(L, 1);
30#else
31 lua_pushboolean(L, 0);
32#endif
33 lua_rawset(L, -3);
22 time_open(L); 34 time_open(L);
35 random_open(L);
36 environment_open(L);
23 return 1; 37 return 1;
24} 38}
diff --git a/src/environment.c b/src/environment.c
new file mode 100644
index 0000000..5f1c3da
--- /dev/null
+++ b/src/environment.c
@@ -0,0 +1,173 @@
1/// @submodule system
2#include <lua.h>
3#include <lauxlib.h>
4#include "compat.h"
5#include <stdlib.h>
6#include <string.h>
7
8#ifdef _WIN32
9#include "windows.h"
10#endif
11
12/***
13Gets the value of an environment variable.
14
15__NOTE__: Windows has multiple copies of environment variables. For this reason,
16the `setenv` function will not work with Lua's `os.getenv` on Windows. If you want
17to use `setenv` then consider patching `os.getenv` with this implementation of `getenv`.
18@function getenv
19@tparam string name name of the environment variable
20@treturn string|nil value of the environment variable, or nil if the variable is not set
21*/
22static int lua_get_environment_variable(lua_State* L) {
23 const char* variableName = luaL_checkstring(L, 1);
24
25#ifdef _WIN32
26 // On Windows, use GetEnvironmentVariable to retrieve the value
27 DWORD bufferSize = GetEnvironmentVariable(variableName, NULL, 0);
28 if (bufferSize > 0) {
29 char* buffer = (char*)malloc(bufferSize);
30 if (GetEnvironmentVariable(variableName, buffer, bufferSize) > 0) {
31 lua_pushstring(L, buffer);
32 free(buffer);
33 return 1;
34 }
35 free(buffer);
36 }
37#else
38 // On non-Windows platforms, use getenv to retrieve the value
39 const char* variableValue = getenv(variableName);
40 if (variableValue != NULL) {
41 lua_pushstring(L, variableValue);
42 return 1;
43 }
44#endif
45
46 // If the variable is not set or an error occurs, push nil
47 lua_pushnil(L);
48 return 1;
49}
50
51
52/***
53Returns a table with all environment variables.
54@function getenvs
55@treturn table table with all environment variables and their values
56*/
57static int lua_list_environment_variables(lua_State* L) {
58 lua_newtable(L);
59
60#ifdef _WIN32
61 char* envStrings = GetEnvironmentStrings();
62 char* envString = envStrings;
63
64 if (envStrings == NULL) {
65 lua_pushnil(L);
66 return 1;
67 }
68
69 while (*envString != '\0') {
70 const char* envVar = envString;
71
72 // Split the environment variable into key and value
73 char* equals = strchr(envVar, '=');
74 if (equals != NULL) {
75 lua_pushlstring(L, envVar, equals - envVar); // Push the key
76 lua_pushstring(L, equals + 1); // Push the value
77 lua_settable(L, -3); // Set the key-value pair in the table
78 }
79
80 envString += strlen(envString) + 1;
81 }
82
83 FreeEnvironmentStrings(envStrings);
84#else
85 extern char** environ;
86
87 if (environ != NULL) {
88 for (char** envVar = environ; *envVar != NULL; envVar++) {
89 const char* envVarStr = *envVar;
90
91 // Split the environment variable into key and value
92 char* equals = strchr(envVarStr, '=');
93 if (equals != NULL) {
94 lua_pushlstring(L, envVarStr, equals - envVarStr); // Push the key
95 lua_pushstring(L, equals + 1); // Push the value
96 lua_settable(L, -3); // Set the key-value pair in the table
97 }
98 }
99 }
100#endif
101
102 return 1;
103}
104
105
106/***
107Sets an environment variable.
108
109__NOTE__: Windows has multiple copies of environment variables. For this reason, the
110`setenv` function will not work with Lua's `os.getenv` on Windows. If you want to use
111it then consider patching `os.getenv` with the implementation of `system.getenv`.
112@function setenv
113@tparam string name name of the environment variable
114@tparam[opt] string value value of the environment variable, if `nil` the variable will be deleted (on
115Windows, setting an empty string, will also delete the variable)
116@treturn boolean success
117*/
118static int lua_set_environment_variable(lua_State* L) {
119 const char* variableName = luaL_checkstring(L, 1);
120 const char* variableValue = luaL_optstring(L, 2, NULL);
121
122#ifdef _WIN32
123 // if (variableValue == NULL) {
124 // // If the value is nil, delete the environment variable
125 // if (SetEnvironmentVariable(variableName, NULL)) {
126 // lua_pushboolean(L, 1);
127 // } else {
128 // lua_pushboolean(L, 0);
129 // }
130 // } else {
131 // Set the environment variable with the provided value
132 if (SetEnvironmentVariable(variableName, variableValue)) {
133 lua_pushboolean(L, 1);
134 } else {
135 lua_pushboolean(L, 0);
136 }
137 // }
138#else
139 if (variableValue == NULL) {
140 // If the value is nil, delete the environment variable
141 if (unsetenv(variableName) == 0) {
142 lua_pushboolean(L, 1);
143 } else {
144 lua_pushboolean(L, 0);
145 }
146 } else {
147 // Set the environment variable with the provided value
148 if (setenv(variableName, variableValue, 1) == 0) {
149 lua_pushboolean(L, 1);
150 } else {
151 lua_pushboolean(L, 0);
152 }
153 }
154#endif
155
156 return 1;
157}
158
159
160
161static luaL_Reg func[] = {
162 { "getenv", lua_get_environment_variable },
163 { "setenv", lua_set_environment_variable },
164 { "getenvs", lua_list_environment_variables },
165 { NULL, NULL }
166};
167
168/*-------------------------------------------------------------------------
169 * Initializes module
170 *-------------------------------------------------------------------------*/
171void environment_open(lua_State *L) {
172 luaL_setfuncs(L, func, 0);
173}
diff --git a/src/random.c b/src/random.c
new file mode 100644
index 0000000..90fb3f2
--- /dev/null
+++ b/src/random.c
@@ -0,0 +1,117 @@
1/// @submodule system
2#include <lua.h>
3#include <lauxlib.h>
4#include "compat.h"
5#include <fcntl.h>
6
7#ifdef _WIN32
8#include "windows.h"
9#include "wincrypt.h"
10#else
11#include <errno.h>
12#include <unistd.h>
13#include <string.h>
14#endif
15
16
17/***
18Generate random bytes.
19This uses `CryptGenRandom()` on Windows, and `/dev/urandom` on other platforms. It will return the
20requested number of bytes, or an error, never a partial result.
21@function random
22@tparam[opt=1] int length number of bytes to get
23@treturn[1] string string of random bytes
24@treturn[2] nil
25@treturn[2] string error message
26*/
27static int lua_get_random_bytes(lua_State* L) {
28 int num_bytes = luaL_optinteger(L, 1, 1); // Number of bytes, default to 1 if not provided
29
30 if (num_bytes <= 0) {
31 if (num_bytes == 0) {
32 lua_pushliteral(L, "");
33 return 1;
34 }
35 lua_pushnil(L);
36 lua_pushstring(L, "invalid number of bytes, must not be less than 0");
37 return 2;
38 }
39
40 unsigned char* buffer = (unsigned char*)lua_newuserdata(L, num_bytes);
41 if (buffer == NULL) {
42 lua_pushnil(L);
43 lua_pushstring(L, "failed to allocate memory for random buffer");
44 return 2;
45 }
46
47 ssize_t n;
48 ssize_t total_read = 0;
49
50#ifdef _WIN32
51 HCRYPTPROV hCryptProv;
52 if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
53 DWORD error = GetLastError();
54 lua_pushnil(L);
55 lua_pushfstring(L, "failed to acquire cryptographic context: %lu", error);
56 return 2;
57 }
58
59 if (!CryptGenRandom(hCryptProv, num_bytes, buffer)) {
60 DWORD error = GetLastError();
61 lua_pushnil(L);
62 lua_pushfstring(L, "failed to get random data: %lu", error);
63 CryptReleaseContext(hCryptProv, 0);
64 return 2;
65 }
66
67 CryptReleaseContext(hCryptProv, 0);
68#else
69
70 // for macOS/unixes use /dev/urandom for non-blocking
71 int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
72 if (fd < 0) {
73 lua_pushnil(L);
74 lua_pushstring(L, "failed opening /dev/urandom");
75 return 2;
76 }
77
78 while (total_read < num_bytes) {
79 n = read(fd, buffer + total_read, num_bytes - total_read);
80
81 if (n < 0) {
82 if (errno == EINTR) {
83 continue; // Interrupted, retry
84
85 } else {
86 lua_pushnil(L);
87 lua_pushfstring(L, "failed reading /dev/urandom: %s", strerror(errno));
88 close(fd);
89 return 2;
90 }
91 }
92
93 total_read += n;
94 }
95
96 close(fd);
97#endif
98
99 lua_pushlstring(L, (const char*)buffer, num_bytes);
100 return 1;
101}
102
103
104
105static luaL_Reg func[] = {
106 { "random", lua_get_random_bytes },
107 { NULL, NULL }
108};
109
110
111
112/*-------------------------------------------------------------------------
113 * Initializes module
114 *-------------------------------------------------------------------------*/
115void random_open(lua_State *L) {
116 luaL_setfuncs(L, func, 0);
117}
diff --git a/src/time.c b/src/time.c
index 8c6a4f2..5f0ead0 100644
--- a/src/time.c
+++ b/src/time.c
@@ -1,3 +1,4 @@
1/// @submodule system
1#include <lua.h> 2#include <lua.h>
2#include <lauxlib.h> 3#include <lauxlib.h>
3 4
@@ -50,11 +51,8 @@ static double time_gettime(void) {
50} 51}
51#endif 52#endif
52 53
53/*------------------------------------------------------------------------- 54
54 * Gets monotonic time in s 55
55 * Returns
56 * time in s.
57 *-------------------------------------------------------------------------*/
58#ifdef _WIN32 56#ifdef _WIN32
59WINBASEAPI ULONGLONG WINAPI GetTickCount64(VOID); 57WINBASEAPI ULONGLONG WINAPI GetTickCount64(VOID);
60 58
@@ -70,53 +68,84 @@ static double time_monotime(void) {
70} 68}
71#endif 69#endif
72 70
73/*------------------------------------------------------------------------- 71
74 * Returns the current system time, 1970 (UTC), in secconds. 72
75 *-------------------------------------------------------------------------*/ 73/***
74Get system time.
75The time is returned as the seconds since the epoch (1 January 1970 00:00:00).
76@function gettime
77@treturn number seconds (fractional)
78*/
76static int time_lua_gettime(lua_State *L) 79static int time_lua_gettime(lua_State *L)
77{ 80{
78 lua_pushnumber(L, time_gettime()); 81 lua_pushnumber(L, time_gettime());
79 return 1; 82 return 1;
80} 83}
81 84
82/*------------------------------------------------------------------------- 85
83 * Returns the monotonic time the system has been up, in secconds. 86
84 *-------------------------------------------------------------------------*/ 87/***
88Get monotonic time.
89The time is returned as the seconds since system start.
90@function monotime
91@treturn number seconds (fractional)
92*/
85static int time_lua_monotime(lua_State *L) 93static int time_lua_monotime(lua_State *L)
86{ 94{
87 lua_pushnumber(L, time_monotime()); 95 lua_pushnumber(L, time_monotime());
88 return 1; 96 return 1;
89} 97}
90 98
91/*------------------------------------------------------------------------- 99
92 * Sleep for n seconds. 100
93 *-------------------------------------------------------------------------*/ 101/***
102Sleep without a busy loop.
103This function will sleep, without doing a busy-loop and wasting CPU cycles.
104@function sleep
105@tparam number seconds seconds to sleep (fractional).
106@tparam[opt=16] integer precision minimum stepsize in milliseconds (Windows only, ignored elsewhere)
107@return `true` on success, or `nil+err` on failure
108*/
94#ifdef _WIN32 109#ifdef _WIN32
95static int time_lua_sleep(lua_State *L) 110static int time_lua_sleep(lua_State *L)
96{ 111{
97 double n = luaL_checknumber(L, 1); 112 double n = luaL_checknumber(L, 1);
98 if (n < 0.0) n = 0.0; 113
99 if (n < DBL_MAX/1000.0) n *= 1000.0; 114 int precision = luaL_optinteger(L, 2, 16);
100 if (n > INT_MAX) n = INT_MAX; 115 if (precision < 0 || precision > 16) precision = 16;
101 Sleep((int)n); 116
102 return 0; 117 if (n > 0.0) {
118 if (n < DBL_MAX/1000.0) n *= 1000.0;
119 if (n > INT_MAX) n = INT_MAX;
120 if (timeBeginPeriod(precision) != TIMERR_NOERROR) {
121 lua_pushnil(L);
122 lua_pushstring(L, "failed to set timer precision");
123 return 2;
124 };
125 Sleep((int)n);
126 timeEndPeriod(precision);
127 }
128 lua_pushboolean(L, 1);
129 return 1;
103} 130}
104#else 131#else
105static int time_lua_sleep(lua_State *L) 132static int time_lua_sleep(lua_State *L)
106{ 133{
107 double n = luaL_checknumber(L, 1); 134 double n = luaL_checknumber(L, 1);
108 struct timespec t, r; 135 struct timespec t, r;
109 if (n < 0.0) n = 0.0; 136 if (n > 0.0) {
110 if (n > INT_MAX) n = INT_MAX; 137 if (n > INT_MAX) n = INT_MAX;
111 t.tv_sec = (int) n; 138 t.tv_sec = (int) n;
112 n -= t.tv_sec; 139 n -= t.tv_sec;
113 t.tv_nsec = (int) (n * 1000000000); 140 t.tv_nsec = (int) (n * 1000000000);
114 if (t.tv_nsec >= 1000000000) t.tv_nsec = 999999999; 141 if (t.tv_nsec >= 1000000000) t.tv_nsec = 999999999;
115 while (nanosleep(&t, &r) != 0) { 142 while (nanosleep(&t, &r) != 0) {
116 t.tv_sec = r.tv_sec; 143 t.tv_sec = r.tv_sec;
117 t.tv_nsec = r.tv_nsec; 144 t.tv_nsec = r.tv_nsec;
145 }
118 } 146 }
119 return 0; 147 lua_pushboolean(L, 1);
148 return 1;
120} 149}
121#endif 150#endif
122 151