URI:
       ltkc.c - ltk - GUI toolkit 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)
  HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       ltkc.c (7255B)
       ---
            1 /*
            2  * Copyright (c) 2021-2024 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 <errno.h>
           18 #include <stdio.h>
           19 #include <string.h>
           20 #include <stddef.h>
           21 #include <unistd.h>
           22 #include <time.h>
           23 #include <inttypes.h>
           24 #include <sys/select.h>
           25 #include <sys/socket.h>
           26 #include <sys/un.h>
           27 
           28 #include "util.h"
           29 
           30 #include <ltk/util.h>
           31 #include <ltk/memory.h>
           32 #include <ltk/macros.h>
           33 
           34 #define BLK_SIZE 128
           35 char tmp_buf[BLK_SIZE];
           36 
           37 static struct {
           38         char *in_buffer;  /* text that is read from stdin and written to the socket */
           39         int in_len;
           40         int in_alloc;
           41         char *out_buffer; /* text that is read from the socket and written to stdout */
           42         int out_len;
           43         int out_alloc;
           44 } io_buffers;
           45 
           46 static char *ltkd_dir = NULL;
           47 static char *sock_path = NULL;
           48 static int sockfd = -1;
           49 
           50 void
           51 ltkc_log_msg(const char *mode, const char *format, va_list args) {
           52         fprintf(stderr, "ltkc %s: ", mode);
           53         vfprintf(stderr, format, args);
           54 }
           55 
           56 static void
           57 ltkc_cleanup(void) {
           58         if (sockfd >= 0)
           59                 close(sockfd);
           60         if (ltkd_dir)
           61                 ltk_free(ltkd_dir);
           62         if (sock_path)
           63                 ltk_free(sock_path);
           64         if (io_buffers.in_buffer)
           65                 ltk_free(io_buffers.in_buffer);
           66         if (io_buffers.out_buffer)
           67                 ltk_free(io_buffers.out_buffer);
           68 }
           69 
           70 LTK_GEN_LOG_FUNC_PROTO(ltkc)
           71 LTK_GEN_LOG_FUNCS(ltkc, ltkc_log_msg, ltkc_cleanup)
           72 
           73 int main(int argc, char *argv[]) {
           74         char num[12];
           75         int bs = 0;
           76         int last_newline = 1;
           77         int in_str = 0;
           78         uint32_t seq = 0;
           79         int maxfd;
           80         int infd = fileno(stdin);
           81         int outfd = fileno(stdout);
           82         struct sockaddr_un un;
           83         fd_set rfds, wfds, rallfds, wallfds;
           84         struct timeval tv;
           85         tv.tv_sec = 0;
           86         tv.tv_usec = 20000;
           87         size_t path_size;
           88 
           89         if (argc != 2) {
           90                 (void)fprintf(stderr, "USAGE: ltkc <socket id>\n");
           91                 return 1;
           92         }
           93 
           94         ltkd_dir = ltk_setup_directory("LTKDDIR", ".ltkd", 1);
           95         if (!ltkd_dir) {
           96                 (void)fprintf(stderr, "Unable to setup ltk directory.\n");
           97                 return 1;
           98         }
           99 
          100         /* 7 because of "/", ".sock", and '\0' */
          101         path_size = strlen(ltkd_dir) + strlen(argv[1]) + 7;
          102         sock_path = ltk_malloc(path_size);
          103         snprintf(sock_path, path_size, "%s/%s.sock", ltkd_dir, argv[1]);
          104 
          105         if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
          106                 perror("Socket error");
          107                 return -1;
          108         }
          109         memset(&un, 0, sizeof(un));
          110         un.sun_family = AF_UNIX;
          111         if (path_size > sizeof(un.sun_path)) {
          112                 (void)fprintf(stderr, "Socket path too long.\n");
          113                 return 1;
          114         }
          115         strcpy(un.sun_path, sock_path);
          116         if (connect(sockfd, (struct sockaddr *)&un, offsetof(struct sockaddr_un, sun_path) + path_size) < 0) {
          117                 perror("Socket error");
          118                 return -2;
          119         }
          120         if (ltkd_set_nonblock(sockfd)) {
          121                 (void)fprintf(stderr, "Unable to set socket to non-blocking mode.\n");
          122                 return 1;
          123         } else if (ltkd_set_nonblock(infd)) {
          124                 (void)fprintf(stderr, "Unable to set stdin to non-blocking mode.\n");
          125                 return 1;
          126         } else if (ltkd_set_nonblock(outfd)) {
          127                 (void)fprintf(stderr, "Unable to set stdout to non-blocking mode.\n");
          128                 return 1;
          129         }
          130 
          131         io_buffers.in_buffer = ltk_malloc(BLK_SIZE);
          132         io_buffers.in_alloc = BLK_SIZE;
          133 
          134         io_buffers.out_buffer = ltk_malloc(BLK_SIZE);
          135         io_buffers.out_alloc = BLK_SIZE;
          136 
          137         FD_ZERO(&rallfds);
          138         FD_ZERO(&wallfds);
          139 
          140         FD_SET(sockfd, &rallfds);
          141         FD_SET(infd, &rallfds);
          142         FD_SET(sockfd, &wallfds);
          143         FD_SET(outfd, &wallfds);
          144         maxfd = sockfd > infd ? sockfd : infd;
          145         if (maxfd < outfd)
          146                 maxfd = outfd;
          147 
          148         struct timespec now, elapsed, last, sleep_time;
          149         clock_gettime(CLOCK_MONOTONIC, &last);
          150         sleep_time.tv_sec = 0;
          151 
          152         while (1) {
          153                 if (!FD_ISSET(sockfd, &rallfds) && io_buffers.out_len == 0)
          154                         break;
          155                 rfds = rallfds;
          156                 wfds = wallfds;
          157                 select(maxfd + 1, &rfds, &wfds, NULL, &tv);
          158 
          159                 /* FIXME: make all this buffer handling a bit more intelligent */
          160                 if (FD_ISSET(sockfd, &rfds)) {
          161                         while (1) {
          162                                 ltk_grow_string(&io_buffers.out_buffer,
          163                                                 &io_buffers.out_alloc,
          164                                                 io_buffers.out_len + BLK_SIZE);
          165                                 int nread = read(sockfd,
          166                                                  io_buffers.out_buffer + io_buffers.out_len,
          167                                                  BLK_SIZE);
          168                                 if (nread < 0) {
          169                                         /* FIXME: distinguish errors */
          170                                         break;
          171                                 } else if (nread == 0) {
          172                                         FD_CLR(sockfd, &rallfds);
          173                                         FD_CLR(sockfd, &wallfds);
          174                                         break;
          175                                 } else {
          176                                         io_buffers.out_len += nread;
          177                                 }
          178                         }
          179                 }
          180 
          181                 if (FD_ISSET(infd, &rfds)) {
          182                         while (1) {
          183                                 int nread = read(infd, tmp_buf, BLK_SIZE);
          184                                 if (nread < 0) {
          185                                         break;
          186                                 } else if (nread == 0) {
          187                                         FD_CLR(infd, &rallfds);
          188                                         break;
          189                                 } else {
          190                                         for (int i = 0; i < nread; i++) {
          191                                                 if (last_newline) {
          192                                                         int numlen = snprintf(num, sizeof(num), "%"PRIu32" ", seq);
          193                                                         if (numlen < 0 || (unsigned)numlen >= sizeof(num))
          194                                                                 ltkc_fatal("There's a bug in the universe.\n");
          195                                                         ltk_grow_string(
          196                                                             &io_buffers.in_buffer,
          197                                                             &io_buffers.in_alloc,
          198                                                             io_buffers.in_len + numlen
          199                                                         );
          200                                                         memcpy(io_buffers.in_buffer + io_buffers.in_len, num, numlen);
          201                                                         io_buffers.in_len += numlen;
          202                                                         last_newline = 0;
          203                                                         seq++;
          204                                                 }
          205                                                 if (tmp_buf[i] == '\\') {
          206                                                         bs++;
          207                                                         bs %= 2;
          208                                                 } else if (tmp_buf[i] == '"' && !bs) {
          209                                                         in_str = !in_str;
          210                                                 } else if (tmp_buf[i] == '\n' && !in_str) {
          211                                                         last_newline = 1;
          212                                                 } else {
          213                                                         bs = 0;
          214                                                 }
          215                                                 if (io_buffers.in_len == io_buffers.in_alloc) {
          216                                                         ltk_grow_string(
          217                                                             &io_buffers.in_buffer,
          218                                                             &io_buffers.in_alloc,
          219                                                             io_buffers.in_len + 1
          220                                                         );
          221                                                 }
          222                                                 io_buffers.in_buffer[io_buffers.in_len++] = tmp_buf[i];
          223                                         }
          224                                 }
          225                         }
          226                 }
          227 
          228                 if (FD_ISSET(sockfd, &wfds)) {
          229                         while (io_buffers.in_len > 0) {
          230                                 int maxwrite = BLK_SIZE > io_buffers.in_len ?
          231                                                io_buffers.in_len : BLK_SIZE;
          232                                 int nwritten = write(sockfd, io_buffers.in_buffer, maxwrite);
          233                                 if (nwritten <= 0) {
          234                                         break;
          235                                 } else {
          236                                         memmove(io_buffers.in_buffer,
          237                                                 io_buffers.in_buffer + nwritten,
          238                                                 io_buffers.in_len - nwritten);
          239                                         io_buffers.in_len -= nwritten;
          240                                 }
          241                         }
          242                 }
          243 
          244                 if (FD_ISSET(outfd, &wfds)) {
          245                         while (io_buffers.out_len > 0) {
          246                                 int maxwrite = BLK_SIZE > io_buffers.out_len ?
          247                                                io_buffers.out_len : BLK_SIZE;
          248                                 int nwritten = write(outfd, io_buffers.out_buffer, maxwrite);
          249                                 if (nwritten <= 0) {
          250                                         break;
          251                                 } else {
          252                                         memmove(io_buffers.out_buffer,
          253                                                 io_buffers.out_buffer + nwritten,
          254                                                 io_buffers.out_len - nwritten);
          255                                         io_buffers.out_len -= nwritten;
          256                                 }
          257                         }
          258                 }
          259                 clock_gettime(CLOCK_MONOTONIC, &now);
          260                 ltk_timespecsub(&now, &last, &elapsed);
          261                 /* FIXME: configure framerate */
          262                 if (elapsed.tv_sec == 0 && elapsed.tv_nsec < 20000000LL) {
          263                         sleep_time.tv_nsec = 20000000LL - elapsed.tv_nsec;
          264                         nanosleep(&sleep_time, NULL);
          265                 }
          266                 last = now;
          267         }
          268 
          269         ltkc_cleanup();
          270 
          271         return 0;
          272 }