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