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

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