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

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