source: trunk/tools/neercs/old/attach.c @ 2183

Last change on this file since 2183 was 2183, checked in by sam, 7 years ago

build: fix the WTFPL site URL in all code comments.

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