ltk

GUI toolkit for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/ltk.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
Log | Files | Refs | README | LICENSE

ltkc.c (7251B)


      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() {
     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 }