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

Last change on this file since 3329 was 3329, checked in by Pascal Terjan, 11 years ago
  • Revert unwanted part of last patch
  • Property svn:eol-style set to native
File size: 12.3 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_set_display_title(screen_list->dp, PACKAGE_STRING);
246            while(1)
247            {
248                if(refresh)
249                {
250                    caca_set_color_argb(screen_list->cv, 0xfcd, CACA_BLACK);
251                    caca_fill_box(screen_list->cv, 0, 0,
252                                  caca_get_display_width(screen_list->dp) - 2,
253                                  caca_get_display_height(screen_list->dp) - 2,
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:");
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);
263                            caca_put_char(screen_list->cv, 1, i+3, '>');
264                        }
265                        else
266                        {
267                            caca_set_attr(screen_list->cv, 0);
268                            caca_set_color_ansi(screen_list->cv, CACA_LIGHTGRAY, CACA_BLACK);
269                            caca_put_char(screen_list->cv, 1, i+3, ' ');
270                        }
271                        caca_printf(screen_list->cv,
272                                    3, i+3,
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;
306                    case CACA_KEY_ESCAPE:
307                        goto end;
308                        break;
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);
358        if(session)
359        {
360            debug("Connected to session %s", session);
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
391int send_event(caca_event_t ev, struct screen_list* screen_list)
392{
393    enum caca_event_type t;
394
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);
404        return write(screen_list->socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
405    }
406    else if(t & CACA_EVENT_RESIZE)
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);
416        return write(screen_list->socket[SOCK_SERVER], buf, strlen(buf)) <= 0;
417    }
418    else if(t & CACA_EVENT_QUIT)
419        return write(screen_list->socket[SOCK_SERVER], "QUIT", strlen("QUIT")) <= 0;
420
421    return 0;
422}
423
Note: See TracBrowser for help on using the repository browser.