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

Last change on this file since 3440 was 3440, checked in by Pascal Terjan, 13 years ago
  • Fix a huge leak I had just introduced
  • Property svn:keywords set to Id
File size: 15.3 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 3440 2009-05-12 11:09:34Z 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 <caca.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    int i, args;
83    long long unsigned int last_key_time = 0;
84    int mainret = -1;
85#define NEERCS_RECV_BUFSIZE 128*1024
86    char * buf = NULL;
87
88    screen_list = create_screen_list();
89    screen_list->default_shell = getenv("SHELL");
90
91    args = argc -1;
92    if(screen_list->default_shell == NULL  && args <= 0)
93    {
94        fprintf(stderr, "Environment variable SHELL not set and no arguments given. kthxbye.\n");
95        goto end;
96    }
97
98    if(handle_command_line(argc, argv, screen_list) < 0)
99        goto end;
100
101    /* Read global configuration first */
102    read_configuration_file("/etc/neercsrc", screen_list);
103
104    /* Then local one  */
105    if(screen_list->user_path)
106    {
107        read_configuration_file(screen_list->user_path, screen_list);
108        free(screen_list->user_path);
109    }
110
111    if(screen_list->attach)
112    {
113        if(screen_list->nb_to_grab || screen_list->to_start)
114        {
115            fprintf(stderr, "-R can not be associated with commands or pids!\n");
116            goto end;
117        }
118
119        attach(screen_list);
120
121        if(screen_list->forceattach && !screen_list->attach)
122            goto end;
123    }
124
125    /* Build default session name */
126    if(!screen_list->session_name)
127    {
128        char mypid[32]; /* FIXME Compute the length of PID_MAX ? */
129        snprintf(mypid, 31, "%d", getpid());
130        mypid[31]= '\0';
131        screen_list->session_name = strdup(mypid);
132        if(!screen_list->session_name)
133        {
134            fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
135            goto end;
136        }
137    }
138    if(!screen_list->socket_path[SOCK_CLIENT])
139        screen_list->socket_path[SOCK_CLIENT] =
140            build_socket_path(screen_list->socket_dir, screen_list->session_name, SOCK_CLIENT);
141
142    if(!screen_list->socket_path[SOCK_SERVER])
143        screen_list->socket_path[SOCK_SERVER] =
144            build_socket_path(screen_list->socket_dir, screen_list->session_name, SOCK_SERVER);
145
146    /* Fork the server if needed */
147    if(!screen_list->attach)
148    {
149        if(start_server(screen_list))
150            goto end;
151    }
152
153    last_key_time = get_us();
154
155    for(;;)
156    {
157        caca_event_t ev;
158        int ret = 0;
159        ssize_t n;
160        if(!screen_list) goto end;
161        if(!buf)
162            buf = malloc(NEERCS_RECV_BUFSIZE);
163        if(!buf)
164        {
165            debug("Failed to allocate memory");
166            goto end;
167        }
168        if(screen_list->socket[SOCK_CLIENT] && (n = read(screen_list->socket[SOCK_CLIENT], buf, NEERCS_RECV_BUFSIZE-1)) > 0)
169        {
170            buf[n] = 0;
171            debug("Received from server: %s", buf);
172            if(!strncmp("DETACH", buf, 6))
173            {
174                ret = 1;
175                break;
176            }
177            else if(!strncmp("REFRESH ", buf, 8))
178            {
179                ssize_t l2, lb = n-8;
180                char * buf2;
181                size_t l = caca_import_memory(screen_list->cv, buf+8, lb, "caca");
182                l2 = lb;
183                /* 0 means we have valid data but incomplete, so read the rest */
184                while(l == 0)
185                {
186                    buf2 = realloc(buf, l2+8 + NEERCS_RECV_BUFSIZE);
187                    if(!buf2)
188                    {
189                        debug("Failed to allocate memory");
190                        goto end;
191                    }
192                    buf = buf2;
193                    fcntl(screen_list->socket[SOCK_CLIENT], F_SETFL, 0);
194                    lb = read(screen_list->socket[SOCK_CLIENT], buf+l2+8, NEERCS_RECV_BUFSIZE-1);
195                    if(lb < 0)
196                    {
197                        debug("Failed to read the end of the refresh message (%s)", strerror(errno));
198                        l = -1;
199                    }
200                    else
201                    {
202                        debug("Got %d more bytes", lb);
203                        l2 += lb;
204                        l = caca_import_memory(screen_list->cv, buf+8, l2, "caca");
205                    }
206                }
207                fcntl(screen_list->socket[SOCK_CLIENT], F_SETFL, O_NONBLOCK);
208                caca_refresh_display(screen_list->dp);
209            }
210            else if(!strncmp("CURSOR ", buf, 7))
211            {
212                caca_set_cursor(screen_list->dp, atoi(buf+7));
213            }
214            else if(!strncmp("TITLE ", buf, 6))
215            {
216                caca_set_display_title(screen_list->dp, buf+6);
217                caca_refresh_display(screen_list->dp);
218            }
219            else
220            {
221                debug("Unknown message received from server: %s", buf);
222            }
223        }
224        if(ret)
225            break;
226
227        ret = caca_get_event(screen_list->dp,
228                             CACA_EVENT_KEY_PRESS
229                             |CACA_EVENT_RESIZE
230                             |CACA_EVENT_QUIT,
231                             &ev, 10000);
232        if(ret)
233            ret = send_event(ev, screen_list);
234
235        if(ret)
236            break;
237    }
238
239    /* Clean up */
240    mainret = 0;
241end:
242    if(buf)
243        free(buf);
244    if(screen_list)
245    {
246        if(screen_list->dp)
247            caca_free_display(screen_list->dp);
248
249        if(screen_list->cv)
250            caca_free_canvas(screen_list->cv);
251
252        for(i = 0; i < screen_list->count; i++)
253        {
254            destroy_screen(screen_list->screen[i]);
255        }
256
257        if(screen_list->socket_path[SOCK_SERVER])
258            free(screen_list->socket_path[SOCK_SERVER]);
259
260        if(screen_list->socket_path[SOCK_CLIENT])
261        {
262            unlink(screen_list->socket_path[SOCK_CLIENT]);
263            free(screen_list->socket_path[SOCK_CLIENT]);
264        }
265
266        if(screen_list->socket[SOCK_CLIENT])
267            close(screen_list->socket[SOCK_CLIENT]);
268
269        if(screen_list->socket[SOCK_SERVER])
270            close(screen_list->socket[SOCK_SERVER]);
271
272        if(screen_list->screen)
273            free(screen_list->screen);
274
275        struct option *option = screen_list->config;
276
277        while(option)
278        {
279            struct option *kromeugnon = option;
280            option = option->next;
281            if(kromeugnon->key)   free(kromeugnon->key);
282            if(kromeugnon->value) free(kromeugnon->value);
283            free(kromeugnon);
284        }
285
286        for(i=0; i<screen_list->recurrent_list->count; i++)
287        {
288            remove_recurrent(screen_list->recurrent_list, i);
289            i = 0;
290        }
291
292        if(screen_list->recurrent_list->recurrent)
293            free(screen_list->recurrent_list->recurrent);
294        if(screen_list->recurrent_list)
295            free(screen_list->recurrent_list);
296
297        if(screen_list->session_name)
298            free(screen_list->session_name);
299
300        if(screen_list->title)
301            free(screen_list->title);
302
303        free(screen_list);
304    }
305
306    return mainret;
307}
308
309
310struct screen_list *create_screen_list(void)
311{
312
313    struct screen_list *screen_list = NULL;
314    struct passwd *user_info;
315    char *user_dir = NULL;
316
317    /* Create screen list */
318    screen_list = (struct screen_list*)     malloc(sizeof(struct screen_list));
319    if(!screen_list)
320    {
321        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
322        return NULL;
323    }
324    screen_list->screen = (struct screen**) malloc(sizeof(sizeof(struct screen*)));
325    if(!screen_list->screen)
326    {
327        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
328        free(screen_list);
329        return NULL;
330    }
331
332    screen_list->count = 0;
333    screen_list->mini = 1;
334    screen_list->help = 0;
335    screen_list->status = 1;
336    screen_list->title = NULL;
337    screen_list->window_list = 0;
338    screen_list->wm_type = WM_VSPLIT;
339    screen_list->in_bell = 0;
340    screen_list->pty = screen_list->prevpty = 0;
341    screen_list->dont_update_coords = 0;
342    screen_list->screensaver_timeout = (60) * 1000000;
343    screen_list->screensaver_data = NULL;
344    screen_list->in_screensaver = 0;
345    screen_list->locked = 0;
346    screen_list->lock_offset = 0;
347    screen_list->attached = 1;
348    screen_list->socket[SOCK_SERVER] = 0;
349    screen_list->socket[SOCK_CLIENT] = 0;
350    screen_list->socket_dir    = NULL;
351    screen_list->socket_path[SOCK_SERVER] = NULL;
352    screen_list->socket_path[SOCK_CLIENT] = NULL;
353    screen_list->session_name  = NULL;
354    screen_list->default_shell = NULL;
355    screen_list->user_path     = NULL;
356    screen_list->autolock_timeout = -1;
357    screen_list->to_grab = NULL;
358    screen_list->to_start = NULL;
359    screen_list->nb_to_grab = 0;
360    screen_list->attach = 0;
361    screen_list->forceattach = 0;
362
363
364    screen_list->recurrent_list = NULL;
365    screen_list->cv = NULL;
366    screen_list->dp = NULL;
367
368    memset(screen_list->lockmsg, 0, 1024);
369    memset(screen_list->lockpass, 0, 1024);
370
371
372
373    /* Build local config file path */
374    user_dir = getenv("HOME");
375    if(!user_dir)
376    {
377        user_info = getpwuid(getuid());
378        if(user_info)
379        {
380            user_dir = user_info->pw_dir;
381        }
382    }
383    if(user_dir)
384    {
385        screen_list->user_path = malloc(strlen(user_dir) + strlen("/.neercsrc") + 1);
386        sprintf(screen_list->user_path, "%s/%s", user_dir, ".neercsrc");
387    }
388
389
390    screen_list->recurrent_list = (struct recurrent_list*) malloc(sizeof(struct recurrent_list));
391    screen_list->recurrent_list->recurrent = (struct recurrent**) malloc(sizeof(struct recurrent*));
392    if(!screen_list->recurrent_list->recurrent)
393    {
394        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
395        free(screen_list);
396        free(screen_list->screen);
397        return NULL;
398    }
399    screen_list->recurrent_list->count = 0;
400
401
402
403    return screen_list;
404}
405
406
407
408int handle_command_line(int argc, char *argv[], struct screen_list *screen_list)
409{
410    int s = 0, i;
411    for(;;)
412    {
413        int option_index = 0;
414        int pidopt;
415        static struct myoption long_options[] =
416            {
417                { "config",      1, NULL, 'c' },
418#if defined USE_GRAB
419                { "pid",         1, NULL, 'P' },
420#endif
421                { "help",        0, NULL, 'h' },
422                { "version",     0, NULL, 'v' },
423            };
424#if defined USE_GRAB
425        int c = mygetopt(argc, argv, "c:S:R::r::P:hv", long_options, &option_index);
426#else
427        int c = mygetopt(argc, argv, "c:S:R::r::hv", long_options, &option_index);
428#endif
429        if(c == -1)
430            break;
431
432        switch(c)
433        {
434        case 'c': /* --config */
435            if(screen_list->user_path)
436                free(screen_list->user_path);
437            screen_list->user_path = strdup(myoptarg);
438            s+=2;
439            break;
440        case 'S':
441            if(!screen_list->session_name)
442                screen_list->session_name = strdup(myoptarg);
443            s+=2;
444            break;
445        case 'P': /* --pid */
446            pidopt = atoi(myoptarg);
447            if(pidopt <= 0)
448            {
449                fprintf(stderr, "Invalid pid %d\n", pidopt);
450                if(screen_list->to_grab)
451                    free(screen_list->to_grab);
452                return -1;
453            }
454            if(!screen_list->to_grab)
455            {
456                /* At most argc-1-s times -P <pid> + final 0 */
457                screen_list->to_grab = (int *)malloc(((argc-1-s)/2+1)*sizeof(int));
458                if(!screen_list->to_grab)
459                {
460                    fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
461                    return -1;
462                }
463            }
464            screen_list->to_grab[screen_list->nb_to_grab++] = pidopt;
465            screen_list->to_grab[screen_list->nb_to_grab] = 0;
466            s+=2;
467            break;
468        case 'r':
469            screen_list->forceattach = 1;
470        case 'R':
471            if(screen_list->attach)
472            {
473                fprintf(stderr, "Attaching can only be requested once\n");
474                return -1;
475            }
476            if(myoptarg)
477            {
478                if(screen_list->session_name)
479                    free(screen_list->session_name);
480                screen_list->session_name = strdup(myoptarg);
481                s+=1;
482            }
483            screen_list->attach = 1;
484            s+=1;
485            break;
486        case 'h': /* --help */
487            usage(argc, argv);
488            return -1;
489            break;
490        case 'v': /* --version */
491            version();
492            return -1;
493            break;
494        case -2:
495            return -1;
496        default:
497            fprintf(stderr, "Unknown argument #%d\n", myoptind);
498            return -1;
499            break;
500        }
501    }
502    if(s >= 0 && s < argc - 1)
503    {
504        screen_list->to_start = (char**)malloc((argc-s)*sizeof(char*));
505        if(!screen_list->to_start)
506        {
507            fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
508            return -1;
509        }
510        for(i=0; i<(argc-1) - s; i++)
511        {
512            screen_list->to_start[i] = strdup(argv[i+s+1]);
513        }
514        screen_list->to_start[argc-1-s] = NULL;
515    }
516    return s;
517}
Note: See TracBrowser for help on using the repository browser.