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