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