luad

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

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 }