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

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