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

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