luac.c (4666B)
1 #include <unistd.h> 2 3 #include <lua.h> 4 #include <lauxlib.h> 5 #include <lualib.h> 6 7 #include <sys/socket.h> 8 #include <string.h> 9 #include <stdlib.h> 10 #include <stdint.h> 11 #include <sys/un.h> 12 13 #include <arpa/inet.h> 14 15 #include <error.h> 16 #include <errno.h> 17 18 #define die(...) error(EXIT_FAILURE, errno, __VA_ARGS__) 19 20 // copied from OpenBSD 21 // https://github.com/openbsd/src/blob/master/lib/libc/string/strlcpy.c 22 23 /* 24 * Copy string src to buffer dst of size dsize. At most dsize-1 25 * chars will be copied. Always NUL terminates (unless dsize == 0). 26 * Returns strlen(src); if retval >= dsize, truncation occurred. 27 */ 28 size_t strlcpy(char *dst, const char *src, size_t dsize) 29 { 30 const char *osrc = src; 31 size_t nleft = dsize; 32 33 /* Copy as many bytes as will fit. */ 34 if (nleft != 0) { 35 while (--nleft != 0) { 36 if ((*dst++ = *src++) == '\0') 37 break; 38 } 39 } 40 41 /* Not enough room in dst, add NUL and traverse rest of src. */ 42 if (nleft == 0) { 43 if (dsize != 0) 44 *dst = '\0'; /* NUL-terminate dst */ 45 while (*src++) 46 ; 47 } 48 49 return(src - osrc - 1); /* count does not include NUL */ 50 } 51 52 int open_socket(struct sockaddr_un *addr, struct sockaddr_un * peer_addr) { 53 int fd = socket(AF_LOCAL, SOCK_DGRAM, PF_LOCAL); 54 if(fd < 0) { 55 die("can't create socket"); 56 } 57 58 // local address is in the abstract namespace and ok to include 59 // trailing nul bytes 60 if((bind(fd, (struct sockaddr *) addr, sizeof (struct sockaddr_un))) < 0) { 61 die("can't bind socket local address"); 62 } 63 64 // peer address is in the actual filesystem, so we need to pass 65 // the real length 66 int namelen = sizeof peer_addr->sun_family + strlen(peer_addr->sun_path); 67 if((connect(fd, (struct sockaddr *) peer_addr, namelen)) < 0) { 68 die("can't connect socket to \"%s\"", peer_addr->sun_path); 69 } 70 71 return fd; 72 } 73 74 int encode_args(char * buf, int len, int argc, char ** argv) { 75 76 char * buf_end = buf + len; 77 char * p = buf; 78 for (int i = 1; i < argc; i++) { 79 p += strlcpy(p, argv[i], buf_end - p); 80 if(p >= buf_end) { 81 error(1, 0, "comand line too long"); 82 } 83 p++; 84 } 85 return p - buf; 86 } 87 88 int main(int argc, char **argv) 89 { 90 #ifdef TEST 91 char *test_args[] = // { "luac", "john", "paul is dead", "ringo" }; 92 { "luac", "hello.lua" }; 93 write(1, encode_args(25, 2, test_args), 25); 94 char *test2_args[] = { // this is too long 95 "luac", "0123456789", "0123456789", "012" 96 }; 97 write(1, encode_args(25, 4, test2_args), 25); 98 exit(0); 99 #endif 100 101 if (argc < 2) { 102 fprintf(stderr, "Usage: %s pathname.lua\n", argv[0]); 103 exit(1); 104 } 105 106 char * socket_path = getenv("LUAD_SOCKET_PATH"); 107 if(! socket_path) { 108 error(1, 0, "Missing required environment variable LUAD_SOCKET_PATH"); 109 } 110 111 char * iobuf = (char *) calloc(1024, sizeof(char)); 112 if(!iobuf) return 1; 113 int iobuf_len = encode_args(iobuf, 1024, argc, argv); 114 115 116 /* for sendmsg with peer address to work on the luad side, we need to 117 * bind our connected docket on this side to something, so we create 118 * a hopefully unique abstract name based on the pid and argv */ 119 120 struct sockaddr_un my_addr = { AF_UNIX, "" }; 121 int maxnamelen = sizeof my_addr.sun_path; 122 pid_t mypid = getpid(); 123 memcpy(my_addr.sun_path + 1, (void *) &mypid, sizeof mypid); 124 memcpy(my_addr.sun_path + 1 + sizeof mypid, iobuf, maxnamelen - sizeof mypid - 1); 125 126 struct sockaddr_un peer_addr = { AF_UNIX }; 127 strlcpy(peer_addr.sun_path, socket_path, sizeof peer_addr.sun_path); 128 int socket = open_socket(&my_addr, &peer_addr); 129 130 struct cmsghdr *cmsg; 131 int myfds[] = {0, 1, 2}; 132 struct iovec iov = { 133 .iov_base = iobuf, 134 .iov_len = iobuf_len 135 }; 136 union { /* Ancillary data buffer, wrapped in a union 137 in order to ensure it is suitably aligned */ 138 char buf[CMSG_SPACE(sizeof(myfds))]; 139 struct cmsghdr align; 140 } u; 141 142 struct msghdr msg = { 143 .msg_name = 0, 144 .msg_namelen = 0, 145 .msg_iov = &iov, 146 .msg_iovlen = 1, 147 .msg_control = u.buf, 148 .msg_controllen = sizeof(u.buf) 149 }; 150 151 cmsg = CMSG_FIRSTHDR(&msg); 152 cmsg->cmsg_level = SOL_SOCKET; 153 cmsg->cmsg_type = SCM_RIGHTS; 154 cmsg->cmsg_len = CMSG_LEN(sizeof(myfds)); 155 memcpy(CMSG_DATA(cmsg), myfds, sizeof(myfds)); 156 157 if(sendmsg(socket, &msg, 0) <= 0) { 158 die("sendmsg failed"); 159 } 160 161 uint32_t exitstatus = 0; 162 recv(socket, (void *) &exitstatus, sizeof exitstatus, 0); 163 exit((int) ntohl(exitstatus)); 164 }