source: neercs/trunk/src/main.c @ 2614

Last change on this file since 2614 was 2614, checked in by Pascal Terjan, 13 years ago
  • Factorize some code in attach.c
  • Property svn:keywords set to Id
File size: 16.0 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: main.c 2614 2008-07-30 23:34:55Z pterjan $
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
33#if !defined HAVE_GETOPT_LONG
34#   include "mygetopt.h"
35#elif defined HAVE_GETOPT_H
36#   include <getopt.h>
37#endif
38#if defined HAVE_GETOPT_LONG
39#   define mygetopt getopt_long
40#   define myoptind optind
41#   define myoptarg optarg
42#   define myoption option
43#endif
44#include <errno.h>
45#include <cucul.h>
46#include <caca.h>
47
48#include "neercs.h"
49
50
51void version(void)
52{
53    printf("%s\n", PACKAGE_STRING);
54    printf("Copyright (C) 2006, 2008 Sam Hocevar <sam@zoy.org>\n");
55    printf("                         Jean-Yves Lamoureux <jylam@lnxscene.org>\n\n");
56    printf("This is free software.  You may redistribute copies of it under the\n");
57    printf("terms of the Do What The Fuck You Want To Public License, Version 2\n");
58    printf("<http://sam.zoy.org/wtfpl/>.\n");
59    printf("There is NO WARRANTY, to the extent permitted by law.\n");
60    printf("\n");
61    printf("For more informations, visit http://libcaca.zoy.org/wiki/neercs\n");
62}
63
64void usage(int argc, char **argv)
65{
66    printf("%s\n", PACKAGE_STRING);
67    printf("Usage : %s [command1] [command2] ... [commandN]\n", argv[0]);
68    printf("Example : %s zsh top \n\n", argv[0]);
69    printf("Options :\n");
70    printf("\t--config\t-c <file>\t\tuse given config file\n");
71    printf("\t--pid\t\t-P <pid>\t\tgrab <pid>\n");
72    printf("\t\t\t-r [session]\t\treattach to a detached neercs\n");
73    printf("\t\t\t-R [session]\t\treattach if possible, otherwise start a new session\n");
74    printf("\t\t\t-S <name>\t\tname this session <name> instead of <pid>\n");
75    printf("\t--version\t-v \t\t\tdisplay version and exit\n");
76    printf("\t--help\t\t-h \t\t\tthis help\n");
77}
78
79int main(int argc, char **argv)
80{
81    struct screen_list *screen_list = NULL;
82    struct passwd *user_info;
83    char *user_path = NULL, *user_dir = NULL;
84    int i, args, s=0;
85    long long unsigned int last_key_time = 0;
86    int mainret = -1;
87    int attach = 0, forceattach = 0;
88    int *to_grab = NULL;
89    char **to_start = NULL;
90    int nb_to_grab = 0;
91
92    screen_list = create_screen_list();
93    screen_list->default_shell = getenv("SHELL");
94
95    args = argc -1;
96    if(screen_list->default_shell == NULL  && args <= 0)
97    {
98        fprintf(stderr, "Environment variable SHELL not set and no arguments given. kthxbye.\n");
99        goto end;
100    }
101
102    if(args==0)
103        args = 1;
104
105    /* Build local config file path */
106    user_dir = getenv("HOME");
107    if(!user_dir)
108    {
109        user_info = getpwuid(getuid());
110        if(user_info)
111        {
112            user_dir = user_info->pw_dir;
113        }
114    }
115    if(user_dir)
116    {
117        user_path = malloc(strlen(user_dir) + strlen("/.neercsrc") + 1);
118        sprintf(user_path, "%s/%s", user_dir, ".neercsrc");
119    }
120
121
122    screen_list->recurrent_list = (struct recurrent_list*) malloc(sizeof(struct recurrent_list));
123    screen_list->recurrent_list->recurrent = (struct recurrent**) malloc(sizeof(struct recurrent*));
124    if(!screen_list->recurrent_list->recurrent)
125    {
126        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
127        goto end;
128    }
129    screen_list->recurrent_list->count = 0;
130
131    for(;;)
132    {
133        int option_index = 0;
134        int pidopt;
135        static struct myoption long_options[] =
136            {
137                { "config",      1, NULL, 'c' },
138#if defined USE_GRAB
139                { "pid",         1, NULL, 'P' },
140#endif
141                { "help",        0, NULL, 'h' },
142                { "version",     0, NULL, 'v' },
143            };
144#if defined USE_GRAB
145        int c = mygetopt(argc, argv, "c:S:R::r::P:hv", long_options, &option_index);
146#else
147        int c = mygetopt(argc, argv, "c:S:R::r::hv", long_options, &option_index);
148#endif
149        if(c == -1)
150            break;
151
152        switch(c)
153        {
154        case 'c': /* --config */
155            if(user_path)
156                free(user_path);
157            user_path = strdup(myoptarg);
158            s+=2;
159            break;
160        case 'S':
161            if(!screen_list->session_name)
162                screen_list->session_name = strdup(myoptarg);
163            s+=2;
164            break;
165        case 'P': /* --pid */
166            pidopt = atoi(myoptarg);
167            if(pidopt <= 0)
168            {
169                fprintf(stderr, "Invalid pid %d\n", pidopt);
170                if(to_grab)
171                    free(to_grab);
172                return -1;
173            }
174            if(!to_grab)
175            {
176                /* At most argc-1-s times -P <pid> + final 0 */
177                to_grab = (int *)malloc(((argc-1-s)/2+1)*sizeof(int));
178                if(!to_grab)
179                {
180                    fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
181                    goto end;
182                }
183            }
184            to_grab[nb_to_grab++] = pidopt;
185            to_grab[nb_to_grab] = 0;
186            s+=2;
187            break;
188        case 'r':
189            forceattach = 1;
190        case 'R':
191            if(attach)
192            {
193                fprintf(stderr, "Attaching can only be requested once\n");
194                goto end;
195            }
196            if(myoptarg)
197            {
198                if(screen_list->session_name)
199                    free(screen_list->session_name);
200                screen_list->session_name = strdup(myoptarg);
201                s+=1;
202            }
203            attach = 1;
204            s+=1;
205            break;
206        case 'h': /* --help */
207            usage(argc, argv);
208            mainret = 0;
209            goto end;
210            break;
211        case 'v': /* --version */
212            version();
213            mainret = 0;
214            goto end;
215            break;
216        default:
217            fprintf(stderr, "Unknown argument #%d\n", myoptind);
218            goto end;
219            break;
220        }
221    }
222
223    if(s < argc - 1)
224    {
225        to_start = (char**)malloc((argc-s)*sizeof(char*));
226        if(!to_start)
227        {
228            fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
229            goto end;
230        }
231        for(i=0; i<(argc-1) - s; i++)
232        {
233            to_start[i] = strdup(argv[i+s+1]);
234        }
235        to_start[argc-1-s] = NULL;
236    }
237
238    /* Read global configuration first */
239    read_configuration_file("/etc/neercsrc", screen_list);
240
241    /* Then local one  */
242    if(user_path)
243    {
244        read_configuration_file(user_path, screen_list);
245        free(user_path);
246    }
247
248    if(attach)
249    {
250        char **sockets;
251        if(nb_to_grab || (argc-1 > s))
252        {
253            fprintf(stderr, "-R can not be associated with commands or pids!\n");
254            goto end;
255        }
256
257        sockets = list_sockets(screen_list->socket_dir, screen_list->session_name);
258        if(sockets && sockets[0])
259        {
260            char *session;
261            for(i=0; sockets[i]; i++);
262            i--;
263            screen_list->socket_path[SOCK_SERVER] = strdup(sockets[i]);
264            session = connect_socket(screen_list, SOCK_SERVER);
265            while(!session && i > 0)
266            {
267                free(screen_list->socket_path[SOCK_SERVER]);
268                i--;
269                screen_list->socket_path[SOCK_SERVER] = strdup(sockets[i]);
270                session = connect_socket(screen_list, SOCK_SERVER);
271            }
272            debug("Connected to session %s", session);
273            if(session)
274            {
275                /* Create main canvas and associated caca window */
276                screen_list->cv = cucul_create_canvas(0, 0);
277                screen_list->dp = caca_create_display(screen_list->cv);
278                if(!screen_list->dp)
279                    goto end;
280                caca_set_cursor(screen_list->dp, 1);
281
282                screen_list->socket_path[SOCK_CLIENT] =
283                    build_socket_path(screen_list->socket_dir, session, SOCK_CLIENT);
284                create_socket(screen_list, SOCK_CLIENT);
285                request_attach(screen_list);
286                if(screen_list->session_name)
287                    free(screen_list->session_name);
288                screen_list->session_name = session;
289            }
290            else
291            {
292                fprintf(stderr, "Failed to attach!\n");
293                free(screen_list->socket_path[SOCK_SERVER]);
294                screen_list->socket_path[SOCK_SERVER] = NULL;
295                attach = 0;
296            }
297            for(i=0; sockets[i]; i++)
298                free(sockets[i]);
299            free(sockets);
300        }
301        else
302        {
303            fprintf(stderr, "No socket found!\n");
304            attach = 0;
305        }
306        if(forceattach && !attach)
307            goto end;
308    }
309
310    /* Build default session name */
311    if(!screen_list->session_name)
312    {
313        char mypid[32]; /* FIXME Compute the length of PID_MAX ? */
314        snprintf(mypid, 31, "%d", getpid());
315        mypid[31]= '\0';
316        screen_list->session_name = strdup(mypid);
317        if(!screen_list->session_name)
318        {
319            fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
320            goto end;
321        }
322    }
323    if(!screen_list->socket_path[SOCK_CLIENT])
324        screen_list->socket_path[SOCK_CLIENT] =
325            build_socket_path(screen_list->socket_dir, screen_list->session_name, SOCK_CLIENT);
326
327    if(!screen_list->socket_path[SOCK_SERVER])
328        screen_list->socket_path[SOCK_SERVER] =
329            build_socket_path(screen_list->socket_dir, screen_list->session_name, SOCK_SERVER);
330
331    /* Fork the server if needed */
332    if(!attach)
333    {
334        pid_t pid;
335        char * sess;
336
337        pid = fork();
338        if(pid < 0)
339        {
340            fprintf(stderr, "Failed to create child process\n");
341            goto end;
342        }
343        if(pid == 0)
344        {
345            int fd;
346            close(0);
347            close(1);
348            close(2);
349            fd = open("/dev/null", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
350            dup2(fd, 0);
351#ifndef DEBUG
352            dup2(fd, 1);
353            dup2(fd, 2);
354            if (fd > 2)
355                close(fd);
356#else
357            if(fd != 0)
358                close(fd);
359            fd = open("/tmp/log.txt", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
360            dup2(fd, 1);
361            dup2(fd, 2);
362            if (fd > 2)
363                close(fd);
364#endif
365            setsid();
366            return server_main(to_grab, to_start, screen_list);
367        }
368        create_socket(screen_list, SOCK_CLIENT);
369        while((sess = connect_socket(screen_list, SOCK_SERVER)) == NULL)
370            usleep(100);
371        free(sess);
372
373        /* Create main canvas and associated caca window */
374        screen_list->cv = cucul_create_canvas(0, 0);
375        screen_list->dp = caca_create_display(screen_list->cv);
376        if(!screen_list->dp)
377            goto end;
378        caca_set_cursor(screen_list->dp, 1);
379
380        request_attach(screen_list);
381    }
382
383    last_key_time = get_us();
384
385    for(;;)
386    {
387        caca_event_t ev;
388        int ret = 0;
389        ssize_t n;
390        char buf[128*1024];
391
392        if (screen_list->socket[SOCK_CLIENT] && (n = read(screen_list->socket[SOCK_CLIENT], buf, sizeof(buf)-1)) > 0)
393        {
394            buf[n] = 0;
395            debug("Received from server: %s", buf);
396            if(!strncmp("DETACH", buf, 6))
397            {
398                ret = 1;
399                break;
400            }
401            else if(!strncmp("REFRESH ", buf, 8))
402            {
403                cucul_import_memory(screen_list->cv, buf+8, n-8, "caca");
404
405                caca_refresh_display(screen_list->dp);
406            }
407            else if(!strncmp("CURSOR ", buf, 7))
408            {
409                caca_set_cursor(screen_list->dp, atoi(buf+7));
410            }
411            else if(!strncmp("TITLE ", buf, 6))
412            {
413                caca_set_display_title(screen_list->dp, buf+6);
414                caca_refresh_display(screen_list->dp);
415            }
416            else
417            {
418                debug("Unknown message received from server: %s", buf);
419            }
420        }
421        if(ret)
422            break;
423
424        ret = caca_get_event(screen_list->dp, CACA_EVENT_ANY, &ev, 100000);
425        if(ret)
426            ret = send_event(ev, screen_list);
427
428        if(ret)
429            break;
430    }
431
432    /* Clean up */
433    mainret = 0;
434end:
435    if(screen_list)
436    {
437        if(screen_list->dp)
438            caca_free_display(screen_list->dp);
439
440        if(screen_list->cv)
441            cucul_free_canvas(screen_list->cv);
442
443        for(i = 0; i < screen_list->count; i++)
444        {
445            destroy_screen(screen_list->screen[i]);
446        }
447
448        if(screen_list->socket_path[SOCK_SERVER])
449            free(screen_list->socket_path[SOCK_SERVER]);
450
451        if(screen_list->socket_path[SOCK_CLIENT])
452        {
453            unlink(screen_list->socket_path[SOCK_CLIENT]);
454            free(screen_list->socket_path[SOCK_CLIENT]);
455        }
456
457        if(screen_list->socket[SOCK_CLIENT])
458            close(screen_list->socket[SOCK_CLIENT]);
459
460        if(screen_list->socket[SOCK_SERVER])
461            close(screen_list->socket[SOCK_SERVER]);
462
463        if(screen_list->screen)
464            free(screen_list->screen);
465
466        struct option *option = screen_list->config;
467
468        while(option)
469        {
470            struct option *kromeugnon = option;
471            option = option->next;
472            if(kromeugnon->key)   free(kromeugnon->key);
473            if(kromeugnon->value) free(kromeugnon->value);
474            free(kromeugnon);
475        }
476
477        for(i=0; i<screen_list->recurrent_list->count; i++)
478        {
479            remove_recurrent(screen_list->recurrent_list, i);
480            i = 0;
481        }
482
483        if(screen_list->recurrent_list->recurrent)
484            free(screen_list->recurrent_list->recurrent);
485        if(screen_list->recurrent_list)
486            free(screen_list->recurrent_list);
487
488        if(screen_list->session_name)
489            free(screen_list->session_name);
490
491        free(screen_list);
492    }
493
494    return mainret;
495}
496
497
498struct screen_list *create_screen_list(void)
499{
500
501    struct screen_list *screen_list = NULL;
502
503    /* Create screen list */
504    screen_list = (struct screen_list*)     malloc(sizeof(struct screen_list));
505    if(!screen_list)
506    {
507        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
508        return NULL;
509    }
510    screen_list->screen = (struct screen**) malloc(sizeof(sizeof(struct screen*)));
511    if(!screen_list->screen)
512    {
513        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
514        free(screen_list);
515        return NULL;
516    }
517
518    screen_list->count = 0;
519    screen_list->mini = 1;
520    screen_list->help = 0;
521    screen_list->status = 1;
522    screen_list->wm_type = WM_VSPLIT;
523    screen_list->in_bell = 0;
524    screen_list->pty = screen_list->prevpty = 0;
525    screen_list->dont_update_coords = 0;
526    screen_list->screensaver_timeout = (60) * 1000000;
527    screen_list->screensaver_data = NULL;
528    screen_list->in_screensaver = 0;
529    screen_list->locked = 0;
530    screen_list->lock_offset = 0;
531    screen_list->attached = 1;
532    screen_list->socket[SOCK_SERVER] = 0;
533    screen_list->socket[SOCK_CLIENT] = 0;
534    screen_list->socket_dir    = NULL;
535    screen_list->socket_path[SOCK_SERVER] = NULL;
536    screen_list->socket_path[SOCK_CLIENT] = NULL;
537    screen_list->session_name  = NULL;
538    screen_list->default_shell = NULL;
539
540    screen_list->autolock_timeout = -1;
541
542
543    screen_list->recurrent_list = NULL;
544    screen_list->cv = NULL;
545    screen_list->dp = NULL;
546
547    memset(screen_list->lockmsg, 0, 1024);
548    memset(screen_list->lockpass, 0, 1024);
549
550    return screen_list;
551}
Note: See TracBrowser for help on using the repository browser.