luad

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 9b7832a44d2e19ea39b249287ca0c872246366dc
parent 713d30f9a2f42bcc43c7f5bba53d962fcba207cf
Author: Daniel Barlow <dan@telent.net>
Date:   Thu, 14 May 2026 23:20:11 +0100

WIP rewrite in C

$ ls -l result/bin/luad src/luad
-r-xr-xr-x 1 root root  415032 Jan  1  1970 result/bin/luad
-rwxr-xr-x 1 dan  users  21872 May 14 23:18 src/luad

it's difficult to argue against that size saving, and we weren't
doing much except calling libraries written in C anyway

this is WIP as in it's functional mostly but it's a first pass, may
contain bugs, needs lint, hardcodes the socket path

Diffstat:
Asrc/luad.c | 242+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 242 insertions(+), 0 deletions(-)

diff --git a/src/luad.c b/src/luad.c @@ -0,0 +1,242 @@ +#include <unistd.h> + +#include <lua.h> +#include <lauxlib.h> +#include <lualib.h> + +#include <sys/socket.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <sys/un.h> + +#include <signal.h> +#include <sys/wait.h> + +#include <poll.h> +#include <arpa/inet.h> + + +static int sigchild_fd = -1; + +static void handle_sigchld(int num) { + if(sigchild_fd >= 0) { + write(sigchild_fd, "\n", 1); + } +} + + +int open_socket(char * pathname) { + + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, pathname, sizeof(addr.sun_path) - 1); + + int fd = socket(AF_LOCAL, SOCK_DGRAM, PF_LOCAL); + if(fd < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + if((bind(fd, (struct sockaddr *) &addr, sizeof addr)) < 0) { + perror("bind"); + exit(EXIT_FAILURE); + } + return fd; +} + +int * fds_from_msg(struct msghdr *msg, size_t peer_fds_count) { + int *peer_fds = (int *) calloc(peer_fds_count, sizeof(int)); + for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR(msg, cmsg)) { + if(cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + memcpy(peer_fds, CMSG_DATA(cmsg), peer_fds_count * sizeof(int)); + } + } + return peer_fds; +} + +int start_lua_child(lua_State *L, + char * command_line, int command_line_length, + int * fds, int fds_count) { + char * word = command_line; + char * command_line_end = command_line + command_line_length; + char * pathname = NULL; + int i = 0; + + fflush(stdout); fflush(stderr); + for(int i=0; i < fds_count; i++) { + if(fds[i] > 0) dup2(fds[i], i); + } + + lua_newtable(L); /* arg */ + while(word < command_line_end) { + char * word_end = memchr(word, '\0', command_line_end - word); + if(!word_end) break; + if(!pathname) { + pathname = strndup(word, (word_end - word)); + } + lua_pushlstring(L, word, (word_end - word)); + lua_seti(L, -2, i++); + word = word_end + 1; + } + lua_setglobal(L, "arg"); + int status = luaL_dofile(L, pathname); + if (status) { + fprintf(stderr, "Error: %s\n", lua_tostring(L, -1)); + return 1; + } else { + return 0; + } +} + +pid_t fork_lua(lua_State *L, + char * command_line, int command_line_length, + int * fds, int fds_count) { + pid_t pid = fork(); + if(pid == 0) { + exit(start_lua_child(L, command_line, command_line_length, + fds, fds_count)); + } else if(pid > 0) { + free(command_line); + return pid; + } else { + perror("fork"); + return -1; + } +} + +struct peer_entry { + pid_t pid; + int addrlen; + struct sockaddr_un addr; +}; + +int register_peer(struct peer_entry *table, pid_t pid, struct sockaddr_un *addr, int addrlen) { + if(addrlen > sizeof (struct sockaddr_un)) + return -1; + + struct peer_entry *p = table; + while(p->pid != 0 && p->pid != -1) + p++; + if(p->pid == -1) + return -1; + p->pid = pid; + p->addrlen = addrlen; + memcpy(&(p->addr), addr, addrlen); +} + +struct peer_entry* find_peer(struct peer_entry *table, pid_t pid) { + while(table->pid != -1) { + if(table->pid == pid) + return table; + table++; + } + return NULL; +} + + + +int main(int argc, char **argv) +{ + int status; + lua_State *L = luaL_newstate(); + luaL_openlibs(L); + int sigpipe_fds[2]; + struct peer_entry * peer_table = calloc(501, sizeof (struct peer_entry)); + peer_table[500].pid = -1; + + if(pipe(sigpipe_fds) < 0) { + perror("pipe"); + exit(EXIT_FAILURE); + } + sigchild_fd = sigpipe_fds[1]; + + signal(SIGCHLD, handle_sigchld); + + if (argc >= 2) { + if (!access(argv[1], F_OK) == 0) { + fprintf(stderr, "File %s does not exist!\n", argv[1]); + exit(1); + } + + status = luaL_dofile(L, argv[1]); + if (status) { + fprintf(stderr, "Error: %s\n", lua_tostring(L, -1)); + return 1; + } + } + + int socket = open_socket("/tmp/mysock"); /* FIXME hardcoded */ + struct sockaddr_un peer_addr; + socklen_t peer_addr_len; + char buf[1025]; + struct iovec iov[1]; + iov[0].iov_base = buf; + iov[0].iov_len = sizeof buf; + struct cmsghdr* cmsg = (struct cmsghdr *) calloc(1000, 1); + + struct msghdr msg = { + .msg_name = &peer_addr, + .msg_namelen = sizeof peer_addr, + .msg_iov = iov, + .msg_iovlen = 1, + .msg_control = cmsg, + .msg_controllen = 1000 + }; + + struct pollfd pollfds[2] = { + { + .fd = socket, + .events = POLLIN, + .revents = 0 + }, + { + .fd = sigpipe_fds[0], + .events = POLLIN, + .revents = 0 + }, + }; + + while(1) { + if(poll(pollfds, 2, 10000) == 0) + continue; + + if(pollfds[0].revents) { + int len = recvmsg(socket, &msg , 0); + int *peer_fds = fds_from_msg(&msg, 5); + char *argline = malloc(len); + memmove(argline, buf, len); + + pid_t child_pid = fork_lua(L, argline, len, peer_fds, 5); + if(child_pid >= 0) { + register_peer(peer_table, child_pid, &peer_addr, msg.msg_namelen); + } + } + + if(pollfds[1].revents) { + pid_t pid; + int wstatus; + + read(pollfds[1].fd, buf, sizeof buf); + while((pid=waitpid(0, &wstatus, WNOHANG)) > 0) { + struct peer_entry *peer = find_peer(peer_table, pid); + if(peer) { + uint32_t ret = 0; + if(WIFEXITED(wstatus)) { + ret = htonl(WEXITSTATUS(wstatus)); + } else if(WIFSIGNALED(wstatus)) { + ret = htonl(128 + WTERMSIG(wstatus)); + } + sendto(socket, (void *) &ret, 4, 0, + (const struct sockaddr *) &(peer->addr), + peer->addrlen); + peer->pid = 0; + } + } + } + } + return 0; +}