luad.c (8607B)
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 <stdbool.h> 12 #include <errno.h> 13 #include <error.h> 14 #include <sys/un.h> 15 16 #include <signal.h> 17 #include <sys/wait.h> 18 19 #include <poll.h> 20 #include <arpa/inet.h> 21 22 #include <stdarg.h> 23 #include <stdnoreturn.h> 24 25 static int sigchild_fd = -1; 26 27 static void handle_sigchld(int num) { 28 if(sigchild_fd >= 0) { 29 (void) !write(sigchild_fd, "\n", 1); 30 } 31 } 32 33 static void noreturn die(char *fmt, ...) { 34 va_list ap; 35 va_start(ap, fmt); 36 if(errno > 0) { 37 fprintf(stderr, "luad: %s: ", strerror(errno)); 38 } else { 39 fprintf(stderr, "luad: "); 40 } 41 vfprintf(stderr, fmt, ap); 42 exit(1); 43 } 44 45 46 int open_socket(char * pathname) { 47 struct sockaddr_un addr; 48 addr.sun_family = AF_UNIX; 49 if(strlen(pathname) >= sizeof(addr.sun_path)) { 50 die("pathname \"%s\" is too long to bind as unix socket (max %lu)", 51 pathname, sizeof(addr.sun_path)); 52 } 53 strcpy(addr.sun_path, pathname); 54 55 int fd = socket(AF_LOCAL, SOCK_DGRAM, PF_LOCAL); 56 if(fd < 0) { 57 die("can't create socket \"%s\"", pathname); 58 } 59 60 if((bind(fd, (struct sockaddr *) &addr, sizeof addr)) < 0) { 61 die("can't bind socket \"%s\"", pathname); 62 } 63 return fd; 64 } 65 66 int * fds_from_msg(struct msghdr *msg, int * peer_fds) { 67 // * there may be multiple SCM_RIGHTS messages 68 // * we expect three fds 69 // * if we get some number other than three, we should 70 // close all of them and return NULL because someone is up to no good 71 int i = 0; 72 peer_fds[0] = -1; 73 peer_fds[1] = -1; 74 peer_fds[2] = -1; 75 76 for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(msg); 77 cmsg != NULL; 78 cmsg = CMSG_NXTHDR(msg, cmsg)) { 79 if(cmsg->cmsg_level == SOL_SOCKET && 80 cmsg->cmsg_type == SCM_RIGHTS) { 81 int passed_fd_count = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof (int); 82 if(i + passed_fd_count <= 3) { 83 memcpy(&peer_fds[i], CMSG_DATA(cmsg), passed_fd_count * sizeof(int)); 84 85 } else { 86 int * reject_fds = (int *) CMSG_DATA(cmsg); 87 for(int fd = 0; fd < passed_fd_count; fd++) { 88 close(reject_fds[fd]); 89 } 90 } 91 i += passed_fd_count; 92 } 93 } 94 if(i != 3) { 95 /* if the peer sent too many fds, we closed the excess ones 96 * in the reject_fds clause above and now we'll close the 97 * first three. 98 * if they sent too few, we'll close whatever they sent 99 */ 100 101 if(peer_fds[0] >=0) close(peer_fds[0]); 102 if(peer_fds[1] >=0) close(peer_fds[1]); 103 if(peer_fds[2] >=0) close(peer_fds[2]); 104 return NULL; 105 } else { 106 return peer_fds; 107 } 108 } 109 110 void start_lua_child(lua_State *L, 111 char * command_line, int command_line_length, 112 int * fds) { 113 char * word = command_line; 114 char * command_line_end = command_line + command_line_length; 115 char * pathname = NULL; 116 int i = 0; 117 118 fflush(stdout); fflush(stderr); 119 for(int i=0; i < 3; i++) { 120 dup2(fds[i], i); 121 } 122 123 lua_newtable(L); /* arg */ 124 while(word < command_line_end) { 125 char * word_end = memchr(word, '\0', command_line_end - word); 126 if(!word_end) break; 127 if(!pathname) { 128 pathname = strndup(word, (word_end - word)); 129 } 130 lua_pushlstring(L, word, (word_end - word)); 131 lua_seti(L, -2, i++); 132 word = word_end + 1; 133 } 134 if(! pathname) { 135 die("no script name received from client"); 136 } 137 lua_setglobal(L, "arg"); 138 int status = luaL_dofile(L, pathname); 139 if (status) { 140 die("Lua error: %s", lua_tostring(L, -1)); 141 } 142 } 143 144 pid_t fork_lua(lua_State *L, 145 char * command_line, int command_line_length, 146 int * fds) { 147 pid_t pid = fork(); 148 if(pid == 0) { 149 start_lua_child(L, command_line, command_line_length, fds); 150 exit(0); 151 } else if(pid > 0) { 152 free(command_line); 153 return pid; 154 } else { 155 die("fork failed"); 156 } 157 } 158 159 struct peer_entry { 160 pid_t pid; 161 int addrlen; 162 struct sockaddr_un addr; 163 } peer_entries[500]; 164 165 bool register_peer(pid_t pid, struct sockaddr_un *addr, int addrlen) { 166 if(addrlen > sizeof (struct sockaddr_un)) 167 return false; 168 169 int peer_entries_max = sizeof peer_entries / sizeof(peer_entries[0]); 170 171 for(int i=0; i < peer_entries_max; i++) { 172 struct peer_entry *p = &peer_entries[i]; 173 if(p->pid == 0) { 174 p->pid = pid; 175 p->addrlen = addrlen; 176 memcpy(&(p->addr), addr, addrlen); 177 return true; 178 } 179 } 180 return false; 181 } 182 183 struct peer_entry* find_peer(pid_t pid) { 184 int peer_entries_max = sizeof peer_entries / sizeof(peer_entries[0]); 185 186 for(int i=0; i < peer_entries_max; i++) { 187 struct peer_entry *p = &peer_entries[i]; 188 if(p->pid == pid) { 189 return p; 190 } 191 } 192 return NULL; 193 } 194 195 void hangup_peer(int socket, struct sockaddr_un * peer_addr, 196 size_t peer_addr_len, 197 uint32_t retval) { 198 sendto(socket, (void *) &retval, 4, 0, (struct sockaddr *) peer_addr, peer_addr_len); 199 } 200 201 int main(int argc, char **argv) 202 { 203 int status; 204 lua_State *L = luaL_newstate(); 205 luaL_openlibs(L); 206 int sigpipe_fds[2]; 207 208 char * socket_path = getenv("LUAD_SOCKET_PATH"); 209 if(! socket_path) { 210 error(1, 0, "Missing required environment variable LUAD_SOCKET_PATH"); 211 } 212 213 if(pipe(sigpipe_fds) < 0) { 214 die("can't create pipe"); 215 } 216 sigchild_fd = sigpipe_fds[1]; 217 218 signal(SIGCHLD, handle_sigchld); 219 220 if (argc >= 2) { 221 status = luaL_dofile(L, argv[1]); 222 if (status) { 223 error(1, 0, "Error: %s", lua_tostring(L, -1)); 224 } 225 } 226 227 int socket = open_socket(socket_path); 228 struct sockaddr_un peer_addr; 229 230 char buf[1025]; 231 struct iovec iov[1]; 232 iov[0].iov_base = buf; 233 iov[0].iov_len = sizeof buf; 234 struct cmsghdr* cmsg = (struct cmsghdr *) calloc(1000, 1); 235 236 struct msghdr msg = { 237 .msg_name = &peer_addr, 238 .msg_namelen = sizeof peer_addr, 239 .msg_iov = iov, 240 .msg_iovlen = 1, 241 .msg_control = cmsg, 242 .msg_controllen = 1000 243 }; 244 245 struct pollfd pollfds[2] = { 246 { 247 .fd = sigpipe_fds[0], 248 .events = POLLIN, 249 .revents = 0 250 }, 251 { 252 .fd = socket, 253 .events = POLLIN, 254 .revents = 0 255 }, 256 }; 257 258 while(1) { 259 pollfds[0].revents = 0; 260 pollfds[1].revents = 0; 261 262 /* if find_peer says the peer table is full, don't poll the 263 * socket 264 */ 265 if(poll(pollfds, find_peer(0) ? 2 : 1, 10000) == 0) 266 continue; 267 268 if(pollfds[1].revents) { 269 int len = recvmsg(socket, &msg , 0); 270 if(len <= 0) continue; 271 272 int peer_fds[3]; 273 if(fds_from_msg(&msg, peer_fds)) { 274 char *argline = malloc(len); 275 if(! argline) { 276 die("malloc failed"); 277 } 278 memmove(argline, buf, len); 279 280 if(msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) { 281 hangup_peer(socket, &peer_addr, msg.msg_namelen, 0xffff); 282 } else { 283 pid_t child_pid = fork_lua(L, argline, len, peer_fds); 284 register_peer(child_pid, &peer_addr, msg.msg_namelen); 285 } 286 /* close peer fds in parent */ 287 close(peer_fds[0]); 288 close(peer_fds[1]); 289 close(peer_fds[2]); 290 } 291 } 292 293 if(pollfds[0].revents) { 294 pid_t pid; 295 int wstatus; 296 297 if(read(pollfds[1].fd, buf, sizeof buf) < 0) 298 continue; 299 300 while((pid = waitpid(0, &wstatus, WNOHANG)) > 0) { 301 struct peer_entry *peer = find_peer(pid); 302 if(peer) { 303 uint32_t ret = 0; 304 if(WIFEXITED(wstatus)) { 305 ret = htonl(WEXITSTATUS(wstatus)); 306 } else if(WIFSIGNALED(wstatus)) { 307 ret = htonl(128 + WTERMSIG(wstatus)); 308 } 309 hangup_peer(socket, &(peer->addr), peer->addrlen, ret); 310 peer->pid = 0; 311 } 312 } 313 } 314 } 315 return 0; 316 }