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

Last change on this file since 1656 was 1656, checked in by sam, 10 years ago

neercs: start porting the old code to Windows; this breaks the Visual Studio
build until further fixes are made.

  • Property svn:keywords set to Id
File size: 13.0 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://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15#if defined HAVE_CONFIG_H
16#   include "config.h"
17#endif
18
19#if !defined _WIN32
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\n", sockets[i]);
242                    break;
243                default:
244                    fprintf(stderr, "Unknown error on %s:%s\n", sockets[i],
245                            strerror(errno));
246                }
247            }
248            else
249            {
250                usable_sockets[nb_usable_sockets] = strdup(sockets[i]);
251                debug("%s is usable", usable_sockets[nb_usable_sockets]);
252                nb_usable_sockets++;
253                close(sock);
254            }
255        }
256        if (!nb_usable_sockets)
257            goto end;
258        if (nb_usable_sockets == 1)
259        {
260            ret = strdup(usable_sockets[0]);
261            goto end;
262        }
263        else
264        {
265            caca_event_t ev;
266            enum caca_event_type t;
267            int current_line = 1;
268            int refresh = 1;
269            screen_list->cv = caca_create_canvas(0, 0);
270            screen_list->dp = caca_create_display(screen_list->cv);
271            if (!screen_list->dp)
272                goto end;
273            caca_set_cursor(screen_list->dp, 0);
274            caca_set_display_title(screen_list->dp, PACKAGE_STRING);
275            while (1)
276            {
277                if (refresh)
278                {
279                    caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE);
280                    caca_fill_box(screen_list->cv,
281                                  0, 0,
282                                  caca_get_canvas_width(screen_list->cv),
283                                  caca_get_canvas_height(screen_list->cv),
284                                  '#');
285                    caca_set_color_ansi(screen_list->cv, CACA_DEFAULT,
286                                        CACA_BLUE);
287                    caca_draw_cp437_box(screen_list->cv, 0, 0,
288                                        caca_get_canvas_width(screen_list->cv),
289                                        caca_get_canvas_height(screen_list->
290                                                               cv));
291                    caca_printf(screen_list->cv, 2, 2,
292                                "Please select a session to reconnect:");
293                    for (i = 0; i < nb_usable_sockets; i++)
294                    {
295                        if (i == current_line - 1)
296                        {
297                            caca_set_attr(screen_list->cv, CACA_BOLD);
298                            caca_set_color_ansi(screen_list->cv, CACA_GREEN,
299                                                CACA_BLUE);
300                            caca_put_char(screen_list->cv, 1, i + 3, '>');
301                        }
302                        else
303                        {
304                            caca_set_attr(screen_list->cv, 0);
305                            caca_set_color_ansi(screen_list->cv,
306                                                CACA_LIGHTGRAY, CACA_BLUE);
307                            caca_put_char(screen_list->cv, 1, i + 3, ' ');
308                        }
309                        caca_printf(screen_list->cv,
310                                    3, i + 3,
311                                    "%s",
312                                    socket_to_session(usable_sockets[i]));
313                    }
314                    caca_refresh_display(screen_list->dp);
315                    refresh = 0;
316                }
317
318                if (!caca_get_event(screen_list->dp,
319                                    CACA_EVENT_KEY_PRESS
320                                    | CACA_EVENT_RESIZE
321                                    | CACA_EVENT_QUIT, &ev, 10000))
322                    continue;
323
324                t = caca_get_event_type(&ev);
325
326                if (t & CACA_EVENT_KEY_PRESS)
327                {
328                    unsigned int c = caca_get_event_key_ch(&ev);
329                    switch (c)
330                    {
331                    case CACA_KEY_UP:
332                        if (current_line > 1)
333                            current_line--;
334                        break;
335                    case CACA_KEY_DOWN:
336                        if (current_line < nb_usable_sockets)
337                            current_line++;
338                        break;
339                    case CACA_KEY_RETURN:
340                        ret = strdup(usable_sockets[current_line - 1]);
341                        goto end;
342                        break;
343                    case CACA_KEY_ESCAPE:
344                        goto end;
345                        break;
346                    default:
347                        break;
348                    }
349                    refresh = 1;
350                }
351                else if (t & CACA_EVENT_RESIZE)
352                {
353                    refresh = 1;
354                }
355                else if (t & CACA_EVENT_QUIT)
356                    goto end;
357            }
358        }
359    }
360
361  end:
362    if (sockets)
363    {
364        for (i = 0; sockets[i]; i++)
365            free(sockets[i]);
366        free(sockets);
367    }
368    if (usable_sockets)
369    {
370        for (i = 0; i < nb_usable_sockets; i++)
371            free(usable_sockets[i]);
372        free(usable_sockets);
373    }
374    if (screen_list->dp)
375    {
376        caca_free_display(screen_list->dp);
377        screen_list->dp = NULL;
378    }
379    if (screen_list->cv)
380    {
381        caca_free_canvas(screen_list->cv);
382        screen_list->cv = NULL;
383    }
384    return ret;
385}
386
387void attach(struct screen_list *screen_list)
388{
389    screen_list->comm.socket_path[SOCK_SERVER] = select_socket(screen_list);
390
391    if (screen_list->comm.socket_path[SOCK_SERVER])
392    {
393        char *session;
394        session = connect_socket(screen_list, SOCK_SERVER);
395        if (session)
396        {
397            debug("Connected to session %s", session);
398            /* Create main canvas and associated caca window */
399            screen_list->cv = caca_create_canvas(0, 0);
400            screen_list->dp = caca_create_display(screen_list->cv);
401            if (!screen_list->dp)
402                return;
403            caca_set_display_time(screen_list->dp, screen_list->delay * 1000);
404            caca_set_cursor(screen_list->dp, 1);
405
406            screen_list->comm.socket_path[SOCK_CLIENT] =
407                build_socket_path(screen_list->comm.socket_dir, session,
408                                  SOCK_CLIENT);
409            create_socket(screen_list, SOCK_CLIENT);
410            request_attach(screen_list);
411            if (screen_list->comm.session_name)
412                free(screen_list->comm.session_name);
413            screen_list->comm.session_name = session;
414        }
415        else
416        {
417            fprintf(stderr, "Failed to attach!\n");
418            free(screen_list->comm.socket_path[SOCK_SERVER]);
419            screen_list->comm.socket_path[SOCK_SERVER] = NULL;
420            screen_list->sys.attach = 0;
421        }
422    }
423    else
424    {
425        fprintf(stderr, "No socket found!\n");
426        screen_list->sys.attach = 0;
427    }
428}
429
430#endif
Note: See TracBrowser for help on using the repository browser.