]> rtime.felk.cvut.cz Git - coffee/mt-apps.git/blob - mt_server.c
queue improved
[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                     }
202                 }
203             } else if (strcmp((const char *)in, "close") == 0) {
204                 fprintf(stderr, "closing websocket\n");
205                 lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
206                                  (unsigned char *)"seeya", 5);
207                 return -1;
208             }
209             break;
210
211         default:
212             break;
213     }
214
215     return 0;
216 }
217
218 static void fd_cb(EV_P_ ev_io *w_, int revents)
219 {
220     ev_io_ws *w = (ev_io_ws *)w_;
221
222     char *pos = w->pos++;
223
224     read(w->w.fd, pos, 1);
225
226     if (*pos == '\n' || (w->pos - w->text) == INPUT_LINE_LENGTH) {
227         *pos = 0;
228         if (list_add(w->lines, w->text) == 0) {
229             char *line = new_line();
230             if (line) {
231                 w->text = line;
232                 w->pos = w->text;
233                 lws_callback_on_writable_all_protocol(w->context,
234                     &protocols[PROTOCOL_MERICA_TERMINAL]);
235                 return;
236             }
237         }
238         fprintf(stderr, "cannot malloc new line\n");
239     }
240 }
241
242 int mt_server_init(mt_server_t *self, struct ev_loop *loop, int fd)
243 {
244     struct lws_context_creation_info info;
245
246     memset(&info, 0, sizeof(info));
247     info.port = HTTP_PORT;
248     info.mounts = &mount;
249     info.protocols = protocols;
250     info.max_http_header_pool = 1;
251     info.options |= LWS_SERVER_OPTION_LIBEV;
252
253     struct lws_context *context = lws_create_context(&info);
254     if (!context) {
255         fprintf(stderr, "lws_create_context failed\n");
256         return -1;
257     }
258
259     self->context = context;
260
261     ev_io_ws *w = &(self->fd_watcher);
262     w->context = context;
263     char *line = copy_line(JSON_EMPTY);
264     if (!line) {
265         return -1;
266     }
267     w->lines = list_init();
268     if (!w->lines) {
269         free_line(line);
270         return -1;
271     }
272     if (list_add(w->lines, line) != 0) {
273         list_deinit(w->lines);
274         return -1;
275     }
276     protocols[PROTOCOL_MERICA_TERMINAL].user = (void *)w->lines;
277     line = new_line();
278     if (!line) {
279         list_deinit(w->lines);
280         return -1;
281     }
282     w->text = line;
283     w->pos = w->text;
284     ev_io_init(&(w->w), fd_cb, fd, EV_READ);
285     ev_io_start(loop, (ev_io *)w);
286
287     return lws_ev_initloop(context, loop, 0);
288 }
289
290 void mt_server_deinit(mt_server_t *self)
291 {
292     list_deinit((list *)protocols[PROTOCOL_MERICA_TERMINAL].user);
293     lws_context_destroy(self->context);
294 }
295
296 #ifndef NO_MAIN
297 int main(int argc, const char **argv)
298 {
299     struct ev_loop *loop = EV_DEFAULT;
300     mt_server_t server;
301
302     set_signal_exit(loop);
303
304     if (mt_server_init(&server, loop, STDIN_FILENO) != 0) {
305         return -1;
306     }
307
308     ev_run(loop, 0);
309
310     mt_server_deinit(&server);
311
312     return 0;
313 }
314 #endif