URI: 
       tltkc.c - ltk - Socket-based GUI for X11 (WIP)
  HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tltkc.c (7123B)
       ---
            1 /*
            2  * Copyright (c) 2021-2023 lumidify <nobody@lumidify.org>
            3  *
            4  * Permission to use, copy, modify, and/or distribute this software for any
            5  * purpose with or without fee is hereby granted, provided that the above
            6  * copyright notice and this permission notice appear in all copies.
            7  *
            8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
            9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
           10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
           11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
           12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
           13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
           14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
           15  */
           16 
           17 #include <stdio.h>
           18 #include <stdlib.h>
           19 #include <string.h>
           20 #include <stdarg.h>
           21 #include <stddef.h>
           22 #include <unistd.h>
           23 #include <time.h>
           24 #include <errno.h>
           25 #include <inttypes.h>
           26 #include <sys/select.h>
           27 #include <sys/socket.h>
           28 #include <sys/un.h>
           29 #include "util.h"
           30 #include "memory.h"
           31 #include "macros.h"
           32 
           33 #define BLK_SIZE 128
           34 char tmp_buf[BLK_SIZE];
           35 
           36 static struct {
           37         char *in_buffer;  /* text that is read from stdin and written to the socket */
           38         int in_len;
           39         int in_alloc;
           40         char *out_buffer; /* text that is read from the socket and written to stdout */
           41         int out_len;
           42         int out_alloc;
           43 } io_buffers;
           44 
           45 static char *ltk_dir = NULL;
           46 static char *sock_path = NULL;
           47 static int sockfd = -1;
           48 
           49 int main(int argc, char *argv[]) {
           50         char num[12];
           51         int bs = 0;
           52         int last_newline = 1;
           53         int in_str = 0;
           54         uint32_t seq = 0;
           55         int maxfd;
           56         int infd = fileno(stdin);
           57         int outfd = fileno(stdout);
           58         struct sockaddr_un un;
           59         fd_set rfds, wfds, rallfds, wallfds;
           60         struct timeval tv;
           61         tv.tv_sec = 0;
           62         tv.tv_usec = 20000;
           63         size_t path_size;
           64 
           65         if (argc != 2) {
           66                 (void)fprintf(stderr, "USAGE: ltkc <socket id>\n");
           67                 return 1;
           68         }
           69 
           70         ltk_dir = ltk_setup_directory();
           71         if (!ltk_dir) {
           72                 (void)fprintf(stderr, "Unable to setup ltk directory.\n");
           73                 return 1;
           74         }
           75 
           76         /* 7 because of "/", ".sock", and '\0' */
           77         path_size = strlen(ltk_dir) + strlen(argv[1]) + 7;
           78         sock_path = ltk_malloc(path_size);
           79         snprintf(sock_path, path_size, "%s/%s.sock", ltk_dir, argv[1]);
           80 
           81         if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
           82                 perror("Socket error");
           83                 return -1;
           84         }
           85         memset(&un, 0, sizeof(un));
           86         un.sun_family = AF_UNIX;
           87         if (path_size > sizeof(un.sun_path)) {
           88                 (void)fprintf(stderr, "Socket path too long.\n");
           89                 return 1;
           90         }
           91         strcpy(un.sun_path, sock_path);
           92         if (connect(sockfd, (struct sockaddr *)&un, offsetof(struct sockaddr_un, sun_path) + path_size) < 0) {
           93                 perror("Socket error");
           94                 return -2;
           95         }
           96         if (set_nonblock(sockfd)) {
           97                 (void)fprintf(stderr, "Unable to set socket to non-blocking mode.\n");
           98                 return 1;
           99         } else if (set_nonblock(infd)) {
          100                 (void)fprintf(stderr, "Unable to set stdin to non-blocking mode.\n");
          101                 return 1;
          102         } else if (set_nonblock(outfd)) {
          103                 (void)fprintf(stderr, "Unable to set stdout to non-blocking mode.\n");
          104                 return 1;
          105         }
          106 
          107         io_buffers.in_buffer = ltk_malloc(BLK_SIZE);
          108         io_buffers.in_alloc = BLK_SIZE;
          109 
          110         io_buffers.out_buffer = ltk_malloc(BLK_SIZE);
          111         io_buffers.out_alloc = BLK_SIZE;
          112 
          113         FD_ZERO(&rallfds);
          114         FD_ZERO(&wallfds);
          115 
          116         FD_SET(sockfd, &rallfds);
          117         FD_SET(infd, &rallfds);
          118         FD_SET(sockfd, &wallfds);
          119         FD_SET(outfd, &wallfds);
          120         maxfd = sockfd > infd ? sockfd : infd;
          121         if (maxfd < outfd)
          122                 maxfd = outfd;
          123 
          124         struct timespec now, elapsed, last, sleep_time;
          125         clock_gettime(CLOCK_MONOTONIC, &last);
          126         sleep_time.tv_sec = 0;
          127 
          128         while (1) {
          129                 if (!FD_ISSET(sockfd, &rallfds) && io_buffers.out_len == 0)
          130                         break;
          131                 rfds = rallfds;
          132                 wfds = wallfds;
          133                 select(maxfd + 1, &rfds, &wfds, NULL, &tv);
          134 
          135                 /* FIXME: make all this buffer handling a bit more intelligent */
          136                 if (FD_ISSET(sockfd, &rfds)) {
          137                         while (1) {
          138                                 ltk_grow_string(&io_buffers.out_buffer,
          139                                                 &io_buffers.out_alloc,
          140                                                 io_buffers.out_len + BLK_SIZE);
          141                                 int nread = read(sockfd,
          142                                                  io_buffers.out_buffer + io_buffers.out_len,
          143                                                  BLK_SIZE);
          144                                 if (nread < 0) {
          145                                         /* FIXME: distinguish errors */
          146                                         break;
          147                                 } else if (nread == 0) {
          148                                         FD_CLR(sockfd, &rallfds);
          149                                         FD_CLR(sockfd, &wallfds);
          150                                         break;
          151                                 } else {
          152                                         io_buffers.out_len += nread;
          153                                 }
          154                         }
          155                 }
          156 
          157                 if (FD_ISSET(infd, &rfds)) {
          158                         while (1) {
          159                                 int nread = read(infd, tmp_buf, BLK_SIZE);
          160                                 if (nread < 0) {
          161                                         break;
          162                                 } else if (nread == 0) {
          163                                         FD_CLR(infd, &rallfds);
          164                                         break;
          165                                 } else {
          166                                         for (int i = 0; i < nread; i++) {
          167                                                 if (last_newline) {
          168                                                         int numlen = snprintf(num, sizeof(num), "%"PRIu32" ", seq);
          169                                                         if (numlen < 0 || (unsigned)numlen >= sizeof(num))
          170                                                                 ltk_fatal("There's a bug in the universe.\n");
          171                                                         ltk_grow_string(
          172                                                             &io_buffers.in_buffer,
          173                                                             &io_buffers.in_alloc,
          174                                                             io_buffers.in_len + numlen
          175                                                         );
          176                                                         memcpy(io_buffers.in_buffer + io_buffers.in_len, num, numlen);
          177                                                         io_buffers.in_len += numlen;
          178                                                         last_newline = 0;
          179                                                         seq++;
          180                                                 }
          181                                                 if (tmp_buf[i] == '\\') {
          182                                                         bs++;
          183                                                         bs %= 2;
          184                                                 } else if (tmp_buf[i] == '"' && !bs) {
          185                                                         in_str = !in_str;
          186                                                 } else if (tmp_buf[i] == '\n' && !in_str) {
          187                                                         last_newline = 1;
          188                                                 } else {
          189                                                         bs = 0;
          190                                                 }
          191                                                 if (io_buffers.in_len == io_buffers.in_alloc) {
          192                                                         ltk_grow_string(
          193                                                             &io_buffers.in_buffer,
          194                                                             &io_buffers.in_alloc,
          195                                                             io_buffers.in_len + 1
          196                                                         );
          197                                                 }
          198                                                 io_buffers.in_buffer[io_buffers.in_len++] = tmp_buf[i];
          199                                         }
          200                                 }
          201                         }
          202                 }
          203 
          204                 if (FD_ISSET(sockfd, &wfds)) {
          205                         while (io_buffers.in_len > 0) {
          206                                 int maxwrite = BLK_SIZE > io_buffers.in_len ?
          207                                                io_buffers.in_len : BLK_SIZE;
          208                                 int nwritten = write(sockfd, io_buffers.in_buffer, maxwrite);
          209                                 if (nwritten <= 0) {
          210                                         break;
          211                                 } else {
          212                                         memmove(io_buffers.in_buffer,
          213                                                 io_buffers.in_buffer + nwritten,
          214                                                 io_buffers.in_len - nwritten);
          215                                         io_buffers.in_len -= nwritten;
          216                                 }
          217                         }
          218                 }
          219 
          220                 if (FD_ISSET(outfd, &wfds)) {
          221                         while (io_buffers.out_len > 0) {
          222                                 int maxwrite = BLK_SIZE > io_buffers.out_len ?
          223                                                io_buffers.out_len : BLK_SIZE;
          224                                 int nwritten = write(outfd, io_buffers.out_buffer, maxwrite);
          225                                 if (nwritten <= 0) {
          226                                         break;
          227                                 } else {
          228                                         memmove(io_buffers.out_buffer,
          229                                                 io_buffers.out_buffer + nwritten,
          230                                                 io_buffers.out_len - nwritten);
          231                                         io_buffers.out_len -= nwritten;
          232                                 }
          233                         }
          234                 }
          235                 clock_gettime(CLOCK_MONOTONIC, &now);
          236                 ltk_timespecsub(&now, &last, &elapsed);
          237                 /* FIXME: configure framerate */
          238                 if (elapsed.tv_sec == 0 && elapsed.tv_nsec < 20000000LL) {
          239                         sleep_time.tv_nsec = 20000000LL - elapsed.tv_nsec;
          240                         nanosleep(&sleep_time, NULL);
          241                 }
          242                 last = now;
          243         }
          244 
          245         ltk_cleanup();
          246 
          247         return 0;
          248 }
          249 
          250 void
          251 ltk_log_msg(const char *mode, const char *format, va_list args) {
          252         fprintf(stderr, "ltkc %s: ", mode);
          253         vfprintf(stderr, format, args);
          254 }
          255 
          256 void
          257 ltk_cleanup() {
          258         if (sockfd >= 0)
          259                 close(sockfd);
          260         if (ltk_dir)
          261                 ltk_free(ltk_dir);
          262         if (sock_path)
          263                 ltk_free(sock_path);
          264         if (io_buffers.in_buffer)
          265                 ltk_free(io_buffers.in_buffer);
          266         if (io_buffers.out_buffer)
          267                 ltk_free(io_buffers.out_buffer);
          268 }