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