]> Pileus Git - ~andy/gtk/blob - gdk/linux-fb/gdkfbmanager.c
845a5b507d8f8c9081bf6696a44951f517015c0f
[~andy/gtk] / gdk / linux-fb / gdkfbmanager.c
1 #include <config.h>
2 #include <glib.h>
3 #include <glib/gprintf.h>
4
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <sys/un.h>
8 #include <sys/time.h>
9 #include <sys/types.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14
15 #include "gdkfbmanager.h"
16
17 typedef struct {
18   int socket;
19   int pid; /* -1 if not initialized */
20 } Client;
21
22 GHashTable *clients = NULL;
23 GHashTable *new_clients = NULL;
24 Client *current_owner = NULL;
25
26 int master_socket;
27
28 int create_master_socket (void)
29 {
30   int fd;
31   struct sockaddr_un addr;
32
33   fd = socket (PF_UNIX, SOCK_STREAM, 0);
34
35   if (fd < 0) 
36     {
37       g_fprintf (stderr, "Error creating socket: %s\n", strerror(errno));
38       return -1;
39     }
40
41   unlink ("/tmp/.fb.manager");
42
43   addr.sun_family = AF_UNIX;
44   strcpy (addr.sun_path, "/tmp/.fb.manager");
45
46   if (bind(fd, (struct sockaddr *)&addr, sizeof (addr)) < 0)
47     {
48       g_fprintf (stderr, "Unable to bind socket: %s\n", strerror (errno));
49       close (fd);
50       return -1;
51     }
52   
53
54   if (listen (fd, 10) < 0)
55     {
56       g_fprintf (stderr, "Unable to listen on socket: %s\n", strerror (errno));
57       close (fd);
58       return -1;
59     }
60
61   master_socket = fd;
62   return 0;
63 }
64
65 void
66 handle_new_client (void)
67 {
68   int fd;
69   Client *client;
70   int true_val;
71
72   fd = accept (master_socket, NULL, NULL);
73   
74   client = g_new (Client, 1);
75   client->socket = fd;
76   client->pid = -1;
77
78   true_val = 1;
79   setsockopt (fd, SOL_SOCKET, SO_PASSCRED, 
80               &true_val, sizeof (true_val));
81
82   g_print ("Handling new client %p conntecting, fd = %d\n", client, fd);
83
84   g_hash_table_insert (new_clients, client, client);
85 }
86
87 struct fd_data
88 {
89   fd_set *read_fds;
90   fd_set *exception_fds;
91   int max_fd;
92 };
93
94 void 
95 send_message (Client *client, enum FBManagerMessageType type, int data)
96 {
97   struct FBManagerMessage msg;
98
99   msg.msg_type = type;
100   msg.data = data;
101
102   send (client->socket, &msg, sizeof (msg), 0);
103 }
104
105 gboolean
106 wait_for_ack (Client *client, int timeout_secs)
107 {
108   struct FBManagerMessage msg;
109   int res;
110   fd_set rfds;
111   struct timeval tv;
112
113   while (1)
114     {
115       FD_ZERO(&rfds);
116       FD_SET(client->socket, &rfds);
117       
118       tv.tv_sec = timeout_secs;
119       tv.tv_usec = 0;
120       
121       res = select (client->socket+1, &rfds, NULL, NULL, &tv);
122       
123       if (res == 0)
124         return FALSE;
125       
126       res = recv (client->socket, &msg, sizeof (msg), 0);
127       if (res != sizeof (msg))
128         return FALSE;
129       
130       if (msg.msg_type == FB_MANAGER_ACK)
131         return TRUE;
132     }
133 }
134
135 void
136 find_another_client (gpointer key,
137                      gpointer value,
138                      gpointer user_data)
139 {
140   Client **res;
141   Client *client;
142
143   res = user_data;
144   
145   if (*res)
146     return;
147
148   client = value;
149   if (client != current_owner)
150     *res = client;
151 }
152
153 void
154 switch_to_client (Client *client)
155 {
156   g_print ("Switch_to_client, client=%p, current_owner=%p\n", client, current_owner);
157
158   if ((current_owner == client) && (client != NULL))
159     return;
160
161   if (current_owner)
162     {
163       g_print ("switching from client fd=%d\n", current_owner->socket);
164       send_message (current_owner, FB_MANAGER_SWITCH_FROM, 0);
165       wait_for_ack (current_owner, 3);
166     }
167
168   current_owner = client;
169
170   if (current_owner)
171     {
172       g_print ("switching to client fd=%d\n", current_owner->socket);
173       send_message (current_owner, FB_MANAGER_SWITCH_TO, 0);
174     }
175 }
176
177 void
178 close_client (Client *client)
179 {
180   Client *other_client;
181   g_print ("Closing client %p (fd=%d)\n", 
182            client, client->socket);
183
184   if (current_owner == client)
185     {
186       other_client = NULL;
187       g_hash_table_foreach (clients,
188                             find_another_client,
189                             &other_client);
190       current_owner = NULL;
191       /* FIXME: This is a hack around the fact that the serial 
192          mouse driver had problems with opening and closing
193          the device almost at the same time. 
194       */
195       sleep (1);
196       switch_to_client (other_client);
197     }
198    
199   close (client->socket);
200   g_free (client);
201 }
202
203
204 /* Returns TRUE if the client was closed */
205 gboolean 
206 read_client_data (Client *client)
207 {
208   struct FBManagerMessage fb_message;
209   struct msghdr msg;
210   struct iovec iov;
211   char control_buffer[256];
212   struct cmsghdr *cmsg;
213   int res;
214   struct ucred *creds;
215   Client *new_client;
216
217   iov.iov_base = &fb_message;
218   iov.iov_len = sizeof (fb_message);
219
220   cmsg = (struct cmsghdr *)control_buffer;
221   msg.msg_name = NULL;
222   msg.msg_namelen = 0;
223   msg.msg_iov = &iov;
224   msg.msg_iovlen = 1;
225   msg.msg_control = cmsg;
226   msg.msg_controllen = 256;
227   msg.msg_flags = 0;
228   
229   g_print ("Reading client data:");
230   res = recvmsg (client->socket, &msg, 0);
231   g_print ("%d bytes, (error: %s)\n", res, 
232            strerror (errno));
233   
234   if (res < 0)
235     return FALSE;
236
237   if (res == 0) 
238     {
239       close_client (client);
240       return TRUE;
241     }
242
243   if (res != sizeof (fb_message))
244     {
245       g_warning ("Packet with wrong size %d received", res);
246       return FALSE;
247     }
248
249   switch (fb_message.msg_type) {
250   case FB_MANAGER_NEW_CLIENT:
251     if (client->pid != -1)
252       {
253         g_warning ("Got a NEW_CLIENT message from an old client");
254         return FALSE;
255       }
256     creds = NULL;
257     for (cmsg = CMSG_FIRSTHDR(&msg);
258          cmsg != NULL;
259          cmsg = CMSG_NXTHDR(&msg,cmsg))
260       {
261         if (cmsg->cmsg_level == SOL_SOCKET && 
262             cmsg->cmsg_type ==  SCM_CREDENTIALS) 
263           {
264             creds = (struct ucred *) CMSG_DATA(cmsg);
265             break;
266           }
267       }
268     if (creds == NULL) 
269       {
270         g_warning ("Got no credentials in NEW_CLIENT message");
271         close_client (client);
272         return TRUE;
273       }
274     client->pid = creds->pid;
275
276     g_hash_table_insert (clients, GINT_TO_POINTER (client->pid), client);
277
278     g_print ("New client connected. Pid=%d\n", (int)creds->pid);
279     return TRUE;
280     break;
281   case FB_MANAGER_REQUEST_SWITCH_TO_PID:
282     if (client->pid == -1)
283       {
284         g_warning ("Got a message from an uninitialized client");
285         return FALSE;
286       }
287
288     new_client = g_hash_table_lookup (clients, GINT_TO_POINTER (fb_message.data));
289     if (new_client)
290       switch_to_client (new_client);
291     else
292       g_warning ("Switchto unknown PID");
293     break;
294   case FB_MANAGER_ACK:
295     if (client->pid == -1)
296       {
297         g_warning ("Got a message from an uninitialized client");
298         return FALSE;
299       }
300     g_warning ("Got an unexpected ACK");
301     break;
302   default:
303     g_warning ("Got unknown package type %d", fb_message.msg_type);
304     break;
305   }
306   return FALSE;
307 }
308
309 /* Returns TRUE if the client was closed */
310 gboolean
311 handle_client_data (gpointer key,
312                     gpointer value,
313                     gpointer user_data)
314 {
315   Client *client;
316   struct fd_data *data;
317
318   client = value;
319   data = user_data;
320
321   if (FD_ISSET (client->socket, data->exception_fds))
322     {
323       close_client (client);
324       return TRUE;
325     }
326   else if (FD_ISSET (client->socket, data->read_fds))
327     {
328       return read_client_data (client);
329     }
330   
331   return FALSE;
332 }
333
334 void
335 set_fds (gpointer key,
336          gpointer value,
337          gpointer user_data)
338 {
339   struct fd_data *data;
340   Client *client;
341
342   client = value;
343   data = user_data;
344
345   FD_SET (client->socket, data->read_fds);
346   FD_SET (client->socket, data->exception_fds);
347   data->max_fd = MAX (data->max_fd, 
348                       client->socket);
349 }
350
351 void
352 main_loop (void)
353 {
354   fd_set read_fds;
355   fd_set exception_fds;
356   struct fd_data data;
357   int res;
358   
359   while (1)
360     {
361       FD_ZERO (&read_fds);
362       FD_ZERO (&exception_fds);
363       FD_SET (master_socket, &read_fds);
364
365       data.read_fds = &read_fds;
366       data.exception_fds = &exception_fds;
367       data.max_fd = master_socket;
368       
369       g_hash_table_foreach (clients,
370                             set_fds,
371                             &data);
372       g_hash_table_foreach (new_clients,
373                             set_fds,
374                             &data);
375       
376
377       res = select (data.max_fd+1, 
378                     &read_fds, NULL, &exception_fds, 
379                     NULL);
380
381       if (FD_ISSET (master_socket, &read_fds)) 
382         handle_new_client ();
383
384       g_hash_table_foreach_remove (clients,
385                                    handle_client_data,
386                                    &data);
387       g_hash_table_foreach_remove (new_clients,
388                                    handle_client_data,
389                                    &data);
390     }
391 }
392
393
394 int
395 main (int argc, char *argv[])
396 {
397   clients = g_hash_table_new (g_direct_hash,
398                               g_direct_equal);
399   new_clients = g_hash_table_new (g_direct_hash,
400                                   g_direct_equal);
401
402   create_master_socket ();
403
404   main_loop ();
405
406   return 0;
407 }