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

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