source: neercs/trunk/src/attach.c @ 3455

Last change on this file since 3455 was 3455, checked in by Pascal Terjan, 11 years ago
  • Don't refresh more than 60 times per second
  • Property svn:eol-style set to native
File size: 13.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$
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 <errno.h>
18#include <fcntl.h>
19#include <glob.h>
20#include <limits.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24
25#include <sys/socket.h>
26#include <sys/un.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29
30#include <caca.h>
31
32#include "config.h"
33#include "neercs.h"
34
35char * build_socket_path(char *socket_dir, char *session_name, enum socket_type socktype)
36{
37    char *path, *dir;
38    path = (char *)malloc(PATH_MAX+1);
39    dir = socket_dir;
40    if(!dir)
41        dir = getenv("NEERCSDIR");
42    if(!dir)
43        dir = getenv("TMPDIR");
44    if(!dir)
45        dir = "/tmp";
46    if(path)
47        snprintf(path, PATH_MAX+1, "%s/neercs.%s%s.sock", dir, session_name, socktype?"":".srv");
48    return path;
49}
50
51static char * socket_to_session(char const * sockpath)
52{
53    char *p, *s;
54    p = strrchr(sockpath, '/');
55    if(!p)
56    {
57        debug("Invalid socket path %s", sockpath);
58        return NULL;
59    }
60    p+=8; /* skip neercs. */
61    s = strdup(p);
62    p = strrchr(s, '.');
63    *p = '\0'; /* drop .sock */
64    p = strrchr(s, '.');
65    *p = '\0'; /* drop .srv */
66    p = strdup(s);
67    free(s);
68    return p;
69}
70
71int create_socket(struct screen_list* screen_list, enum socket_type socktype)
72{
73    int sock;
74    struct sockaddr_un myaddr;
75
76    sock = socket(AF_UNIX, SOCK_DGRAM, 0);
77
78    if(sock < 0)
79    {
80        perror("create_socket:socket");
81        return errno;
82    }
83
84    memset(&myaddr, 0, sizeof(struct sockaddr_un));
85
86    myaddr.sun_family = AF_UNIX;
87    strncpy(myaddr.sun_path, screen_list->socket_path[socktype], sizeof(myaddr.sun_path) - 1);
88
89    unlink(screen_list->socket_path[socktype]);
90
91    if(bind(sock, (struct sockaddr *)&myaddr, sizeof(struct sockaddr_un)) < 0)
92    {
93        free(screen_list->socket_path[socktype]);
94        screen_list->socket_path[socktype] = NULL;
95        close(sock);
96        perror("create_socket:bind");
97        return errno;
98    }
99    fcntl(sock, F_SETFL, O_NONBLOCK);
100
101    debug("Listening on %s (%d)", screen_list->socket_path[socktype], sock);
102
103    screen_list->socket[socktype] = sock;
104
105    return 0;
106}
107
108char ** list_sockets(char *socket_dir, char *session_name)
109{
110    char *pattern, *dir;
111    glob_t globbuf;
112
113    globbuf.gl_pathv = NULL;
114
115    pattern = (char *)malloc(PATH_MAX+1);
116
117    dir = socket_dir;
118    if(!dir)
119        dir = getenv("NEERCSDIR");
120    if(!dir)
121        dir = getenv("TMPDIR");
122    if(!dir)
123        dir = "/tmp";
124
125    if(!pattern)
126        return globbuf.gl_pathv;
127
128    if(session_name && strlen(session_name)+strlen(dir)+13<PATH_MAX)
129        sprintf(pattern, "%s/neercs.%s.srv.sock", dir, session_name);
130    else
131        snprintf(pattern, PATH_MAX, "%s/neercs.*.srv.sock", dir);
132    pattern[PATH_MAX] = '\0';
133
134    debug("Looking for sockets in the form %s", pattern);
135
136    glob(pattern, 0, NULL, &globbuf);
137
138    free(pattern);
139
140    return globbuf.gl_pathv;
141}
142
143char * connect_socket(struct screen_list* screen_list, enum socket_type socktype)
144{
145    int sock;
146    struct sockaddr_un addr;
147
148    debug("Connecting to %s", screen_list->socket_path[socktype]);
149
150    /* Open the socket */
151    if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
152        perror("connect_server:socket");
153        return NULL;
154    }
155
156    memset(&addr,0,sizeof(addr));
157    addr.sun_family = AF_UNIX;
158    strcpy(addr.sun_path,screen_list->socket_path[socktype]);
159    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
160    {
161        debug("Failed to connect to %s: %s", screen_list->socket_path[socktype], strerror(errno));
162        close(sock);
163        return NULL;
164    }
165    fcntl(sock, F_SETFL, O_NONBLOCK);
166
167    screen_list->socket[socktype] = sock;
168
169    if(socktype == SOCK_SERVER)
170    {
171        return socket_to_session(screen_list->socket_path[socktype]);
172    }
173    else
174        return NULL;
175}
176
177int request_attach(struct screen_list* screen_list)
178{
179    char buf[32];
180    int bytes;
181
182    bytes = snprintf(buf, sizeof(buf)-1, "ATTACH %10d%c%10d",
183                     caca_get_canvas_width(screen_list->cv),
184                     ' ',
185                     caca_get_canvas_height(screen_list->cv));
186    buf[bytes] = '\0';
187    debug("Requesting attach: %s", buf);
188    return write(screen_list->socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
189}
190
191static char * select_socket(struct screen_list* screen_list)
192{
193    char **sockets = NULL, **usable_sockets = NULL;
194    int i, sock, nb_usable_sockets = 0;
195    char *ret = NULL;
196
197    sockets = list_sockets(screen_list->socket_dir, screen_list->session_name);
198    if(sockets)
199    {
200        for(i=0; sockets[i]; i++);
201
202        /* Return the socket or NULL if there is not more than one match */
203        if(i<=1)
204        {
205            if(sockets[0])
206                ret = strdup(sockets[0]);
207            goto end;
208        }
209
210        /* Else ask the user to chose one */
211        usable_sockets = malloc(i*sizeof(char*));
212        for(i=0; sockets[i]; i++)
213        {
214            struct sockaddr_un addr;
215            if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
216                perror("select_socket:socket");
217                goto end;
218            }
219            memset(&addr,0,sizeof(addr));
220            addr.sun_family = AF_UNIX;
221            strcpy(addr.sun_path, sockets[i]);
222            if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
223            {
224                switch(errno)
225                {
226                case EACCES:
227                    debug("Connection refused on %s", sockets[i]);
228                    break;
229                case ECONNREFUSED:
230                    fprintf(stderr, "%s is dead\n", sockets[i]);
231                    break;
232                default:
233                    fprintf(stderr, "Unknown error on %s:%s\n", sockets[i], strerror(errno));
234                }
235            }
236            else
237            {
238                usable_sockets[nb_usable_sockets] = strdup(sockets[i]);
239                debug("%s is usable", usable_sockets[nb_usable_sockets]);
240                nb_usable_sockets++;
241                close(sock);
242            }
243        }
244        if(!nb_usable_sockets)
245            goto end;
246        if(nb_usable_sockets == 1)
247        {
248            ret = strdup(usable_sockets[0]);
249            goto end;
250        }
251        else
252        {
253            caca_event_t ev;
254            enum caca_event_type t;
255            int current_line = 1;
256            int refresh = 1;
257            screen_list->cv = caca_create_canvas(0, 0);
258            screen_list->dp = caca_create_display(screen_list->cv);
259            if(!screen_list->dp)
260                goto end;
261            caca_set_cursor(screen_list->dp, 0);
262            caca_set_display_title(screen_list->dp, PACKAGE_STRING);
263            while(1)
264            {
265                if(refresh)
266                {
267                    caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE);
268                    caca_fill_box(screen_list->cv,
269                                  0, 0,
270                                  caca_get_canvas_width(screen_list->cv),
271                                  caca_get_canvas_height(screen_list->cv),
272                                  '#');
273                    caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, CACA_BLUE);
274                    caca_draw_cp437_box(screen_list->cv,
275                                  0, 0,
276                                  caca_get_canvas_width(screen_list->cv),
277                                  caca_get_canvas_height(screen_list->cv)
278                                  );
279                    caca_printf(screen_list->cv, 2, 2, "Please select a session to reconnect:");
280                    for(i=0; i<nb_usable_sockets; i++)
281                    {
282                        if(i == current_line-1)
283                        {
284                            caca_set_attr(screen_list->cv, CACA_BOLD);
285                            caca_set_color_ansi(screen_list->cv, CACA_GREEN, CACA_BLUE);
286                            caca_put_char(screen_list->cv, 1, i+3, '>');
287                        }
288                        else
289                        {
290                            caca_set_attr(screen_list->cv, 0);
291                            caca_set_color_ansi(screen_list->cv, CACA_LIGHTGRAY, CACA_BLUE);
292                            caca_put_char(screen_list->cv, 1, i+3, ' ');
293                        }
294                        caca_printf(screen_list->cv,
295                                    3, i+3,
296                                    "%s",
297                                    socket_to_session(usable_sockets[i]));
298                    }
299                    caca_refresh_display(screen_list->dp);
300                    refresh = 0;
301                }
302
303                if(!caca_get_event(screen_list->dp,
304                                  CACA_EVENT_KEY_PRESS
305                                  |CACA_EVENT_RESIZE
306                                  |CACA_EVENT_QUIT,
307                                  &ev, 10000))
308                    continue;
309
310                t = caca_get_event_type(&ev);
311
312                if(t & CACA_EVENT_KEY_PRESS)
313                {
314                    unsigned int c = caca_get_event_key_ch(&ev);
315                    switch(c)
316                    {
317                    case CACA_KEY_UP:
318                        if(current_line>1)
319                            current_line--;
320                        break;
321                    case CACA_KEY_DOWN:
322                        if(current_line<nb_usable_sockets)
323                            current_line++;
324                        break;
325                    case CACA_KEY_RETURN:
326                        ret = strdup(usable_sockets[current_line-1]);
327                        goto end;
328                        break;
329                    case CACA_KEY_ESCAPE:
330                        goto end;
331                        break;
332                    default:
333                        break;
334                    }
335                    refresh = 1;
336                }
337                else if(t & CACA_EVENT_RESIZE)
338                {
339                    refresh = 1;
340                }
341                else if(t & CACA_EVENT_QUIT)
342                    goto end;
343            }
344        }
345    }
346
347end:
348    if(sockets)
349    {
350        for(i=0; sockets[i]; i++)
351            free(sockets[i]);
352        free(sockets);
353    }
354    if(usable_sockets)
355    {
356        for(i=0; i<nb_usable_sockets; i++)
357            free(usable_sockets[i]);
358        free(usable_sockets);
359    }
360    if(screen_list->dp)
361    {
362        caca_free_display(screen_list->dp);
363        screen_list->dp = NULL;
364    }
365    if(screen_list->cv)
366    {
367        caca_free_canvas(screen_list->cv);
368        screen_list->cv = NULL;
369    }
370    return ret;
371}
372
373void attach(struct screen_list* screen_list)
374{
375    screen_list->socket_path[SOCK_SERVER] = select_socket(screen_list);
376
377    if(screen_list->socket_path[SOCK_SERVER])
378    {
379        char *session;
380        session = connect_socket(screen_list, SOCK_SERVER);
381        if(session)
382        {
383            debug("Connected to session %s", session);
384            /* Create main canvas and associated caca window */
385            screen_list->cv = caca_create_canvas(0, 0);
386            screen_list->dp = caca_create_display(screen_list->cv);
387            if(!screen_list->dp)
388                return;
389            caca_set_display_time(screen_list->dp, screen_list->delay);
390            caca_set_cursor(screen_list->dp, 1);
391
392            screen_list->socket_path[SOCK_CLIENT] =
393                build_socket_path(screen_list->socket_dir, session, SOCK_CLIENT);
394            create_socket(screen_list, SOCK_CLIENT);
395            request_attach(screen_list);
396            if(screen_list->session_name)
397                free(screen_list->session_name);
398            screen_list->session_name = session;
399        }
400        else
401        {
402            fprintf(stderr, "Failed to attach!\n");
403            free(screen_list->socket_path[SOCK_SERVER]);
404            screen_list->socket_path[SOCK_SERVER] = NULL;
405            screen_list->attach = 0;
406        }
407    }
408    else
409    {
410        fprintf(stderr, "No socket found!\n");
411        screen_list->attach = 0;
412    }
413}
414
415int send_event(caca_event_t ev, struct screen_list* screen_list)
416{
417    enum caca_event_type t;
418
419    t = caca_get_event_type(&ev);
420
421    if(t & CACA_EVENT_KEY_PRESS)
422    {
423        char buf[16];
424        int bytes;
425        bytes = snprintf(buf, sizeof(buf)-1, "KEY %d", caca_get_event_key_ch(&ev));
426        buf[bytes] = '\0';
427        debug("Sending key press to server: %s", buf);
428        return write(screen_list->socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
429    }
430    else if(t & CACA_EVENT_RESIZE)
431    {
432        char buf[32];
433        int bytes;
434        bytes = snprintf(buf, sizeof(buf)-1, "RESIZE %10d %10d",
435                         caca_get_event_resize_width(&ev),
436                         caca_get_event_resize_height(&ev));
437        buf[bytes] = '\0';
438        debug("Sending resize to server: %s", buf);
439        return write(screen_list->socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
440    }
441    else if(t & CACA_EVENT_QUIT)
442        return write(screen_list->socket[SOCK_SERVER], "QUIT", strlen("QUIT")) <= 0;
443
444    return 0;
445}
446
Note: See TracBrowser for help on using the repository browser.