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

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