1 /*
2     Terminal Mixer - multi-point multi-user access to terminal applications
3     Copyright (C) 2007  Lluís Batlle i Rossell
4 
5     Please find the license in the provided COPYING file.
6 */
7 #include <stdlib.h>
8 #include <assert.h>
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <sys/un.h>
12 #include <sys/socket.h>
13 #include <sys/select.h>
14 
15 #include "main.h"
16 #include "handlers.h"
17 
18 static int served_sockets = 0;
19 
20 static int listen_socket = -1; /* not listening */
21 static int connected_sockets = 0;
22 static int *conn_sockets = 0;
23 
24 static char *socket_path = 0;
25 
26 static char default_path_prefix[] = "/tmp/tm-socket.";
27 
get_unix_path()28 void get_unix_path()
29 {
30     char *new;
31 
32     new = getenv("TM_SOCKET");
33     if (new == 0) /* Compose the path from the default values */
34     {
35         char num[20]; /* enough for an int */
36         int len;
37 
38         sprintf(num, "%i", (int) getuid());
39 
40         len = strlen(default_path_prefix) + strlen(num) + 1;
41 
42         command_line.unix_path = malloc(len);
43 
44         sprintf(command_line.unix_path, "%s%s",
45                 default_path_prefix, num);
46     } else
47     {
48         command_line.unix_path = malloc(strlen(new) + 1);
49         strcpy(command_line.unix_path, new);
50     }
51 }
52 
53 
start_listening(int new)54 static void start_listening(int new)
55 {
56     int ls;
57     struct sockaddr_un addr;
58     int res;
59 
60     assert(new > 0);
61     assert(command_line.unix_path != 0);
62 
63     socket_path = command_line.unix_path;
64 
65     ls = socket(AF_UNIX, SOCK_STREAM, 0);
66     if (ls == -1)
67         error("Cannot create the unix listen socket in the server");
68 
69     addr.sun_family = AF_UNIX;
70     strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path));
71 
72     res = bind(ls, (struct sockaddr *) & addr, sizeof(addr));
73     if (res == -1)
74         error("Error binding to %s", socket_path);
75 
76     /* NUANCE: 0 backlog. Why should we assure future connections? */
77     res = listen(ls, 0);
78     if (res == -1)
79         error("Error listening on the binded unix socket");
80 
81     listen_socket = ls;
82 }
83 
s_unix_update_served(int new)84 void s_unix_update_served(int new)
85 {
86     if (new > served_sockets && new > 0 )
87     {
88         conn_sockets = realloc(conn_sockets, sizeof(*conn_sockets) * new);
89         if (listen_socket == -1) /* not listening */
90         {
91             start_listening(new);
92         }
93         served_sockets = new;
94     }
95     else if (new < served_sockets)
96     {
97         not_implemented("new < served_sockets at unix_update_served");
98     }
99 }
100 
s_unix_shutdown()101 void s_unix_shutdown()
102 {
103     int i;
104 
105     if (listen_socket != -1)
106     {
107         close(listen_socket);
108         unlink(socket_path);
109     }
110 
111     for (i=0; i < connected_sockets; ++i)
112     {
113         close(conn_sockets[i]);
114     }
115     connected_sockets = 0;
116     served_sockets = 0;
117 }
118 
s_unix_prepare_read_fdset(fd_set * read_set,int * maxfd)119 void s_unix_prepare_read_fdset(fd_set *read_set, int *maxfd)
120 {
121     int i;
122 
123     FD_SET(listen_socket, read_set);
124     *maxfd = max(*maxfd, listen_socket);
125 
126     for (i=0; i < connected_sockets; ++i)
127     {
128         /* We only accept if we don't have any
129          * connetion opened. */
130         FD_SET(conn_sockets[i], read_set);
131         *maxfd = max(*maxfd, conn_sockets[i]);
132     }
133 }
134 
remove_conn_socket(int n)135 static void remove_conn_socket(int n)
136 {
137     int i;
138 
139     assert(n >= 0);
140 
141     for(i=n+1; i<connected_sockets; ++i)
142     {
143         conn_sockets[i-1] = conn_sockets[i];
144     }
145 
146     connected_sockets -= 1;
147 
148     /* We shrink the memory until we have an array for served_sockets */
149     if (served_sockets <= connected_sockets)
150     {
151         conn_sockets = realloc(conn_sockets, sizeof(*conn_sockets)
152                 * connected_sockets);
153     }
154 }
155 
accept_connection(int ls)156 static int accept_connection(int ls)
157 {
158     int cs;
159     cs = accept(ls, 0, 0);
160     if (cs == -1)
161         not_implemented("accept unix socket error check");
162 
163     /* TODO: Prepare unblocking sockets if needed */
164 
165     return cs;
166 }
167 
s_unix_process_read_fdset(fd_set * read_set)168 void s_unix_process_read_fdset(fd_set *read_set)
169 {
170     int i;
171     /* Active streams */
172     for (i=0; i < connected_sockets; ++i)
173     {
174         if (FD_ISSET(conn_sockets[i], read_set))
175         {
176             int res;
177             res = read(conn_sockets[i], stream_buffer,
178                     stream_buffer_size);
179             if (res == 0)
180             {
181                 close(conn_sockets[i]);
182                 remove_conn_socket(i);
183             } else
184             {
185                 app_control_remote_send_to_stdin(stream_buffer, res);
186             }
187         }
188     }
189 
190     /* Listen connection */
191     if (FD_ISSET(listen_socket, read_set))
192     {
193         if (served_sockets > connected_sockets)
194         {
195             int s;
196             s = accept_connection(listen_socket);
197             conn_sockets[connected_sockets++] = s;
198             welcome_new_client_socket(s);
199         } else
200         {
201             int s;
202             s = accept_connection(listen_socket);
203             close(s);
204         }
205     }
206 }
207 
s_unix_send_to_connected(const char * buffer,size_t size)208 void s_unix_send_to_connected(const char *buffer, size_t size)
209 {
210     int i;
211 
212     for (i=0; i < connected_sockets; ++i)
213     {
214         write(conn_sockets[i], buffer, size);
215     }
216 }
217