1 /* pk-hserver.c - A terminal hyperlinks server for poke. */
2
3 /* Copyright (C) 2019, 2021 Jose E. Marchesi */
4
5 /* This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20
21 #include <pthread.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <stdbool.h>
27 #include <sys/time.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <netdb.h>
33 #include <assert.h>
34 #include "xalloc.h"
35
36 #include "libpoke.h"
37 #include "poke.h"
38
39 #include "pk-cmd.h"
40 #include "pk-hserver.h"
41 #include "pk-repl.h"
42
43 /* The app:// protocol defines a maximum length of messages of two
44 kilobytes. */
45 #define MAXMSG 2048
46
47 /* Thread that runs the server. */
48 static pthread_t hserver_thread;
49
50 /* Socket used by the worker thread. */
51 static int hserver_socket;
52
53 /* Port where the server listens for connections. */
54 static pk_val hserver_port;
55
56 /* hserver_finish is used to tell the server threads to terminate. It
57 is protected with a mutex. */
58 static int hserver_finish;
59 static pthread_mutex_t hserver_mutex = PTHREAD_MUTEX_INITIALIZER;
60
61 /* The server maintains a table with tokens. Each hyperlink uses its
62 own unique token, which is included in the payload and checked upon
63 connection. */
64 static pk_val hserver_tokens;
65
66 int
pk_hserver_port(void)67 pk_hserver_port (void)
68 {
69 pk_val port = pk_decl_val (poke_compiler, "hserver_port");
70
71 assert (hserver_port != PK_NULL);
72 return pk_int_value (port);
73 }
74
75 int
pk_hserver_get_token(void)76 pk_hserver_get_token (void)
77 {
78 pk_val cls = pk_decl_val (poke_compiler, "hserver_get_token");
79 pk_val token;
80 int ret;
81
82 assert (cls != PK_NULL);
83 ret = pk_call (poke_compiler, cls, &token, PK_NULL);
84 assert (ret == PK_OK);
85
86 return pk_int_value (token);
87 }
88
89 static int
pk_hserver_token_p(int token)90 pk_hserver_token_p (int token)
91 {
92 pk_val cls = pk_decl_val (poke_compiler, "hserver_token_p");
93 pk_val token_val = pk_make_int (token, 32);
94 pk_val token_p;
95 int ret;
96
97 assert (cls != PK_NULL);
98 ret = pk_call (poke_compiler, cls, &token_p, token_val, PK_NULL);
99 assert (ret == PK_OK);
100
101 return pk_int_value (token_p);
102 }
103
104 static char
pk_hserver_token_kind(int token)105 pk_hserver_token_kind (int token)
106 {
107 pk_val cls = pk_decl_val (poke_compiler, "hserver_token_kind");
108 pk_val token_val = pk_make_int (token, 32);
109 pk_val token_kind;
110 int ret;
111
112 assert (cls != PK_NULL);
113 ret = pk_call (poke_compiler, cls, &token_kind, token_val, PK_NULL);
114 assert (ret == PK_OK);
115
116 return pk_uint_value (token_kind);
117 }
118
119 static const char *
pk_hserver_cmd(int token)120 pk_hserver_cmd (int token)
121 {
122 pk_val cls = pk_decl_val (poke_compiler, "hserver_token_cmd");
123 pk_val token_val = pk_make_int (token, 32);
124 pk_val cmd;
125 int ret;
126
127 assert (cls != PK_NULL);
128 ret = pk_call (poke_compiler, cls, &cmd, token_val, PK_NULL);
129 assert (ret == PK_OK);
130
131 return pk_string_str (cmd);
132 }
133
134 static int
make_socket(uint16_t port)135 make_socket (uint16_t port)
136 {
137 int sock;
138 struct sockaddr_in name;
139
140 /* Create the socket. */
141 sock = socket (PF_INET, SOCK_STREAM, 0);
142 if (sock < 0)
143 {
144 perror ("socket");
145 pk_fatal (NULL);
146 }
147
148 /* Give the socket a name. */
149 name.sin_family = AF_INET;
150 name.sin_port = htons (port);
151 name.sin_addr.s_addr = htonl (INADDR_ANY);
152 if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
153 {
154 perror ("bind");
155 pk_fatal (NULL);
156 }
157
158 return sock;
159 }
160
161 static int
parse_int(char ** p,int * number)162 parse_int (char **p, int *number)
163 {
164 long int li;
165 char *end;
166
167 errno = 0;
168 li = strtoll (*p, &end, 0);
169 if ((errno != 0 && li == 0)
170 || end == *p)
171 return 0;
172
173 *number = li;
174 *p = end;
175 return 1;
176 }
177
178 static int
read_from_client(int filedes)179 read_from_client (int filedes)
180 {
181 char buffer[MAXMSG];
182 int nbytes;
183
184 nbytes = read (filedes, buffer, MAXMSG);
185 if (nbytes < 0)
186 {
187 /* Read error. */
188 perror ("read");
189 pk_fatal (NULL);
190 return 0;
191 }
192 else if (nbytes == 0)
193 /* End-of-file. */
194 return -1;
195 else
196 {
197 int token;
198 char kind;
199 char *p = buffer;
200 const char *cmd;
201
202 /* Remove the newline at the end. */
203 buffer[nbytes-1] = '\0';
204
205 /* The format of the payload is:
206 [0-9]+/{e,i} */
207
208 /* Get the token and check it. */
209 if (!parse_int (&p, &token))
210 {
211 printf ("PARSING INT\n");
212 return 0;
213 }
214
215 if (!pk_hserver_token_p (token))
216 return 0;
217
218 kind = pk_hserver_token_kind (token);
219
220 if (*p != '\0')
221 return 0;
222
223 switch (kind)
224 {
225 case 'e':
226 /* Command 'execute'. */
227 cmd = pk_hserver_cmd (token);
228 pthread_mutex_lock (&hserver_mutex);
229 pk_repl_display_begin ();
230 pk_puts (p);
231 pk_puts ("\n");
232 pk_cmd_exec (cmd);
233 pk_repl_display_end ();
234 pthread_mutex_unlock (&hserver_mutex);
235 break;
236 case 'i':
237 /* Command 'insert'. */
238 cmd = pk_hserver_cmd (token);
239 pthread_mutex_lock (&hserver_mutex);
240 pk_repl_insert (cmd);
241 pthread_mutex_unlock (&hserver_mutex);
242 break;
243 default:
244 break;
245 }
246
247 return 0;
248 }
249 }
250
251 static void *
hserver_thread_worker(void * data)252 hserver_thread_worker (void *data)
253 {
254 fd_set active_fd_set, read_fd_set;
255 int i;
256 struct sockaddr_in clientname;
257 socklen_t size;
258
259 /* Initialize the set of active sockets. */
260 FD_ZERO (&active_fd_set);
261 FD_SET (hserver_socket, &active_fd_set);
262
263 while (1)
264 {
265 struct timeval timeout = { 0, 200000 };
266
267 /* Block until input arrives on one or more active sockets. */
268 read_fd_set = active_fd_set;
269 if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, &timeout) < 0)
270 {
271 if (errno == EINTR)
272 continue;
273 perror ("select");
274 pk_fatal (NULL);
275 }
276
277 /* Service all the sockets with input pending. */
278 for (i = 0; i < FD_SETSIZE; ++i)
279 if (FD_ISSET (i, &read_fd_set))
280 {
281 if (i == hserver_socket)
282 {
283 /* Connection request on original socket. */
284 int new;
285 size = sizeof (clientname);
286 new = accept (hserver_socket,
287 (struct sockaddr *) &clientname,
288 &size);
289 if (new < 0)
290 {
291 perror ("accept");
292 pk_fatal (NULL);
293 }
294 FD_SET (new, &active_fd_set);
295 }
296 else
297 {
298 /* Data arriving on an already-connected socket. */
299 if (read_from_client (i) < 0)
300 {
301 close (i);
302 FD_CLR (i, &active_fd_set);
303 }
304 }
305 }
306
307 pthread_mutex_lock (&hserver_mutex);
308 if (hserver_finish)
309 {
310 pthread_mutex_unlock (&hserver_mutex);
311 pthread_exit (NULL);
312 }
313 pthread_mutex_unlock (&hserver_mutex);
314 }
315 }
316
317 void
pk_hserver_init(void)318 pk_hserver_init (void)
319 {
320 char hostname[128];
321
322 /* Load the Poke components of the hserver. */
323 if (!pk_load (poke_compiler, "pk-hserver"))
324 pk_fatal ("unable to load the pk-hserver module");
325
326 hserver_tokens = pk_decl_val (poke_compiler, "hserver_tokens");
327 assert (hserver_tokens != PK_NULL);
328
329 if (gethostname (hostname, sizeof (hostname)) != 0)
330 {
331 perror ("gethostname");
332 pk_fatal (NULL);
333 }
334 pk_decl_set_val (poke_compiler, "hserver_hostname",
335 pk_make_string (hostname));
336 }
337
338 void
pk_hserver_start(void)339 pk_hserver_start (void)
340 {
341 int ret;
342 struct sockaddr_in clientname;
343 socklen_t size;
344
345 /* Create the socket and set it up to accept connections. */
346 hserver_socket = make_socket (pk_hserver_port ());
347 if (listen (hserver_socket, 1) < 0)
348 {
349 perror ("listen");
350 pk_fatal (NULL);
351 }
352
353 /* Get a suitable ephemeral port and initialize hserver_port and
354 hserver_port_str. These will be used until the server shuts
355 down. */
356 size = sizeof (clientname);
357 if (getsockname (hserver_socket, &clientname, &size) != 0)
358 {
359 perror ("getsockname");
360 pk_fatal (NULL);
361 }
362
363 pk_decl_set_val (poke_compiler, "hserver_port",
364 pk_make_int (ntohs (clientname.sin_port), 32));
365
366 hserver_finish = 0;
367 ret = pthread_create (&hserver_thread,
368 NULL /* attr */,
369 hserver_thread_worker,
370 NULL);
371
372 if (ret != 0)
373 {
374 errno = ret;
375 perror ("pthread_create");
376 pk_fatal (NULL);
377 }
378 }
379
380 void
pk_hserver_shutdown(void)381 pk_hserver_shutdown (void)
382 {
383 int ret;
384 void *res;
385
386 pthread_mutex_lock (&hserver_mutex);
387 hserver_finish = 1;
388 pthread_mutex_unlock (&hserver_mutex);
389
390 ret = pthread_join (hserver_thread, &res);
391 if (ret != 0)
392 {
393 errno = ret;
394 perror ("pthread_join");
395 pk_fatal (NULL);
396 }
397 }
398
399 char *
pk_hserver_make_hyperlink(char type,const char * cmd)400 pk_hserver_make_hyperlink (char type,
401 const char *cmd)
402 {
403 pk_val cls = pk_decl_val (poke_compiler, "hserver_make_hyperlink");
404 pk_val hyperlink, kind_val, cmd_val;
405 int ret;
406
407 kind_val = pk_make_uint (type, 8);
408 cmd_val = pk_make_string (cmd);
409
410 assert (cls != PK_NULL);
411 ret = pk_call (poke_compiler, cls, &hyperlink,
412 kind_val, cmd_val, PK_NULL);
413 assert (ret == PK_OK);
414
415 return xstrdup (pk_string_str (hyperlink));
416 }
417