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