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

Last change on this file since 3336 was 3336, checked in by Pascal Terjan, 11 years ago
  • Have a more classical look on session selection menu
  • Property svn:eol-style set to native
File size: 12.7 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_BLUE, CACA_BLUE);
252                    caca_fill_box(screen_list->cv,
253                                  0, 0,
254                                  caca_get_canvas_width(screen_list->cv),
255                                  caca_get_canvas_height(screen_list->cv),
256                                  '#');
257                    caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, CACA_BLUE);
258                    caca_draw_cp437_box(screen_list->cv,
259                                  0, 0,
260                                  caca_get_canvas_width(screen_list->cv),
261                                  caca_get_canvas_height(screen_list->cv)
262                                  );
263                    caca_printf(screen_list->cv, 2, 2, "Please select a session to reconnect:");
264                    for(i=0; i<nb_usable_sockets; i++)
265                    {
266                        if(i == current_line-1)
267                        {
268                            caca_set_attr(screen_list->cv, CACA_BOLD);
269                            caca_set_color_ansi(screen_list->cv, CACA_GREEN, CACA_BLUE);
270                            caca_put_char(screen_list->cv, 1, i+3, '>');
271                        }
272                        else
273                        {
274                            caca_set_attr(screen_list->cv, 0);
275                            caca_set_color_ansi(screen_list->cv, CACA_LIGHTGRAY, CACA_BLUE);
276                            caca_put_char(screen_list->cv, 1, i+3, ' ');
277                        }
278                        caca_printf(screen_list->cv,
279                                    3, i+3,
280                                    "%s",
281                                    socket_to_session(usable_sockets[i]));
282                    }
283                    caca_refresh_display(screen_list->dp);
284                    refresh = 0;
285                }
286
287                if(!caca_get_event(screen_list->dp,
288                                  CACA_EVENT_KEY_PRESS
289                                  |CACA_EVENT_RESIZE
290                                  |CACA_EVENT_QUIT,
291                                  &ev, 10000))
292                    continue;
293
294                t = caca_get_event_type(&ev);
295
296                if(t & CACA_EVENT_KEY_PRESS)
297                {
298                    unsigned int c = caca_get_event_key_ch(&ev);
299                    switch(c)
300                    {
301                    case CACA_KEY_UP:
302                        if(current_line>1)
303                            current_line--;
304                        break;
305                    case CACA_KEY_DOWN:
306                        if(current_line<nb_usable_sockets)
307                            current_line++;
308                        break;
309                    case CACA_KEY_RETURN:
310                        ret = strdup(usable_sockets[current_line-1]);
311                        goto end;
312                        break;
313                    case CACA_KEY_ESCAPE:
314                        goto end;
315                        break;
316                    default:
317                        break;
318                    }
319                    refresh = 1;
320                }
321                else if(t & CACA_EVENT_RESIZE)
322                {
323                    refresh = 1;
324                }
325                else if(t & CACA_EVENT_QUIT)
326                    goto end;
327            }
328        }
329    }
330
331end:
332    if(sockets)
333    {
334        for(i=0; sockets[i]; i++)
335            free(sockets[i]);
336        free(sockets);
337    }
338    if(usable_sockets)
339    {
340        for(i=0; i<nb_usable_sockets; i++)
341            free(usable_sockets[i]);
342        free(usable_sockets);
343    }
344    if(screen_list->dp)
345    {
346        caca_free_display(screen_list->dp);
347        screen_list->dp = NULL;
348    }
349    if(screen_list->cv)
350    {
351        caca_free_canvas(screen_list->cv);
352        screen_list->cv = NULL;
353    }
354    return ret;
355}
356
357void attach(struct screen_list* screen_list)
358{
359    screen_list->socket_path[SOCK_SERVER] = select_socket(screen_list);
360
361    if(screen_list->socket_path[SOCK_SERVER])
362    {
363        char *session;
364        session = connect_socket(screen_list, SOCK_SERVER);
365        if(session)
366        {
367            debug("Connected to session %s", session);
368            /* Create main canvas and associated caca window */
369            screen_list->cv = caca_create_canvas(0, 0);
370            screen_list->dp = caca_create_display(screen_list->cv);
371            if(!screen_list->dp)
372                return;
373            caca_set_cursor(screen_list->dp, 1);
374
375            screen_list->socket_path[SOCK_CLIENT] =
376                build_socket_path(screen_list->socket_dir, session, SOCK_CLIENT);
377            create_socket(screen_list, SOCK_CLIENT);
378            request_attach(screen_list);
379            if(screen_list->session_name)
380                free(screen_list->session_name);
381            screen_list->session_name = session;
382        }
383        else
384        {
385            fprintf(stderr, "Failed to attach!\n");
386            free(screen_list->socket_path[SOCK_SERVER]);
387            screen_list->socket_path[SOCK_SERVER] = NULL;
388            screen_list->attach = 0;
389        }
390    }
391    else
392    {
393        fprintf(stderr, "No socket found!\n");
394        screen_list->attach = 0;
395    }
396}
397
398int send_event(caca_event_t ev, struct screen_list* screen_list)
399{
400    enum caca_event_type t;
401
402    t = caca_get_event_type(&ev);
403
404    if(t & CACA_EVENT_KEY_PRESS)
405    {
406        char buf[16];
407        int bytes;
408        bytes = snprintf(buf, sizeof(buf)-1, "KEY %d", caca_get_event_key_ch(&ev));
409        buf[bytes] = '\0';
410        debug("Sending key press to server: %s", buf);
411        return write(screen_list->socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
412    }
413    else if(t & CACA_EVENT_RESIZE)
414    {
415        char buf[32];
416        int bytes;
417        bytes = snprintf(buf, sizeof(buf)-1, "RESIZE %10d%c%10d",
418                         caca_get_event_resize_width(&ev),
419                         ' ',
420                         caca_get_event_resize_height(&ev));
421        buf[bytes] = '\0';
422        debug("Sending resize to server: %s", buf);
423        return write(screen_list->socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
424    }
425    else if(t & CACA_EVENT_QUIT)
426        return write(screen_list->socket[SOCK_SERVER], "QUIT", strlen("QUIT")) <= 0;
427
428    return 0;
429}
430
Note: See TracBrowser for help on using the repository browser.