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

Last change on this file since 3968 was 3968, checked in by Jean-Yves Lamoureux, 12 years ago
  • Identation
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 13.6 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: attach.c 3968 2009-11-19 16:18:32Z jylam $
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    {
153        perror("connect_server:socket");
154        return NULL;
155    }
156
157    memset(&addr,0,sizeof(addr));
158    addr.sun_family = AF_UNIX;
159    strcpy(addr.sun_path,screen_list->socket_path[socktype]);
160    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
161    {
162        debug("Failed to connect to %s: %s", screen_list->socket_path[socktype], strerror(errno));
163        close(sock);
164        return NULL;
165    }
166    fcntl(sock, F_SETFL, O_NONBLOCK);
167
168    screen_list->socket[socktype] = sock;
169
170    if(socktype == SOCK_SERVER)
171    {
172        return socket_to_session(screen_list->socket_path[socktype]);
173    }
174    else
175        return NULL;
176}
177
178int request_attach(struct screen_list* screen_list)
179{
180    char buf[41];
181    int bytes;
182
183    bytes = snprintf(buf, sizeof(buf)-1, "ATTACH %10d %10d %10d",
184                     caca_get_canvas_width(screen_list->cv),
185                     caca_get_canvas_height(screen_list->cv),
186                     screen_list->delay);
187    buf[bytes] = '\0';
188    debug("Requesting attach: %s", buf);
189    return write(screen_list->socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
190}
191
192static char * select_socket(struct screen_list* screen_list)
193{
194    char **sockets = NULL, **usable_sockets = NULL;
195    int i, sock, nb_usable_sockets = 0;
196    char *ret = NULL;
197
198    sockets = list_sockets(screen_list->socket_dir, screen_list->session_name);
199    if(sockets)
200    {
201        for(i=0; sockets[i]; i++);
202
203        /* Return the socket or NULL if there is not more than one match */
204        if(i<=1)
205        {
206            if(sockets[0])
207                ret = strdup(sockets[0]);
208            goto end;
209        }
210
211        /* Else ask the user to chose one */
212        usable_sockets = malloc(i*sizeof(char*));
213        for(i=0; sockets[i]; i++)
214        {
215            struct sockaddr_un addr;
216            if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
217            {
218                perror("select_socket:socket");
219                goto end;
220            }
221            memset(&addr,0,sizeof(addr));
222            addr.sun_family = AF_UNIX;
223            strcpy(addr.sun_path, sockets[i]);
224            if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
225            {
226                switch(errno)
227                {
228                case EACCES:
229                    debug("Connection refused on %s", sockets[i]);
230                    break;
231                case ECONNREFUSED:
232                    fprintf(stderr, "%s is dead\n", sockets[i]);
233                    break;
234                default:
235                    fprintf(stderr, "Unknown error on %s:%s\n", sockets[i], strerror(errno));
236                }
237            }
238            else
239            {
240                usable_sockets[nb_usable_sockets] = strdup(sockets[i]);
241                debug("%s is usable", usable_sockets[nb_usable_sockets]);
242                nb_usable_sockets++;
243                close(sock);
244            }
245        }
246        if(!nb_usable_sockets)
247            goto end;
248        if(nb_usable_sockets == 1)
249        {
250            ret = strdup(usable_sockets[0]);
251            goto end;
252        }
253        else
254        {
255            caca_event_t ev;
256            enum caca_event_type t;
257            int current_line = 1;
258            int refresh = 1;
259            screen_list->cv = caca_create_canvas(0, 0);
260            screen_list->dp = caca_create_display(screen_list->cv);
261            if(!screen_list->dp)
262                goto end;
263            caca_set_cursor(screen_list->dp, 0);
264            caca_set_display_title(screen_list->dp, PACKAGE_STRING);
265            while(1)
266            {
267                if(refresh)
268                {
269                    caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE);
270                    caca_fill_box(screen_list->cv,
271                                  0, 0,
272                                  caca_get_canvas_width(screen_list->cv),
273                                  caca_get_canvas_height(screen_list->cv),
274                                  '#');
275                    caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, CACA_BLUE);
276                    caca_draw_cp437_box(screen_list->cv,
277                                  0, 0,
278                                  caca_get_canvas_width(screen_list->cv),
279                                  caca_get_canvas_height(screen_list->cv)
280                                  );
281                    caca_printf(screen_list->cv, 2, 2, "Please select a session to reconnect:");
282                    for(i=0; i<nb_usable_sockets; i++)
283                    {
284                        if(i == current_line-1)
285                        {
286                            caca_set_attr(screen_list->cv, CACA_BOLD);
287                            caca_set_color_ansi(screen_list->cv, CACA_GREEN, CACA_BLUE);
288                            caca_put_char(screen_list->cv, 1, i+3, '>');
289                        }
290                        else
291                        {
292                            caca_set_attr(screen_list->cv, 0);
293                            caca_set_color_ansi(screen_list->cv, CACA_LIGHTGRAY, CACA_BLUE);
294                            caca_put_char(screen_list->cv, 1, i+3, ' ');
295                        }
296                        caca_printf(screen_list->cv,
297                                    3, i+3,
298                                    "%s",
299                                    socket_to_session(usable_sockets[i]));
300                    }
301                    caca_refresh_display(screen_list->dp);
302                    refresh = 0;
303                }
304
305                if(!caca_get_event(screen_list->dp,
306                                  CACA_EVENT_KEY_PRESS
307                                  |CACA_EVENT_RESIZE
308                                  |CACA_EVENT_QUIT,
309                                  &ev, 10000))
310                    continue;
311
312                t = caca_get_event_type(&ev);
313
314                if(t & CACA_EVENT_KEY_PRESS)
315                {
316                    unsigned int c = caca_get_event_key_ch(&ev);
317                    switch(c)
318                    {
319                    case CACA_KEY_UP:
320                        if(current_line>1)
321                            current_line--;
322                        break;
323                    case CACA_KEY_DOWN:
324                        if(current_line<nb_usable_sockets)
325                            current_line++;
326                        break;
327                    case CACA_KEY_RETURN:
328                        ret = strdup(usable_sockets[current_line-1]);
329                        goto end;
330                        break;
331                    case CACA_KEY_ESCAPE:
332                        goto end;
333                        break;
334                    default:
335                        break;
336                    }
337                    refresh = 1;
338                }
339                else if(t & CACA_EVENT_RESIZE)
340                {
341                    refresh = 1;
342                }
343                else if(t & CACA_EVENT_QUIT)
344                    goto end;
345            }
346        }
347    }
348
349end:
350    if(sockets)
351    {
352        for(i=0; sockets[i]; i++)
353            free(sockets[i]);
354        free(sockets);
355    }
356    if(usable_sockets)
357    {
358        for(i=0; i<nb_usable_sockets; i++)
359            free(usable_sockets[i]);
360        free(usable_sockets);
361    }
362    if(screen_list->dp)
363    {
364        caca_free_display(screen_list->dp);
365        screen_list->dp = NULL;
366    }
367    if(screen_list->cv)
368    {
369        caca_free_canvas(screen_list->cv);
370        screen_list->cv = NULL;
371    }
372    return ret;
373}
374
375void attach(struct screen_list* screen_list)
376{
377    screen_list->socket_path[SOCK_SERVER] = select_socket(screen_list);
378
379    if(screen_list->socket_path[SOCK_SERVER])
380    {
381        char *session;
382        session = connect_socket(screen_list, SOCK_SERVER);
383        if(session)
384        {
385            debug("Connected to session %s", session);
386            /* Create main canvas and associated caca window */
387            screen_list->cv = caca_create_canvas(0, 0);
388            screen_list->dp = caca_create_display(screen_list->cv);
389            if(!screen_list->dp)
390                return;
391            caca_set_display_time(screen_list->dp, screen_list->delay*1000);
392            caca_set_cursor(screen_list->dp, 1);
393
394            screen_list->socket_path[SOCK_CLIENT] =
395                build_socket_path(screen_list->socket_dir, session, SOCK_CLIENT);
396            create_socket(screen_list, SOCK_CLIENT);
397            request_attach(screen_list);
398            if(screen_list->session_name)
399                free(screen_list->session_name);
400            screen_list->session_name = session;
401        }
402        else
403        {
404            fprintf(stderr, "Failed to attach!\n");
405            free(screen_list->socket_path[SOCK_SERVER]);
406            screen_list->socket_path[SOCK_SERVER] = NULL;
407            screen_list->attach = 0;
408        }
409    }
410    else
411    {
412        fprintf(stderr, "No socket found!\n");
413        screen_list->attach = 0;
414    }
415}
416
417int send_event(caca_event_t ev, struct screen_list* screen_list)
418{
419    enum caca_event_type t;
420
421    t = caca_get_event_type(&ev);
422
423    if(t & CACA_EVENT_KEY_PRESS)
424    {
425        char buf[16];
426        int bytes;
427        bytes = snprintf(buf, sizeof(buf)-1, "KEY %d", caca_get_event_key_ch(&ev));
428        buf[bytes] = '\0';
429        debug("Sending key press to server: %s", buf);
430        return write(screen_list->socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
431    }
432    else if(t & CACA_EVENT_RESIZE)
433    {
434        char buf[32];
435        int bytes;
436        bytes = snprintf(buf, sizeof(buf)-1, "RESIZE %10d %10d",
437                         caca_get_event_resize_width(&ev),
438                         caca_get_event_resize_height(&ev));
439        buf[bytes] = '\0';
440        debug("Sending resize to server: %s", buf);
441        return write(screen_list->socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
442    }
443    else if(t & CACA_EVENT_QUIT)
444        return write(screen_list->socket[SOCK_SERVER], "QUIT", strlen("QUIT")) <= 0;
445
446    return 0;
447}
448
449int send_delay(struct screen_list* screen_list)
450{
451    char buf[18];
452    int bytes;
453    bytes = snprintf(buf, sizeof(buf)-1, "DELAY %10d", screen_list->delay);
454    buf[bytes] = '\0';
455    return write(screen_list->socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
456}
Note: See TracBrowser for help on using the repository browser.