Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/libexec/flua/lposix/lposix.c
39483 views
1
/*-
2
* Copyright (c) 2019, 2023 Kyle Evans <[email protected]>
3
*
4
* SPDX-License-Identifier: BSD-2-Clause
5
*/
6
7
#include <sys/stat.h>
8
#include <sys/utsname.h>
9
#include <sys/wait.h>
10
11
#include <errno.h>
12
#include <fnmatch.h>
13
#include <grp.h>
14
#include <libgen.h>
15
#include <pwd.h>
16
#include <stdlib.h>
17
#include <string.h>
18
#include <unistd.h>
19
20
#include <lua.h>
21
#include "lauxlib.h"
22
#include "lposix.h"
23
24
#include "bootstrap.h"
25
26
static void
27
enforce_max_args(lua_State *L, int max)
28
{
29
int narg;
30
31
narg = lua_gettop(L);
32
luaL_argcheck(L, narg <= max, max + 1, "too many arguments");
33
}
34
35
/*
36
* Minimal implementation of luaposix needed for internal FreeBSD bits.
37
*/
38
static int
39
lua__exit(lua_State *L)
40
{
41
int code;
42
43
enforce_max_args(L, 1);
44
code = luaL_checkinteger(L, 1);
45
46
_exit(code);
47
}
48
49
static int
50
lua_basename(lua_State *L)
51
{
52
char *inpath, *outpath;
53
54
enforce_max_args(L, 1);
55
inpath = strdup(luaL_checkstring(L, 1));
56
if (inpath == NULL) {
57
lua_pushnil(L);
58
lua_pushstring(L, strerror(ENOMEM));
59
lua_pushinteger(L, ENOMEM);
60
return (3);
61
}
62
63
outpath = basename(inpath);
64
lua_pushstring(L, outpath);
65
free(inpath);
66
return (1);
67
}
68
69
static int
70
lua_chmod(lua_State *L)
71
{
72
const char *path;
73
mode_t mode;
74
75
enforce_max_args(L, 2);
76
path = luaL_checkstring(L, 1);
77
mode = (mode_t)luaL_checkinteger(L, 2);
78
79
if (chmod(path, mode) == -1) {
80
lua_pushnil(L);
81
lua_pushstring(L, strerror(errno));
82
lua_pushinteger(L, errno);
83
return (3);
84
}
85
lua_pushinteger(L, 0);
86
return (1);
87
}
88
89
static int
90
lua_chown(lua_State *L)
91
{
92
const char *path;
93
uid_t owner = (uid_t)-1;
94
gid_t group = (gid_t)-1;
95
int error;
96
97
enforce_max_args(L, 3);
98
99
path = luaL_checkstring(L, 1);
100
if (lua_isinteger(L, 2))
101
owner = (uid_t)lua_tointeger(L, 2);
102
else if (lua_isstring(L, 2)) {
103
char buf[4096];
104
struct passwd passwd, *pwd;
105
106
error = getpwnam_r(lua_tostring(L, 2), &passwd,
107
buf, sizeof(buf), &pwd);
108
if (error == 0)
109
owner = pwd->pw_uid;
110
else
111
return (luaL_argerror(L, 2,
112
lua_pushfstring(L, "unknown user %s",
113
lua_tostring(L, 2))));
114
} else if (!lua_isnoneornil(L, 2)) {
115
const char *type = luaL_typename(L, 2);
116
return (luaL_argerror(L, 2,
117
lua_pushfstring(L, "integer or string expected, got %s",
118
type)));
119
}
120
121
if (lua_isinteger(L, 3))
122
group = (gid_t)lua_tointeger(L, 3);
123
else if (lua_isstring(L, 3)) {
124
char buf[4096];
125
struct group gr, *grp;
126
127
error = getgrnam_r(lua_tostring(L, 3), &gr, buf, sizeof(buf),
128
&grp);
129
if (error == 0)
130
group = grp->gr_gid;
131
else
132
return (luaL_argerror(L, 3,
133
lua_pushfstring(L, "unknown group %s",
134
lua_tostring(L, 3))));
135
} else if (!lua_isnoneornil(L, 3)) {
136
const char *type = luaL_typename(L, 3);
137
return (luaL_argerror(L, 3,
138
lua_pushfstring(L, "integer or string expected, got %s",
139
type)));
140
}
141
142
if (chown(path, owner, group) == -1) {
143
lua_pushnil(L);
144
lua_pushstring(L, strerror(errno));
145
lua_pushinteger(L, errno);
146
return (3);
147
}
148
lua_pushinteger(L, 0);
149
return (1);
150
}
151
152
static int
153
lua_pclose(lua_State *L)
154
{
155
int error, fd;
156
157
enforce_max_args(L, 1);
158
159
fd = luaL_checkinteger(L, 1);
160
if (fd < 0) {
161
error = EBADF;
162
goto err;
163
}
164
165
if (close(fd) == 0) {
166
lua_pushinteger(L, 0);
167
return (1);
168
}
169
170
error = errno;
171
err:
172
lua_pushnil(L);
173
lua_pushstring(L, strerror(error));
174
lua_pushinteger(L, error);
175
return (3);
176
177
}
178
179
static int
180
lua_dup2(lua_State *L)
181
{
182
int error, oldd, newd;
183
184
enforce_max_args(L, 2);
185
186
oldd = luaL_checkinteger(L, 1);
187
if (oldd < 0) {
188
error = EBADF;
189
goto err;
190
}
191
192
newd = luaL_checkinteger(L, 2);
193
if (newd < 0) {
194
error = EBADF;
195
goto err;
196
}
197
198
error = dup2(oldd, newd);
199
if (error >= 0) {
200
lua_pushinteger(L, error);
201
return (1);
202
}
203
204
error = errno;
205
err:
206
lua_pushnil(L);
207
lua_pushstring(L, strerror(error));
208
lua_pushinteger(L, error);
209
return (3);
210
}
211
212
static int
213
lua_execp(lua_State *L)
214
{
215
int argc, error;
216
const char *file;
217
const char **argv;
218
219
enforce_max_args(L, 2);
220
221
file = luaL_checkstring(L, 1);
222
luaL_checktype(L, 2, LUA_TTABLE);
223
224
lua_len(L, 2);
225
argc = lua_tointeger(L, -1);
226
227
/*
228
* Use lua_newuserdatauv() to allocate a scratch buffer that is tracked
229
* and freed by lua's GC. This avoid any chance of a leak if a lua error
230
* is raised later in this function (e.g. by luaL_argerror()).
231
* The (argc + 2) size gives enough space in the buffer for argv[0] and
232
* the terminating NULL.
233
*/
234
argv = lua_newuserdatauv(L, (argc + 2) * sizeof(char *), 0);
235
236
/*
237
* Sequential tables in lua start at index 1 by convention.
238
* If there happens to be a string at index 0, use that to
239
* override the default argv[0]. This matches the lposix API.
240
*/
241
lua_pushinteger(L, 0);
242
lua_gettable(L, 2);
243
argv[0] = lua_tostring(L, -1);
244
if (argv[0] == NULL) {
245
argv[0] = file;
246
}
247
248
for (int i = 1; i <= argc; i++) {
249
lua_pushinteger(L, i);
250
lua_gettable(L, 2);
251
argv[i] = lua_tostring(L, -1);
252
if (argv[i] == NULL) {
253
luaL_argerror(L, 2,
254
"argv table must contain only strings");
255
}
256
}
257
argv[argc + 1] = NULL;
258
259
execvp(file, (char **)argv);
260
error = errno;
261
262
lua_pushnil(L);
263
lua_pushstring(L, strerror(error));
264
lua_pushinteger(L, error);
265
return (3);
266
}
267
268
static int
269
lua_fnmatch(lua_State *L)
270
{
271
const char *pattern, *string;
272
int flags;
273
274
enforce_max_args(L, 3);
275
pattern = luaL_checkstring(L, 1);
276
string = luaL_checkstring(L, 2);
277
flags = luaL_optinteger(L, 3, 0);
278
279
lua_pushinteger(L, fnmatch(pattern, string, flags));
280
281
return (1);
282
}
283
284
static int
285
lua_uname(lua_State *L)
286
{
287
struct utsname name;
288
int error;
289
290
enforce_max_args(L, 0);
291
292
error = uname(&name);
293
if (error != 0) {
294
error = errno;
295
lua_pushnil(L);
296
lua_pushstring(L, strerror(error));
297
lua_pushinteger(L, error);
298
return (3);
299
}
300
301
lua_newtable(L);
302
#define setkv(f) do { \
303
lua_pushstring(L, name.f); \
304
lua_setfield(L, -2, #f); \
305
} while (0)
306
setkv(sysname);
307
setkv(nodename);
308
setkv(release);
309
setkv(version);
310
setkv(machine);
311
#undef setkv
312
313
return (1);
314
}
315
316
static int
317
lua_dirname(lua_State *L)
318
{
319
char *inpath, *outpath;
320
321
enforce_max_args(L, 1);
322
323
inpath = strdup(luaL_checkstring(L, 1));
324
if (inpath == NULL) {
325
lua_pushnil(L);
326
lua_pushstring(L, strerror(ENOMEM));
327
lua_pushinteger(L, ENOMEM);
328
return (3);
329
}
330
331
outpath = dirname(inpath);
332
lua_pushstring(L, outpath);
333
free(inpath);
334
return (1);
335
}
336
337
static int
338
lua_fork(lua_State *L)
339
{
340
pid_t pid;
341
342
enforce_max_args(L, 0);
343
344
pid = fork();
345
if (pid < 0) {
346
lua_pushnil(L);
347
lua_pushstring(L, strerror(errno));
348
lua_pushinteger(L, errno);
349
return (3);
350
}
351
352
lua_pushinteger(L, pid);
353
return (1);
354
}
355
356
static int
357
lua_getpid(lua_State *L)
358
{
359
enforce_max_args(L, 0);
360
361
lua_pushinteger(L, getpid());
362
return (1);
363
}
364
365
static int
366
lua_pipe(lua_State *L)
367
{
368
int error, fd[2];
369
370
enforce_max_args(L, 0);
371
372
error = pipe(fd);
373
if (error != 0) {
374
lua_pushnil(L);
375
lua_pushstring(L, strerror(errno));
376
lua_pushinteger(L, errno);
377
return (1);
378
}
379
380
lua_pushinteger(L, fd[0]);
381
lua_pushinteger(L, fd[1]);
382
return (2);
383
}
384
385
static int
386
lua_read(lua_State *L)
387
{
388
char *buf;
389
ssize_t ret;
390
size_t sz;
391
int error, fd;
392
393
enforce_max_args(L, 2);
394
fd = luaL_checkinteger(L, 1);
395
sz = luaL_checkinteger(L, 2);
396
397
if (fd < 0) {
398
error = EBADF;
399
goto err;
400
}
401
402
buf = malloc(sz);
403
if (buf == NULL)
404
goto err;
405
406
/*
407
* For 0-byte reads, we'll still push the empty string and let the
408
* caller deal with EOF to match lposix semantics.
409
*/
410
ret = read(fd, buf, sz);
411
if (ret >= 0)
412
lua_pushlstring(L, buf, ret);
413
else if (ret < 0)
414
error = errno; /* Save to avoid clobber by free() */
415
416
free(buf);
417
if (error != 0)
418
goto err;
419
420
/* Just the string pushed. */
421
return (1);
422
err:
423
lua_pushnil(L);
424
lua_pushstring(L, strerror(error));
425
lua_pushinteger(L, error);
426
return (3);
427
}
428
429
static int
430
lua_realpath(lua_State *L)
431
{
432
const char *inpath;
433
char *outpath;
434
435
enforce_max_args(L, 1);
436
inpath = luaL_checkstring(L, 1);
437
438
outpath = realpath(inpath, NULL);
439
if (outpath == NULL) {
440
lua_pushnil(L);
441
lua_pushstring(L, strerror(errno));
442
lua_pushinteger(L, errno);
443
return (3);
444
}
445
446
lua_pushstring(L, outpath);
447
free(outpath);
448
return (1);
449
}
450
451
static int
452
lua_wait(lua_State *L)
453
{
454
pid_t pid;
455
int options, status;
456
457
enforce_max_args(L, 2);
458
pid = luaL_optinteger(L, 1, -1);
459
options = luaL_optinteger(L, 2, 0);
460
461
status = 0;
462
pid = waitpid(pid, &status, options);
463
if (pid < 0) {
464
lua_pushnil(L);
465
lua_pushstring(L, strerror(errno));
466
lua_pushinteger(L, errno);
467
return (3);
468
}
469
470
lua_pushinteger(L, pid);
471
if (pid == 0) {
472
lua_pushliteral(L, "running");
473
return (2);
474
}
475
476
if (WIFCONTINUED(status)) {
477
lua_pushliteral(L, "continued");
478
return (2);
479
} else if(WIFSTOPPED(status)) {
480
lua_pushliteral(L, "stopped");
481
lua_pushinteger(L, WSTOPSIG(status));
482
return (3);
483
} else if (WIFEXITED(status)) {
484
lua_pushliteral(L, "exited");
485
lua_pushinteger(L, WEXITSTATUS(status));
486
return (3);
487
} else if (WIFSIGNALED(status)) {
488
lua_pushliteral(L, "killed");
489
lua_pushinteger(L, WTERMSIG(status));
490
return (3);
491
}
492
493
return (1);
494
}
495
496
static int
497
lua_write(lua_State *L)
498
{
499
const char *buf;
500
size_t bufsz, sz;
501
ssize_t ret;
502
off_t offset;
503
int error, fd;
504
505
enforce_max_args(L, 4);
506
507
fd = luaL_checkinteger(L, 1);
508
if (fd < 0) {
509
error = EBADF;
510
goto err;
511
}
512
513
buf = luaL_checkstring(L, 2);
514
515
bufsz = lua_rawlen(L, 2);
516
sz = luaL_optinteger(L, 3, bufsz);
517
518
offset = luaL_optinteger(L, 4, 0);
519
520
521
if ((size_t)offset > bufsz || offset + sz > bufsz) {
522
lua_pushnil(L);
523
lua_pushfstring(L,
524
"write: invalid access offset %zu, size %zu in a buffer size %zu",
525
offset, sz, bufsz);
526
lua_pushinteger(L, EINVAL);
527
return (3);
528
}
529
530
ret = write(fd, buf + offset, sz);
531
if (ret < 0) {
532
error = errno;
533
goto err;
534
}
535
536
lua_pushinteger(L, ret);
537
return (1);
538
err:
539
lua_pushnil(L);
540
lua_pushstring(L, strerror(error));
541
lua_pushinteger(L, error);
542
return (3);
543
}
544
545
#define REG_DEF(n, func) { #n, func }
546
#define REG_SIMPLE(n) REG_DEF(n, lua_ ## n)
547
static const struct luaL_Reg libgenlib[] = {
548
REG_SIMPLE(basename),
549
REG_SIMPLE(dirname),
550
{ NULL, NULL },
551
};
552
553
static const struct luaL_Reg stdliblib[] = {
554
REG_SIMPLE(realpath),
555
{ NULL, NULL },
556
};
557
558
static const struct luaL_Reg fnmatchlib[] = {
559
REG_SIMPLE(fnmatch),
560
{ NULL, NULL },
561
};
562
563
static const struct luaL_Reg sys_statlib[] = {
564
REG_SIMPLE(chmod),
565
{ NULL, NULL },
566
};
567
568
static const struct luaL_Reg sys_utsnamelib[] = {
569
REG_SIMPLE(uname),
570
{ NULL, NULL },
571
};
572
573
static const struct luaL_Reg sys_waitlib[] = {
574
REG_SIMPLE(wait),
575
{NULL, NULL},
576
};
577
578
static const struct luaL_Reg unistdlib[] = {
579
REG_SIMPLE(_exit),
580
REG_SIMPLE(chown),
581
REG_DEF(close, lua_pclose),
582
REG_SIMPLE(dup2),
583
REG_SIMPLE(execp),
584
REG_SIMPLE(fork),
585
REG_SIMPLE(getpid),
586
REG_SIMPLE(pipe),
587
REG_SIMPLE(read),
588
REG_SIMPLE(write),
589
{ NULL, NULL },
590
};
591
592
#undef REG_SIMPLE
593
#undef REG_DEF
594
595
static int
596
luaopen_posix_libgen(lua_State *L)
597
{
598
luaL_newlib(L, libgenlib);
599
return (1);
600
}
601
602
static int
603
luaopen_posix_stdlib(lua_State *L)
604
{
605
luaL_newlib(L, stdliblib);
606
return (1);
607
}
608
609
static int
610
luaopen_posix_fnmatch(lua_State *L)
611
{
612
luaL_newlib(L, fnmatchlib);
613
614
#define setkv(f) do { \
615
lua_pushinteger(L, f); \
616
lua_setfield(L, -2, #f); \
617
} while (0)
618
setkv(FNM_PATHNAME);
619
setkv(FNM_NOESCAPE);
620
setkv(FNM_NOMATCH);
621
setkv(FNM_PERIOD);
622
#undef setkv
623
624
return 1;
625
}
626
627
static int
628
luaopen_posix_sys_stat(lua_State *L)
629
{
630
luaL_newlib(L, sys_statlib);
631
return (1);
632
}
633
634
static int
635
luaopen_posix_sys_utsname(lua_State *L)
636
{
637
luaL_newlib(L, sys_utsnamelib);
638
return 1;
639
}
640
641
static int
642
luaopen_posix_sys_wait(lua_State *L)
643
{
644
luaL_newlib(L, sys_waitlib);
645
646
#define lua_pushflag(L, flag) do { \
647
lua_pushinteger(L, flag); \
648
lua_setfield(L, -2, #flag); \
649
} while(0)
650
651
/* Only these two exported by lposix */
652
lua_pushflag(L, WNOHANG);
653
lua_pushflag(L, WUNTRACED);
654
655
lua_pushflag(L, WCONTINUED);
656
lua_pushflag(L, WSTOPPED);
657
#ifdef WTRAPPED
658
lua_pushflag(L, WTRAPPED);
659
#endif
660
lua_pushflag(L, WEXITED);
661
lua_pushflag(L, WNOWAIT);
662
#undef lua_pushflag
663
664
return (1);
665
}
666
667
static int
668
luaopen_posix_unistd(lua_State *L)
669
{
670
luaL_newlib(L, unistdlib);
671
return (1);
672
}
673
674
int
675
luaopen_posix(lua_State *L)
676
{
677
lua_newtable(L); /* posix */
678
679
luaL_requiref(L, "posix.fnmatch", luaopen_posix_fnmatch, 0);
680
lua_setfield(L, -2, "fnmatch");
681
682
luaL_requiref(L, "posix.libgen", luaopen_posix_libgen, 0);
683
lua_setfield(L, -2, "libgen");
684
685
luaL_requiref(L, "posix.stdlib", luaopen_posix_stdlib, 0);
686
lua_setfield(L, -2, "stdlib");
687
688
lua_newtable(L); /* posix.sys */
689
luaL_requiref(L, "posix.sys.stat", luaopen_posix_sys_stat, 0);
690
lua_setfield(L, -2, "stat");
691
luaL_requiref(L, "posix.sys.utsname", luaopen_posix_sys_utsname, 0);
692
lua_setfield(L, -2, "utsname");
693
luaL_requiref(L, "posix.sys.wait", luaopen_posix_sys_wait, 0);
694
lua_setfield(L, -2, "wait");
695
lua_setfield(L, -2, "sys");
696
697
luaL_requiref(L, "posix.unistd", luaopen_posix_unistd, 0);
698
lua_setfield(L, -2, "unistd");
699
700
return (1);
701
}
702
703
/* Only this one needed in our bootstrap set, it will load the others. */
704
FLUA_MODULE(posix);
705
706