source: neercs/trunk/src/attach.c

Last change on this file was 4366, checked in by Sam Hocevar, 10 years ago

Clean up source code, copyright information, author names, SVN keywords...

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