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

Last change on this file since 4059 was 4059, checked in by Jean-Yves Lamoureux, 11 years ago
  • Moved socket-related stuff to struct comm
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 13.0 KB
RevLine 
[3969]1/*
[3338]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 4059 2009-11-29 11:29:51Z 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
[2474]17#include <errno.h>
[2459]18#include <fcntl.h>
[2474]19#include <glob.h>
20#include <limits.h>
[2459]21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24
[2474]25#include <sys/socket.h>
26#include <sys/un.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29
[2588]30#include <caca.h>
31
[2592]32#include "config.h"
[2459]33#include "neercs.h"
34
[3969]35char *build_socket_path(char *socket_dir, char *session_name,
36                        enum socket_type socktype)
[2474]37{
38    char *path, *dir;
[3969]39    path = (char *)malloc(PATH_MAX + 1);
[2474]40    dir = socket_dir;
[3969]41    if (!dir)
[2474]42        dir = getenv("NEERCSDIR");
[3969]43    if (!dir)
[2474]44        dir = getenv("TMPDIR");
[3969]45    if (!dir)
[2474]46        dir = "/tmp";
[3969]47    if (path)
48        snprintf(path, PATH_MAX + 1, "%s/neercs.%s%s.sock", dir, session_name,
49                 socktype ? "" : ".srv");
[2474]50    return path;
51}
52
[3969]53static char *socket_to_session(char const *sockpath)
[3327]54{
55    char *p, *s;
56    p = strrchr(sockpath, '/');
[3969]57    if (!p)
[3327]58    {
59        debug("Invalid socket path %s", sockpath);
60        return NULL;
61    }
[3969]62    p += 8;                     /* skip neercs. */
[3327]63    s = strdup(p);
64    p = strrchr(s, '.');
[3969]65    *p = '\0';                  /* drop .sock */
[3327]66    p = strrchr(s, '.');
[3969]67    *p = '\0';                  /* drop .srv */
[3327]68    p = strdup(s);
69    free(s);
70    return p;
71}
72
[3969]73int create_socket(struct screen_list *screen_list, enum socket_type socktype)
[2474]74{
75    int sock;
76    struct sockaddr_un myaddr;
77
78    sock = socket(AF_UNIX, SOCK_DGRAM, 0);
79
[3969]80    if (sock < 0)
[2474]81    {
[2614]82        perror("create_socket:socket");
[2474]83        return errno;
84    }
85
86    memset(&myaddr, 0, sizeof(struct sockaddr_un));
87
88    myaddr.sun_family = AF_UNIX;
[4059]89    strncpy(myaddr.sun_path, screen_list->comm.socket_path[socktype],
[3969]90            sizeof(myaddr.sun_path) - 1);
[2474]91
[4059]92    unlink(screen_list->comm.socket_path[socktype]);
[2589]93
[3969]94    if (bind(sock, (struct sockaddr *)&myaddr, sizeof(struct sockaddr_un)) < 0)
[2474]95    {
[4059]96        free(screen_list->comm.socket_path[socktype]);
97        screen_list->comm.socket_path[socktype] = NULL;
[2474]98        close(sock);
99        perror("create_socket:bind");
100        return errno;
101    }
102    fcntl(sock, F_SETFL, O_NONBLOCK);
103
[4059]104    debug("Listening on %s (%d)", screen_list->comm.socket_path[socktype], sock);
[2474]105
[4059]106    screen_list->comm.socket[socktype] = sock;
[2474]107
108    return 0;
109}
110
[3969]111char **list_sockets(char *socket_dir, char *session_name)
[2474]112{
113    char *pattern, *dir;
114    glob_t globbuf;
115
116    globbuf.gl_pathv = NULL;
117
[3969]118    pattern = (char *)malloc(PATH_MAX + 1);
[2474]119
120    dir = socket_dir;
[3969]121    if (!dir)
[2474]122        dir = getenv("NEERCSDIR");
[3969]123    if (!dir)
[2474]124        dir = getenv("TMPDIR");
[3969]125    if (!dir)
[2474]126        dir = "/tmp";
127
[3969]128    if (!pattern)
[2474]129        return globbuf.gl_pathv;
130
[3969]131    if (session_name && strlen(session_name) + strlen(dir) + 13 < PATH_MAX)
[2596]132        sprintf(pattern, "%s/neercs.%s.srv.sock", dir, session_name);
[2488]133    else
[2596]134        snprintf(pattern, PATH_MAX, "%s/neercs.*.srv.sock", dir);
[2474]135    pattern[PATH_MAX] = '\0';
136
137    debug("Looking for sockets in the form %s", pattern);
138
139    glob(pattern, 0, NULL, &globbuf);
140
[2482]141    free(pattern);
142
[2474]143    return globbuf.gl_pathv;
144}
145
[3969]146char *connect_socket(struct screen_list *screen_list,
147                     enum socket_type socktype)
[2474]148{
149    int sock;
150    struct sockaddr_un addr;
151
[2614]152    debug("Connecting to %s", screen_list->socket_path[socktype]);
[2474]153
154    /* Open the socket */
[3969]155    if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
[3968]156    {
[3996]157        debug("Failed to create socket\n");
[2589]158        perror("connect_server:socket");
[2588]159        return NULL;
[2474]160    }
[2470]161
[3969]162    memset(&addr, 0, sizeof(addr));
[2474]163    addr.sun_family = AF_UNIX;
[4059]164    strcpy(addr.sun_path, screen_list->comm.socket_path[socktype]);
[2476]165    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
[2474]166    {
[3969]167        debug("Failed to connect to %s: %s",
[4059]168              screen_list->comm.socket_path[socktype], strerror(errno));
[2674]169        close(sock);
[2588]170        return NULL;
[2474]171    }
[2588]172    fcntl(sock, F_SETFL, O_NONBLOCK);
[2474]173
[4059]174    screen_list->comm.socket[socktype] = sock;
[2474]175
[3969]176    if (socktype == SOCK_SERVER)
[2589]177    {
[4059]178        return socket_to_session(screen_list->comm.socket_path[socktype]);
[2589]179    }
[2614]180    else
181        return NULL;
[2588]182}
[2474]183
[3969]184int request_attach(struct screen_list *screen_list)
[2588]185{
[3457]186    char buf[41];
[2589]187    int bytes;
188
[3969]189    bytes = snprintf(buf, sizeof(buf) - 1, "ATTACH %10d %10d %10d",
[2995]190                     caca_get_canvas_width(screen_list->cv),
[3457]191                     caca_get_canvas_height(screen_list->cv),
192                     screen_list->delay);
[2589]193    buf[bytes] = '\0';
194    debug("Requesting attach: %s", buf);
[4059]195    return write(screen_list->comm.socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
[2589]196}
197
[3969]198static char *select_socket(struct screen_list *screen_list)
[3324]199{
[3327]200    char **sockets = NULL, **usable_sockets = NULL;
201    int i, sock, nb_usable_sockets = 0;
202    char *ret = NULL;
[3324]203
[4059]204    sockets = list_sockets(screen_list->comm.socket_dir, screen_list->comm.session_name);
[3969]205    if (sockets)
[3324]206    {
[3969]207        for (i = 0; sockets[i]; i++);
[3327]208
209        /* Return the socket or NULL if there is not more than one match */
[3969]210        if (i <= 1)
[3324]211        {
[3969]212            if (sockets[0])
[3327]213                ret = strdup(sockets[0]);
214            goto end;
[3324]215        }
[3327]216
217        /* Else ask the user to chose one */
[3969]218        usable_sockets = malloc(i * sizeof(char *));
219        for (i = 0; sockets[i]; i++)
[3327]220        {
221            struct sockaddr_un addr;
[3969]222            if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
[3968]223            {
[3327]224                perror("select_socket:socket");
225                goto end;
226            }
[3969]227            memset(&addr, 0, sizeof(addr));
[3327]228            addr.sun_family = AF_UNIX;
229            strcpy(addr.sun_path, sockets[i]);
230            if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
231            {
[3969]232                switch (errno)
[3327]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:
[3969]241                    fprintf(stderr, "Unknown error on %s:%s\n", sockets[i],
242                            strerror(errno));
[3327]243                }
244            }
245            else
246            {
247                usable_sockets[nb_usable_sockets] = strdup(sockets[i]);
[3335]248                debug("%s is usable", usable_sockets[nb_usable_sockets]);
[3327]249                nb_usable_sockets++;
250                close(sock);
251            }
252        }
[3969]253        if (!nb_usable_sockets)
[3327]254            goto end;
[3969]255        if (nb_usable_sockets == 1)
[3327]256        {
[3335]257            ret = strdup(usable_sockets[0]);
[3327]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);
[3969]268            if (!screen_list->dp)
[3327]269                goto end;
270            caca_set_cursor(screen_list->dp, 0);
[3331]271            caca_set_display_title(screen_list->dp, PACKAGE_STRING);
[3969]272            while (1)
[3327]273            {
[3969]274                if (refresh)
[3327]275                {
[3336]276                    caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE);
277                    caca_fill_box(screen_list->cv,
278                                  0, 0,
[3331]279                                  caca_get_canvas_width(screen_list->cv),
280                                  caca_get_canvas_height(screen_list->cv),
[3336]281                                  '#');
[3969]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++)
[3327]291                    {
[3969]292                        if (i == current_line - 1)
[3327]293                        {
294                            caca_set_attr(screen_list->cv, CACA_BOLD);
[3969]295                            caca_set_color_ansi(screen_list->cv, CACA_GREEN,
296                                                CACA_BLUE);
297                            caca_put_char(screen_list->cv, 1, i + 3, '>');
[3327]298                        }
299                        else
300                        {
301                            caca_set_attr(screen_list->cv, 0);
[3969]302                            caca_set_color_ansi(screen_list->cv,
303                                                CACA_LIGHTGRAY, CACA_BLUE);
304                            caca_put_char(screen_list->cv, 1, i + 3, ' ');
[3327]305                        }
306                        caca_printf(screen_list->cv,
[3969]307                                    3, i + 3,
[3327]308                                    "%s",
309                                    socket_to_session(usable_sockets[i]));
310                    }
311                    caca_refresh_display(screen_list->dp);
312                    refresh = 0;
313                }
314
[3969]315                if (!caca_get_event(screen_list->dp,
316                                    CACA_EVENT_KEY_PRESS
317                                    | CACA_EVENT_RESIZE
318                                    | CACA_EVENT_QUIT, &ev, 10000))
[3327]319                    continue;
320
321                t = caca_get_event_type(&ev);
322
[3969]323                if (t & CACA_EVENT_KEY_PRESS)
[3327]324                {
325                    unsigned int c = caca_get_event_key_ch(&ev);
[3969]326                    switch (c)
[3327]327                    {
328                    case CACA_KEY_UP:
[3969]329                        if (current_line > 1)
[3327]330                            current_line--;
331                        break;
332                    case CACA_KEY_DOWN:
[3969]333                        if (current_line < nb_usable_sockets)
[3327]334                            current_line++;
335                        break;
336                    case CACA_KEY_RETURN:
[3969]337                        ret = strdup(usable_sockets[current_line - 1]);
[3327]338                        goto end;
339                        break;
[3328]340                    case CACA_KEY_ESCAPE:
341                        goto end;
342                        break;
[3327]343                    default:
344                        break;
345                    }
346                    refresh = 1;
347                }
[3969]348                else if (t & CACA_EVENT_RESIZE)
[3327]349                {
350                    refresh = 1;
351                }
[3969]352                else if (t & CACA_EVENT_QUIT)
[3327]353                    goto end;
354            }
355        }
356    }
357
[3969]358  end:
359    if (sockets)
[3327]360    {
[3969]361        for (i = 0; sockets[i]; i++)
[3327]362            free(sockets[i]);
363        free(sockets);
364    }
[3969]365    if (usable_sockets)
[3327]366    {
[3969]367        for (i = 0; i < nb_usable_sockets; i++)
[3327]368            free(usable_sockets[i]);
369        free(usable_sockets);
370    }
[3969]371    if (screen_list->dp)
[3327]372    {
373        caca_free_display(screen_list->dp);
374        screen_list->dp = NULL;
375    }
[3969]376    if (screen_list->cv)
[3327]377    {
378        caca_free_canvas(screen_list->cv);
379        screen_list->cv = NULL;
380    }
381    return ret;
382}
383
[3969]384void attach(struct screen_list *screen_list)
[3327]385{
[4059]386    screen_list->comm.socket_path[SOCK_SERVER] = select_socket(screen_list);
[3327]387
[4059]388    if (screen_list->comm.socket_path[SOCK_SERVER])
[3327]389    {
390        char *session;
391        session = connect_socket(screen_list, SOCK_SERVER);
[3969]392        if (session)
[3324]393        {
[3327]394            debug("Connected to session %s", session);
[3324]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);
[3969]398            if (!screen_list->dp)
[3324]399                return;
[3969]400            caca_set_display_time(screen_list->dp, screen_list->delay * 1000);
[3324]401            caca_set_cursor(screen_list->dp, 1);
402
[4059]403            screen_list->comm.socket_path[SOCK_CLIENT] =
404                build_socket_path(screen_list->comm.socket_dir, session,
[3969]405                                  SOCK_CLIENT);
[3324]406            create_socket(screen_list, SOCK_CLIENT);
407            request_attach(screen_list);
[4059]408            if (screen_list->comm.session_name)
409                free(screen_list->comm.session_name);
410            screen_list->comm.session_name = session;
[3324]411        }
412        else
413        {
414            fprintf(stderr, "Failed to attach!\n");
[4059]415            free(screen_list->comm.socket_path[SOCK_SERVER]);
416            screen_list->comm.socket_path[SOCK_SERVER] = NULL;
[3324]417            screen_list->attach = 0;
418        }
419    }
420    else
421    {
422        fprintf(stderr, "No socket found!\n");
423        screen_list->attach = 0;
424    }
425}
426
Note: See TracBrowser for help on using the repository browser.