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