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

Last change on this file since 3335 was 3335, checked in by Pascal Terjan, 12 years ago
  • Fix a crash when only one session is detached
  • Property svn:eol-style set to native
File size: 12.4 KB
Line 
1#include <errno.h>
2#include <fcntl.h>
3#include <glob.h>
4#include <limits.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <unistd.h>
8
9#include <sys/socket.h>
10#include <sys/un.h>
11#include <sys/types.h>
12#include <sys/stat.h>
13
14#include <caca.h>
15
16#include "config.h"
17#include "neercs.h"
18
19char * build_socket_path(char *socket_dir, char *session_name, enum socket_type socktype)
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)
31        snprintf(path, PATH_MAX+1, "%s/neercs.%s%s.sock", dir, session_name, socktype?"":".srv");
32    return path;
33}
34
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
55int create_socket(struct screen_list* screen_list, enum socket_type socktype)
56{
57    int sock;
58    struct sockaddr_un myaddr;
59
60    sock = socket(AF_UNIX, SOCK_DGRAM, 0);
61
62    if(sock < 0)
63    {
64        perror("create_socket:socket");
65        return errno;
66    }
67
68    memset(&myaddr, 0, sizeof(struct sockaddr_un));
69
70    myaddr.sun_family = AF_UNIX;
71    strncpy(myaddr.sun_path, screen_list->socket_path[socktype], sizeof(myaddr.sun_path) - 1);
72
73    unlink(screen_list->socket_path[socktype]);
74
75    if(bind(sock, (struct sockaddr *)&myaddr, sizeof(struct sockaddr_un)) < 0)
76    {
77        free(screen_list->socket_path[socktype]);
78        screen_list->socket_path[socktype] = NULL;
79        close(sock);
80        perror("create_socket:bind");
81        return errno;
82    }
83    fcntl(sock, F_SETFL, O_NONBLOCK);
84
85    debug("Listening on %s (%d)", screen_list->socket_path[socktype], sock);
86
87    screen_list->socket[socktype] = sock;
88
89    return 0;
90}
91
92char ** list_sockets(char *socket_dir, char *session_name)
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
112    if(session_name && strlen(session_name)+strlen(dir)+13<PATH_MAX)
113        sprintf(pattern, "%s/neercs.%s.srv.sock", dir, session_name);
114    else
115        snprintf(pattern, PATH_MAX, "%s/neercs.*.srv.sock", dir);
116    pattern[PATH_MAX] = '\0';
117
118    debug("Looking for sockets in the form %s", pattern);
119
120    glob(pattern, 0, NULL, &globbuf);
121
122    free(pattern);
123
124    return globbuf.gl_pathv;
125}
126
127char * connect_socket(struct screen_list* screen_list, enum socket_type socktype)
128{
129    int sock;
130    struct sockaddr_un addr;
131
132    debug("Connecting to %s", screen_list->socket_path[socktype]);
133
134    /* Open the socket */
135    if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
136        perror("connect_server:socket");
137        return NULL;
138    }
139
140    memset(&addr,0,sizeof(addr));
141    addr.sun_family = AF_UNIX;
142    strcpy(addr.sun_path,screen_list->socket_path[socktype]);
143    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
144    {
145        fprintf(stderr, "Failed to connect to %s: %s\n", screen_list->socket_path[socktype], strerror(errno));
146        close(sock);
147        return NULL;
148    }
149    fcntl(sock, F_SETFL, O_NONBLOCK);
150
151    screen_list->socket[socktype] = sock;
152
153    if(socktype == SOCK_SERVER)
154    {
155        return socket_to_session(screen_list->socket_path[socktype]);
156    }
157    else
158        return NULL;
159}
160
161int request_attach(struct screen_list* screen_list)
162{
163    char buf[32];
164    int bytes;
165
166    bytes = snprintf(buf, sizeof(buf)-1, "ATTACH %10d%c%10d",
167                     caca_get_canvas_width(screen_list->cv),
168                     ' ',
169                     caca_get_canvas_height(screen_list->cv));
170    buf[bytes] = '\0';
171    debug("Requesting attach: %s", buf);
172    return write(screen_list->socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
173}
174
175static char * select_socket(struct screen_list* screen_list)
176{
177    char **sockets = NULL, **usable_sockets = NULL;
178    int i, sock, nb_usable_sockets = 0;
179    char *ret = NULL;
180
181    sockets = list_sockets(screen_list->socket_dir, screen_list->session_name);
182    if(sockets)
183    {
184        for(i=0; sockets[i]; i++);
185
186        /* Return the socket or NULL if there is not more than one match */
187        if(i<=1)
188        {
189            if(sockets[0])
190                ret = strdup(sockets[0]);
191            goto end;
192        }
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                debug("%s is usable", usable_sockets[nb_usable_sockets]);
224                nb_usable_sockets++;
225                close(sock);
226            }
227        }
228        if(!nb_usable_sockets)
229            goto end;
230        if(nb_usable_sockets == 1)
231        {
232            ret = strdup(usable_sockets[0]);
233            goto end;
234        }
235        else
236        {
237            caca_event_t ev;
238            enum caca_event_type t;
239            int current_line = 1;
240            int refresh = 1;
241            screen_list->cv = caca_create_canvas(0, 0);
242            screen_list->dp = caca_create_display(screen_list->cv);
243            if(!screen_list->dp)
244                goto end;
245            caca_set_cursor(screen_list->dp, 0);
246            caca_set_display_title(screen_list->dp, PACKAGE_STRING);
247            while(1)
248            {
249                if(refresh)
250                {
251                    caca_set_color_ansi(screen_list->cv, CACA_RED, CACA_BLACK);
252                    caca_draw_box(screen_list->cv, 0, 0,
253                                  caca_get_canvas_width(screen_list->cv),
254                                  caca_get_canvas_height(screen_list->cv),
255                                  0x2764);
256                    caca_set_color_ansi(screen_list->cv, CACA_LIGHTGRAY, CACA_BLACK);
257                    caca_printf(screen_list->cv, 2, 2, "Please select a session to reconnect:");
258                    for(i=0; i<nb_usable_sockets; i++)
259                    {
260                        if(i == current_line-1)
261                        {
262                            caca_set_attr(screen_list->cv, CACA_BOLD);
263                            caca_set_color_ansi(screen_list->cv, CACA_GREEN, CACA_BLACK);
264                            caca_put_char(screen_list->cv, 1, i+3, '>');
265                        }
266                        else
267                        {
268                            caca_set_attr(screen_list->cv, 0);
269                            caca_set_color_ansi(screen_list->cv, CACA_LIGHTGRAY, CACA_BLACK);
270                            caca_put_char(screen_list->cv, 1, i+3, ' ');
271                        }
272                        caca_printf(screen_list->cv,
273                                    3, i+3,
274                                    "%s",
275                                    socket_to_session(usable_sockets[i]));
276                    }
277                    caca_refresh_display(screen_list->dp);
278                    refresh = 0;
279                }
280
281                if(!caca_get_event(screen_list->dp,
282                                  CACA_EVENT_KEY_PRESS
283                                  |CACA_EVENT_RESIZE
284                                  |CACA_EVENT_QUIT,
285                                  &ev, 10000))
286                    continue;
287
288                t = caca_get_event_type(&ev);
289
290                if(t & CACA_EVENT_KEY_PRESS)
291                {
292                    unsigned int c = caca_get_event_key_ch(&ev);
293                    switch(c)
294                    {
295                    case CACA_KEY_UP:
296                        if(current_line>1)
297                            current_line--;
298                        break;
299                    case CACA_KEY_DOWN:
300                        if(current_line<nb_usable_sockets)
301                            current_line++;
302                        break;
303                    case CACA_KEY_RETURN:
304                        ret = strdup(usable_sockets[current_line-1]);
305                        goto end;
306                        break;
307                    case CACA_KEY_ESCAPE:
308                        goto end;
309                        break;
310                    default:
311                        break;
312                    }
313                    refresh = 1;
314                }
315                else if(t & CACA_EVENT_RESIZE)
316                {
317                    refresh = 1;
318                }
319                else if(t & CACA_EVENT_QUIT)
320                    goto end;
321            }
322        }
323    }
324
325end:
326    if(sockets)
327    {
328        for(i=0; sockets[i]; i++)
329            free(sockets[i]);
330        free(sockets);
331    }
332    if(usable_sockets)
333    {
334        for(i=0; i<nb_usable_sockets; i++)
335            free(usable_sockets[i]);
336        free(usable_sockets);
337    }
338    if(screen_list->dp)
339    {
340        caca_free_display(screen_list->dp);
341        screen_list->dp = NULL;
342    }
343    if(screen_list->cv)
344    {
345        caca_free_canvas(screen_list->cv);
346        screen_list->cv = NULL;
347    }
348    return ret;
349}
350
351void attach(struct screen_list* screen_list)
352{
353    screen_list->socket_path[SOCK_SERVER] = select_socket(screen_list);
354
355    if(screen_list->socket_path[SOCK_SERVER])
356    {
357        char *session;
358        session = connect_socket(screen_list, SOCK_SERVER);
359        if(session)
360        {
361            debug("Connected to session %s", session);
362            /* Create main canvas and associated caca window */
363            screen_list->cv = caca_create_canvas(0, 0);
364            screen_list->dp = caca_create_display(screen_list->cv);
365            if(!screen_list->dp)
366                return;
367            caca_set_cursor(screen_list->dp, 1);
368
369            screen_list->socket_path[SOCK_CLIENT] =
370                build_socket_path(screen_list->socket_dir, session, SOCK_CLIENT);
371            create_socket(screen_list, SOCK_CLIENT);
372            request_attach(screen_list);
373            if(screen_list->session_name)
374                free(screen_list->session_name);
375            screen_list->session_name = session;
376        }
377        else
378        {
379            fprintf(stderr, "Failed to attach!\n");
380            free(screen_list->socket_path[SOCK_SERVER]);
381            screen_list->socket_path[SOCK_SERVER] = NULL;
382            screen_list->attach = 0;
383        }
384    }
385    else
386    {
387        fprintf(stderr, "No socket found!\n");
388        screen_list->attach = 0;
389    }
390}
391
392int send_event(caca_event_t ev, struct screen_list* screen_list)
393{
394    enum caca_event_type t;
395
396    t = caca_get_event_type(&ev);
397
398    if(t & CACA_EVENT_KEY_PRESS)
399    {
400        char buf[16];
401        int bytes;
402        bytes = snprintf(buf, sizeof(buf)-1, "KEY %d", caca_get_event_key_ch(&ev));
403        buf[bytes] = '\0';
404        debug("Sending key press to server: %s", buf);
405        return write(screen_list->socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
406    }
407    else if(t & CACA_EVENT_RESIZE)
408    {
409        char buf[32];
410        int bytes;
411        bytes = snprintf(buf, sizeof(buf)-1, "RESIZE %10d%c%10d",
412                         caca_get_event_resize_width(&ev),
413                         ' ',
414                         caca_get_event_resize_height(&ev));
415        buf[bytes] = '\0';
416        debug("Sending resize to server: %s", buf);
417        return write(screen_list->socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
418    }
419    else if(t & CACA_EVENT_QUIT)
420        return write(screen_list->socket[SOCK_SERVER], "QUIT", strlen("QUIT")) <= 0;
421
422    return 0;
423}
424
Note: See TracBrowser for help on using the repository browser.