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 }