]> rtime.felk.cvut.cz Git - coffee/mt-apps.git/blob - mt_server.c
Use server at localhost
[coffee/mt-apps.git] / mt_server.c
1 #define _GNU_SOURCE         /* See feature_test_macros(7) */
2 #include <errno.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <syslog.h>
7
8 #include "mt_server.h"
9 #include "signal_exit.h"
10 #include "mt_blank.h"
11
12 char *new_line()
13 {
14     char *line = (char *)malloc((LWS_PRE + INPUT_LINE_LENGTH)*sizeof(char));
15     if (!line) {
16         perror("malloc");
17         return NULL;
18     }
19     return line + LWS_PRE;
20 }
21
22 char *copy_line(char *line) {
23     char *copy = new_line();
24     if (!copy) {
25         return NULL;
26     }
27     strncpy(copy, line, INPUT_LINE_LENGTH);
28 }
29
30 void free_line(char *line)
31 {
32     free(line - LWS_PRE);
33 }
34
35 node *node_init(char *line)
36 {
37     node *res = (node *)malloc(sizeof(node));
38     if (!res) {
39         perror("malloc");
40         return res;
41     }
42     res->line = line;;
43     res->next = NULL;
44     return res;
45 }
46
47 list *list_init()
48 {
49     list *res = (list *)calloc(1, sizeof(list));
50     if (!res) {
51         perror("malloc");
52     }
53     return res;
54 }
55
56 int list_add(list *in, char *line)
57 {
58     node *new = node_init(line);
59     if (!new) {
60         return -1;
61     }
62     if (!in->first) {
63         in->first = new;
64     } else {
65         in->last->next = new;
66     }
67     in->last = new;
68     return 0;
69 }
70
71 void list_remove(list *in)
72 {
73     node *tmp = in->first;
74     if (tmp) {
75         in->first = tmp->next;
76         if (!tmp->next) {
77             in->last = NULL;
78         }
79         free_line(tmp->line);
80         free(tmp);
81     }
82 }
83
84 void list_deinit(list *in)
85 {
86     while (in->first) {
87         list_remove(in);
88     }
89     free(in);
90 }
91
92 static const struct lws_http_mount mount = {
93     .mount_next =            NULL,         /* linked-list "next" */
94     .mountpoint =            HTTP_MOUNTPOINT,
95     .origin =                HTTP_ORIGIN,  /* serve from dir */
96     .def =                   HTTP_DEFAULT, /* default filename */
97     .protocol =              NULL,
98     .cgienv =                NULL,
99     .extra_mimetypes =       NULL,
100     .interpret =             NULL,
101     .cgi_timeout =           0,
102     .cache_max_age =         0,
103     .auth_mask =             0,
104     .cache_reusable =        0,
105     .cache_revalidate =      0,
106     .cache_intermediaries =  0,
107     .origin_protocol =       LWSMPRO_FILE, /* files in a dir */
108     .mountpoint_len =        1,            /* char count */
109     .basic_auth_login_file = NULL,
110 };
111
112 typedef struct per_vhost_data__merica_terminal {
113     struct lws_context *context;
114     struct lws_vhost *vhost;
115     const struct lws_protocols *protocol;
116 } per_vhost_data__merica_terminal;
117
118 /*
119 typedef struct per_session_data__merica_terminal {
120     int number;
121 } per_session_data__merica_terminal;
122 */
123
124 enum protocols {
125     PROTOCOL_HTTP = 0, // always first
126     PROTOCOL_MERICA_TERMINAL
127 };
128
129 static int callback_merica_terminal(struct lws *wsi,
130                                     enum lws_callback_reasons reason,
131                                     void *user, void *in, size_t len);
132
133 // list of supported protocols and callbacks
134 static struct lws_protocols protocols[] = {
135     // first protocol must always be HTTP handler
136     {"http", lws_callback_http_dummy, 0, 0},
137     {
138         "merica-terminal-protocol",
139         callback_merica_terminal,
140         0, //sizeof(struct per_session_data__merica_terminal),
141         MT_PROTOCOL_RX_BUFFER_SIZE
142     },
143     {NULL, NULL, 0, 0} // terminator
144 };
145
146 static int callback_merica_terminal(struct lws *wsi,
147                                     enum lws_callback_reasons reason,
148                                     void *user, void *in, size_t len)
149 {
150     struct lws_context *context = lws_get_context(wsi);
151     struct lws_vhost *vhost = lws_get_vhost(wsi);
152     const struct lws_protocols *prot = lws_get_protocol(wsi);
153     /*
154     per_session_data__merica_terminal *pss =
155         (per_session_data__merica_terminal *)user;
156     */
157     per_vhost_data__merica_terminal *vhd =
158         (per_vhost_data__merica_terminal *)
159         lws_protocol_vh_priv_get(vhost, prot);
160
161     int n, m;
162
163     list *lines = (list *)lws_context_user(context);
164     char *line;
165
166     switch (reason) {
167         case LWS_CALLBACK_PROTOCOL_INIT:
168             vhd = lws_protocol_vh_priv_zalloc(
169                       vhost, prot,
170                       sizeof(struct per_vhost_data__merica_terminal)
171                   );
172             vhd->context = context;
173             vhd->protocol = prot;
174             vhd->vhost = vhost;
175             break;
176
177         case LWS_CALLBACK_PROTOCOL_DESTROY:
178             if (!vhd) {
179                 break;
180             }
181             break;
182
183         case LWS_CALLBACK_ESTABLISHED:
184             break;
185
186         case LWS_CALLBACK_SERVER_WRITEABLE:
187             if (lines->first) {
188                 line = lines->first->line;
189                 n = strlen(line);
190                 m = lws_write(wsi, (unsigned char *)line, n, LWS_WRITE_TEXT);
191                 list_remove(lines);
192                 if (lines->first) {
193                     lws_callback_on_writable_all_protocol(context,
194                         &protocols[PROTOCOL_MERICA_TERMINAL]);
195                 }
196             } else {
197                 line = copy_line(JSON_EMPTY);
198                 if (line) {
199                     n = strlen(line);
200                     m = lws_write(wsi, (unsigned char *)line, n, LWS_WRITE_TEXT);
201                     free_line(line);
202                 } else {
203                     return -1;
204                 }
205             }
206             if (m < n) {
207                 fprintf(stderr, "ERROR %d writing to di socket\n", n);
208                 return -1;
209             }
210             break;
211
212         case LWS_CALLBACK_RECEIVE:
213             if (strcmp((const char *)in, "reset") == 0) {
214                 line = copy_line(JSON_EMPTY);
215                 if (line) {
216                     if (list_add(lines, line) == 0) {
217                         lws_callback_on_writable_all_protocol(context,
218                             &protocols[PROTOCOL_MERICA_TERMINAL]);
219                     } else {
220                         free_line(line);
221                     }
222                 }
223             } else if (strcmp((const char *)in, "close") == 0) {
224                 fprintf(stderr, "closing websocket\n");
225                 lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
226                                  (unsigned char *)"seeya", 5);
227                 return -1;
228             }
229             break;
230
231         default:
232             break;
233     }
234
235     return 0;
236 }
237
238 static void fd_cb(EV_P_ ev_io *w_, int revents)
239 {
240     ev_io_ws *w = (ev_io_ws *)w_;
241
242     char *pos = w->pos++;
243
244     read(w->w.fd, pos, 1);
245
246     if (*pos == '\n' || (w->pos - w->text) == INPUT_LINE_LENGTH) {
247 #ifdef NO_MAIN
248         mt_blank_wake();
249 #endif
250         *pos = 0;
251         syslog(LOG_INFO, w->text);
252         char *line = new_line();
253         if (line) {
254             if (list_add(w->lines, w->text) == 0) {
255                 w->text = line;
256                 w->pos = w->text;
257                 lws_callback_on_writable_all_protocol(w->context,
258                     &protocols[PROTOCOL_MERICA_TERMINAL]);
259                 return;
260             }
261             free_line(line);
262         }
263         fprintf(stderr, "cannot malloc new line\n");
264     }
265 }
266
267 int mt_server_init(mt_server_t *self, struct ev_loop *loop, int fd)
268 {
269     struct lws_context_creation_info info;
270
271     list *l = list_init();
272     if (!l) {
273         return -1;
274     }
275
276     openlog(program_invocation_short_name, LOG_PID | LOG_PERROR, LOG_DAEMON);
277
278     memset(&info, 0, sizeof(info));
279     info.port = HTTP_PORT;
280     info.mounts = &mount;
281     info.protocols = protocols;
282     info.max_http_header_pool = 1;
283     info.options |= LWS_SERVER_OPTION_LIBEV;
284     info.user = l;
285
286     struct lws_context *context = lws_create_context(&info);
287     if (!context) {
288         fprintf(stderr, "lws_create_context failed\n");
289         list_deinit(l);
290         return -1;
291     }
292
293     self->context = context;
294
295     ev_io_ws *w = &(self->fd_watcher);
296     w->context = context;
297     w->lines = l;
298
299     char *line = new_line();
300     if (!line) {
301         list_deinit(l);
302         return -1;
303     }
304     w->text = line;
305     w->pos = w->text;
306     ev_io_init(&(w->w), fd_cb, fd, EV_READ);
307     ev_io_start(loop, (ev_io *)w);
308
309     return lws_ev_initloop(context, loop, 0);
310 }
311
312 void mt_server_deinit(mt_server_t *self)
313 {
314     free_line(self->fd_watcher.text);
315     list_deinit(self->fd_watcher.lines);
316     lws_context_destroy(self->context);
317     closelog();
318 }
319
320 #ifndef NO_MAIN
321 int main(int argc, const char **argv)
322 {
323     struct ev_loop *loop = EV_DEFAULT;
324     mt_server_t server;
325
326     set_signal_exit(loop);
327
328     if (mt_server_init(&server, loop, STDIN_FILENO) != 0) {
329         return -1;
330     }
331
332     ev_run(loop, 0);
333
334     mt_server_deinit(&server);
335     ev_loop_destroy(loop);
336
337     return 0;
338 }
339 #endif