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 }