ltkc.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 }