source: neercs/trunk/src/server.c @ 3438

Last change on this file since 3438 was 3438, checked in by Pascal Terjan, 12 years ago
  • Avoid a memcpy and allocating 128K on the stack
  • Property svn:eol-style set to native
File size: 15.5 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
4 *                2008 Jean-Yves Lamoureux <jylam@lnxscene.org>
5 *                2008 Pascal Terjan <pterjan@linuxfr.org>
6 *                All Rights Reserved
7 *
8 *  $Id$
9 *
10 *  This program is free software. It comes without any warranty, to
11 *  the extent permitted by applicable law. You can redistribute it
12 *  and/or modify it under the terms of the Do What The Fuck You Want
13 *  To Public License, Version 2, as published by Sam Hocevar. See
14 *  http://sam.zoy.org/wtfpl/COPYING for more details.
15 */
16
17#include "config.h"
18
19#include <stdio.h>
20#include <string.h>
21#include <stdlib.h>
22#include <unistd.h>
23#include <fcntl.h>
24#include <signal.h>
25#include <sys/ioctl.h>
26#include <sys/socket.h>
27#include <sys/types.h>
28#include <sys/wait.h>
29#include <sys/time.h>
30#include <time.h>
31#include <pwd.h>
32
33#include <errno.h>
34#include <caca.h>
35
36#include "neercs.h"
37
38static int send_to_client(const char * msg, struct screen_list* screen_list)
39{
40    int ret;
41    if(!screen_list->socket[SOCK_CLIENT])
42        connect_socket(screen_list, SOCK_CLIENT);
43    debug("Sending message (%.8s,%d) to client on socket %d", msg, strlen(msg), screen_list->socket[SOCK_CLIENT]);
44    if(!screen_list->socket[SOCK_CLIENT])
45        ret = -1;
46    else
47        ret = write(screen_list->socket[SOCK_CLIENT], msg, strlen(msg));
48    if(ret < 0 && errno != EAGAIN)
49    {
50        fprintf(stderr, "Failed to send message to client: %s\n", strerror(errno));
51        if(screen_list->attached)
52            detach(screen_list);
53    }
54    return ret;
55}
56
57static int set_title(struct screen_list* screen_list)
58{
59    char buf[1024];
60    int bytes;
61    char *title;
62
63    if(screen_list->attached)
64    {
65        if(screen_list->pty < screen_list->count &&
66           screen_list->screen[screen_list->pty]->title)
67            title = screen_list->screen[screen_list->pty]->title;
68        else
69            title = PACKAGE_STRING;
70    }
71
72    if(screen_list->title)
73    {
74        if(!strcmp(screen_list->title, title))
75            return 0;
76        free(screen_list->title);
77    }
78
79    screen_list->title = strdup(title);
80
81    bytes = snprintf(buf, sizeof(buf)-1, "TITLE %s", title);
82    buf[bytes] = '\0';
83    return send_to_client(buf, screen_list);
84}
85
86static int set_cursor(int state, struct screen_list* screen_list)
87{
88    char buf[16];
89    int bytes;
90
91    bytes = snprintf(buf, sizeof(buf)-1, "CURSOR %d", state);
92    buf[bytes] = '\0';
93
94    return send_to_client(buf, screen_list);
95}
96
97static int request_refresh(struct screen_list* screen_list)
98{
99    size_t bytes;
100    void *buf;
101    char *buf2;
102    buf = caca_export_memory (screen_list->cv, "caca", &bytes);
103    buf2 = malloc(bytes+8);
104    memcpy(buf2, "REFRESH ", 8);
105    memcpy(buf2+8, buf, bytes);
106    if(!screen_list->socket[SOCK_CLIENT])
107        connect_socket(screen_list, SOCK_CLIENT);
108    if(screen_list->socket[SOCK_CLIENT])
109    {
110        size_t bufsize, towrite = bytes+8;
111        ssize_t written;
112        socklen_t optlen = sizeof(bufsize);
113        debug("Requesting refresh for %d", bytes+8);
114        getsockopt(screen_list->socket[SOCK_CLIENT], SOL_SOCKET, SO_SNDBUF,
115                   &bufsize, &optlen);
116        bufsize /= 2;
117        debug("bufsize=%d", bufsize);
118        written = write(screen_list->socket[SOCK_CLIENT],
119                        buf2,
120                        towrite > bufsize ? bufsize : towrite);
121        if(written <= 0 && errno != EAGAIN)
122        {
123            debug("Can't refresh (%s), with %d bytes (out of %d)", strerror(errno), towrite > bufsize ? bufsize : towrite, towrite);
124            return -1;
125        }
126        debug("Wrote %d bytes", written);
127        towrite -= written;
128        while(written > 0 && towrite > 0)
129        {
130            ssize_t n;
131            debug("Wrote %d, %d remaining", written, towrite);
132            /* Block to read the end of the message */
133            fcntl(screen_list->socket[SOCK_CLIENT], F_SETFL, 0);
134            n = write(screen_list->socket[SOCK_CLIENT],
135                      buf2 + written,
136                      towrite > bufsize ? bufsize : towrite);
137            if(n < 0)
138            {
139                debug("Can't refresh (%s), with %d bytes", strerror(errno), bytes+8);
140                return -1;
141            }
142            written += n;
143            towrite -= n;
144        }
145        fcntl(screen_list->socket[SOCK_CLIENT], F_SETFL, O_NONBLOCK);
146    }
147    free(buf);
148    free(buf2);
149    return 0;
150}
151
152int detach(struct screen_list* screen_list)
153{
154    screen_list->attached = 0;
155    if(screen_list->socket[SOCK_CLIENT])
156    {
157        send_to_client("DETACH", screen_list);
158        close(screen_list->socket[SOCK_CLIENT]);
159        screen_list->socket[SOCK_CLIENT] = 0;
160    }
161    return 0;
162}
163
164static int server_main(struct screen_list *screen_list)
165{
166    int i;
167    int eof = 0, refresh = 1, command = 0, was_in_bell = 0;
168    long long unsigned int last_key_time = 0;
169    int mainret = 0;
170
171    screen_list->attached = 0;
172
173    /* Create socket and bind it */
174    create_socket(screen_list, SOCK_SERVER);
175
176    /* Connect to the client */
177    connect_socket(screen_list, SOCK_CLIENT);
178
179    screen_list->width = screen_list->height = 80;
180
181    /* Create main canvas */
182    screen_list->cv = caca_create_canvas(screen_list->width,
183                                          screen_list->height
184                                          + screen_list->mini*6
185                                          + screen_list->status);
186
187    if(!screen_list->to_grab && !screen_list->to_start)
188    {
189        add_screen(screen_list,
190                   create_screen(screen_list->width,
191                                 screen_list->height,
192                                 screen_list->default_shell));
193    }
194
195    /* Attach processes */
196    if(screen_list->to_grab)
197    {
198        for(i=0; screen_list->to_grab[i]; i++)
199        {
200            add_screen(screen_list,
201                       create_screen_grab(screen_list->width,
202                                          screen_list->height,
203                                          screen_list->to_grab[i]));
204        }
205        free(screen_list->to_grab);
206        screen_list->to_grab = NULL;
207    }
208
209    /* Launch command line processes */
210    if(screen_list->to_start)
211    {
212        for(i=0; screen_list->to_start[i]; i++)
213        {
214            add_screen(screen_list,
215                       create_screen(screen_list->width,
216                                     screen_list->height,
217                                     screen_list->to_start[i]));
218            free(screen_list->to_start[i]);
219        }
220        free(screen_list->to_start);
221        screen_list->to_start = NULL;
222    }
223
224    last_key_time = get_us();
225
226    for(;;)
227    {
228        int quit = 0;
229        ssize_t n;
230        char buf[4097];
231
232        /* Read program output */
233        refresh = update_screens_contents(screen_list);
234
235        /* Check if we got something from the client */
236        while (screen_list->socket[SOCK_SERVER] && (n = read(screen_list->socket[SOCK_SERVER], buf, sizeof(buf)-1)) > 0)
237        {
238            buf[n] = 0;
239            debug("Received command %s", buf);
240            if(!strncmp("ATTACH ", buf, 7))
241            {
242                /* If we were attached to someone else, detach first */
243                if(screen_list->attached)
244                    detach(screen_list);
245                screen_list->attached = 1;
246                caca_free_canvas(screen_list->cv);
247                screen_list->cv = caca_create_canvas(atoi(buf+7), atoi(buf+18));
248                screen_list->width  = caca_get_canvas_width(screen_list->cv);
249                screen_list->height = caca_get_canvas_height(screen_list->cv) - ((screen_list->mini*6) + (screen_list->status));
250                update_windows_props(screen_list);
251                caca_clear_canvas(screen_list->cv);
252                refresh = 1;
253            }
254            else if(!strncmp("QUIT", buf, 4))
255            {
256                quit = 1;
257            }
258            else if(!strncmp("RESIZE ", buf, 7))
259            {
260                caca_free_canvas(screen_list->cv);
261                screen_list->cv = caca_create_canvas(atoi(buf+7), atoi(buf+18));
262                screen_list->width  = caca_get_canvas_width(screen_list->cv);
263                screen_list->height = caca_get_canvas_height(screen_list->cv) - ((screen_list->mini*6) + (screen_list->status));
264                update_windows_props(screen_list);
265                caca_clear_canvas(screen_list->cv);
266                refresh = 1;
267            }
268            else if(!strncmp("KEY ", buf, 4))
269            {
270                unsigned int c = atoi(buf+4);
271                char *str = NULL;
272                int size = 0;
273                /* CTRL-A has been pressed before, handle this as a
274                 * command, except that CTRL-A a sends literal CTRL-A */
275                if(command && (c != 'a'))
276                {
277                    command = 0;
278                    refresh |= handle_command_input(screen_list, c);
279                }
280                else
281                {
282                    /* Not in command mode */
283                    last_key_time = get_us();
284                    set_cursor(1, screen_list);
285
286                    /* Kill screensaver */
287                    if(screen_list->in_screensaver)
288                    {
289                        screensaver_kill(screen_list);
290                        screen_list->in_screensaver = 0;
291                        refresh = 1;
292                        continue;
293                    }
294                    /* Handle lock window */
295                    if(screen_list->locked)
296                    {
297                        refresh |= update_lock(c, screen_list);
298                    }
299                    else if(screen_list->window_list) {
300                        refresh |= update_window_list(c, screen_list);
301                    }
302                    else
303                    {
304                        switch(c)
305                        {
306                        case 0x01: //CACA_KEY_CTRL_A:
307                            command = 1; break;
308                        case CACA_KEY_ESCAPE:
309                            if(screen_list->help)
310                            {
311                                screen_list->help = 0;
312                                refresh = 1;
313                                break;
314                            }
315                        default:
316                            /* CTRL-A a sends literal CTRL-A */
317                            if (command && (c == 'a'))
318                            {
319                                c = 0x01;
320                            }
321                            /* Normal key, convert it if needed */
322                            str = convert_input_ansi(&c, &size);
323                            write(screen_list->screen[screen_list->pty]->fd, str, size);
324                            break;
325                        }
326                    }
327                }
328            }
329            else
330            {
331                fprintf(stderr, "Unknown command received: %s\n", buf);
332            }
333        }
334
335        /* No more screens, exit */
336        if(!screen_list->count) break;
337
338        /* User requested to exit */
339        if(quit) break;
340
341        /* Update each screen canvas  */
342        refresh |= update_terms(screen_list);
343
344        /* Launch reccurents if any */
345        refresh |= handle_recurrents(screen_list);
346
347        /* Resfresh screen */
348        if(!screen_list->attached)
349        {
350            /* No need to refresh
351             * Don't use the CPU too much
352             * Would be better to select on terms + socket
353             */
354            sleep(1);
355            refresh = 0;
356        }
357        /* Draw lock window */
358        else if(screen_list->locked)
359        {
360            draw_lock(screen_list);
361            refresh = 1;
362        }
363        else
364        {
365            if((refresh || screen_list->in_bell || was_in_bell) &&
366               (get_us() - last_key_time < screen_list->screensaver_timeout))
367            {
368                was_in_bell = screen_list->in_bell;
369                refresh_screens(screen_list);
370                set_title(screen_list);
371                refresh = 1;
372
373            }
374            if((get_us() - last_key_time > screen_list->screensaver_timeout))
375            {
376                if(!screen_list->in_screensaver)
377                {
378                    screensaver_init(screen_list);
379                    screen_list->in_screensaver = 1;
380                    set_cursor(0, screen_list);
381                }
382                draw_screensaver(screen_list);
383                refresh = 1;
384            }
385
386            if((get_us() - last_key_time > screen_list->autolock_timeout))
387            {
388                screen_list->locked = 1;
389                refresh = 1;
390            }
391
392        }
393
394        if(refresh)
395        {
396            if(screen_list->attached)
397                request_refresh(screen_list);
398            refresh = 0;
399        }
400
401        eof = 1;
402        for(i=0; i < screen_list->count; i++)
403            if(screen_list->screen[i]->fd >= 0)
404                eof = 0;
405        if(eof)
406            break;
407    }
408
409    detach(screen_list);
410
411    /* Clean up */
412    caca_free_canvas(screen_list->cv);
413
414    for(i = 0; i < screen_list->count; i++)
415    {
416        destroy_screen(screen_list->screen[i]);
417    }
418
419    if(screen_list->socket_path[SOCK_SERVER])
420    {
421        unlink(screen_list->socket_path[SOCK_SERVER]);
422        free(screen_list->socket_path[SOCK_SERVER]);
423    }
424
425    if(screen_list->socket_path[SOCK_CLIENT])
426        free(screen_list->socket_path[SOCK_CLIENT]);
427
428    if(screen_list->socket[SOCK_SERVER])
429        close(screen_list->socket[SOCK_SERVER]);
430
431    if(screen_list->socket[SOCK_CLIENT])
432        close(screen_list->socket[SOCK_CLIENT]);
433
434    if(screen_list->screen) free(screen_list->screen);
435
436    for(i=0; i<screen_list->recurrent_list->count; i++)
437    {
438        remove_recurrent(screen_list->recurrent_list, i);
439        i = 0;
440    }
441
442    if(screen_list->recurrent_list->recurrent) free(screen_list->recurrent_list->recurrent);
443    if(screen_list->recurrent_list)            free(screen_list->recurrent_list);
444
445    if(screen_list->session_name) {
446        free(screen_list->session_name);
447        screen_list->session_name = NULL;
448    }
449
450    if(screen_list->title)
451        free(screen_list->title);
452
453    if(screen_list) {
454        free(screen_list);
455        screen_list = NULL;
456    }
457
458    return mainret;
459}
460
461int start_server(struct screen_list *screen_list)
462{
463    pid_t pid;
464    char * sess;
465
466    pid = fork();
467    if(pid < 0)
468    {
469        perror("Failed to create child process");
470        return -1;
471    }
472    if(pid == 0)
473    {
474        int fd;
475        close(0);
476        close(1);
477        close(2);
478        fd = open("/dev/null", O_RDWR, 0);
479        if(fd < 0)
480        {
481            perror("Failed to open /dev/null");
482            return -2;
483        }
484        dup2(fd, 0);
485#ifndef DEBUG
486        dup2(fd, 1);
487        dup2(fd, 2);
488        if (fd > 2)
489            close(fd);
490#else
491        if(fd != 0)
492            close(fd);
493        fd = open("/tmp/log.txt", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
494        dup2(fd, 1);
495        dup2(fd, 2);
496        if (fd > 2)
497            close(fd);
498#endif
499        setsid();
500        return server_main(screen_list);
501    }
502    create_socket(screen_list, SOCK_CLIENT);
503    while((sess = connect_socket(screen_list, SOCK_SERVER)) == NULL)
504        usleep(100);
505    free(sess);
506
507    /* Create main canvas and associated caca window */
508    screen_list->cv = caca_create_canvas(0, 0);
509    screen_list->dp = caca_create_display(screen_list->cv);
510    if(!screen_list->dp)
511        return -3;
512    caca_set_cursor(screen_list->dp, 1);
513
514    request_attach(screen_list);
515
516    return 0;
517}
518
519long long get_us(void)
520{
521    struct timeval tv;
522    gettimeofday(&tv, NULL);
523    return (tv.tv_sec*(1000000) + tv.tv_usec);
524}
Note: See TracBrowser for help on using the repository browser.