Move debug macros from main.h to new debug.h.
[geeqie.git] / src / remote.c
1 /*
2  * Geeqie
3  * (C) 2004 John Ellis
4  * Copyright (C) 2008 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13
14 #include "main.h"
15 #include "remote.h"
16
17 #include "debug.h"
18
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/un.h>
22 #include <signal.h>
23 #include <errno.h>
24
25
26 #define SERVER_MAX_CLIENTS 8
27
28 #define REMOTE_SERVER_BACKLOG 4
29
30
31 #ifndef UNIX_PATH_MAX
32 #define UNIX_PATH_MAX 108
33 #endif
34
35
36 typedef struct _RemoteClient RemoteClient;
37 struct _RemoteClient {
38         gint fd;
39         gint channel_id;
40         RemoteConnection *rc;
41 };
42
43
44 static gboolean remote_server_client_cb(GIOChannel *source, GIOCondition condition, gpointer data)
45 {
46         RemoteClient *client = data;
47         RemoteConnection *rc;
48
49         rc = client->rc;
50
51         if (condition & G_IO_IN)
52                 {
53                 GList *queue = NULL;
54                 GList *work;
55                 gchar *buffer = NULL;
56                 GError *error = NULL;
57                 guint termpos;
58
59                 while (g_io_channel_read_line(source, &buffer, NULL, &termpos, &error) == G_IO_STATUS_NORMAL)
60                         {
61                         if (buffer)
62                                 {
63                                 buffer[termpos] = '\0';
64
65                                 if (strlen(buffer) > 0)
66                                         {
67                                         queue = g_list_append(queue, buffer);
68                                         }
69                                 else
70                                         {
71                                         g_free(buffer);
72                                         }
73
74                                 buffer = NULL;
75                                 }
76                         }
77
78                 if (error)
79                         {
80                         printf("error reading socket: %s\n", error->message);
81                         g_error_free(error);
82                         }
83
84                 work = queue;
85                 while (work)
86                         {
87                         gchar *command = work->data;
88                         work = work->next;
89
90                         if (rc->read_func) rc->read_func(rc, command, rc->read_data);
91                         g_free(command);
92                         }
93
94                 g_list_free(queue);
95                 }
96
97         if (condition & G_IO_HUP)
98                 {
99                 rc->clients = g_list_remove(rc->clients, client);
100
101                 DEBUG_1("HUP detected, closing client.");
102                 DEBUG_1("client count %d", g_list_length(rc->clients));
103                 
104                 g_source_remove(client->channel_id);
105                 close(client->fd);
106                 g_free(client);
107                 }
108
109         return TRUE;
110 }
111
112 static void remote_server_client_add(RemoteConnection *rc, int fd)
113 {
114         RemoteClient *client;
115         GIOChannel *channel;
116
117         if (g_list_length(rc->clients) > SERVER_MAX_CLIENTS)
118                 {
119                 printf("maximum remote clients of %d exceeded, closing connection\n", SERVER_MAX_CLIENTS);
120                 close(fd);
121                 return;
122                 }
123
124         client = g_new0(RemoteClient, 1);
125         client->rc = rc;
126         client->fd = fd;
127
128         channel = g_io_channel_unix_new(fd);
129         client->channel_id = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP,
130                                                  remote_server_client_cb, client, NULL);
131         g_io_channel_unref(channel);
132
133         rc->clients = g_list_append(rc->clients, client);
134         DEBUG_1("client count %d", g_list_length(rc->clients));
135 }
136
137 static void remote_server_clients_close(RemoteConnection *rc)
138 {
139         while (rc->clients)
140                 {
141                 RemoteClient *client = rc->clients->data;
142
143                 rc->clients = g_list_remove(rc->clients, client);
144
145                 g_source_remove(client->channel_id);
146                 close(client->fd);
147                 g_free(client);
148                 }
149 }
150
151 static gboolean remote_server_read_cb(GIOChannel *source, GIOCondition condition, gpointer data)
152 {
153         RemoteConnection *rc = data;
154         int fd;
155         unsigned int alen;
156
157         fd = accept(rc->fd, NULL, &alen);
158         if (fd == -1)
159                 {
160                 printf("error accepting socket: %s\n", strerror(errno));
161                 return TRUE;
162                 }
163
164         remote_server_client_add(rc, fd);
165
166         return TRUE;
167 }
168
169 static gint remote_server_exists(const gchar *path)
170 {
171         RemoteConnection *rc;
172
173         /* verify server up */
174         rc = remote_client_open(path);
175         remote_close(rc);
176
177         if (rc) return TRUE;
178
179         /* unable to connect, remove socket file to free up address */
180         unlink(path);
181         return FALSE;
182 }
183
184 RemoteConnection *remote_server_open(const gchar *path)
185 {
186         RemoteConnection *rc;
187         struct sockaddr_un addr;
188         gint sun_path_len;
189         int fd;
190         GIOChannel *channel;
191
192         if (remote_server_exists(path))
193                 {
194                 printf("Address already in use: %s\n", path);
195                 return NULL;
196                 }
197
198         fd = socket(PF_UNIX, SOCK_STREAM, 0);
199         if (fd == -1) return NULL;
200
201         addr.sun_family = AF_UNIX;
202         sun_path_len = MIN(strlen(path) + 1, UNIX_PATH_MAX);
203         strncpy(addr.sun_path, path, sun_path_len);
204         if (bind(fd, &addr, sizeof(addr)) == -1 ||
205             listen(fd, REMOTE_SERVER_BACKLOG) == -1)
206                 {
207                 printf("error subscribing to socket: %s\n", strerror(errno));
208                 close(fd);
209                 return NULL;
210                 }
211
212         rc = g_new0(RemoteConnection, 1);
213         rc->server = TRUE;
214         rc->fd = fd;
215         rc->path = g_strdup(path);
216
217         rc->read_func = NULL;
218         rc->read_data = NULL;
219
220         rc->clients = NULL;
221
222         channel = g_io_channel_unix_new(rc->fd);
223         rc->channel_id = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN,
224                                              remote_server_read_cb, rc, NULL);
225         g_io_channel_unref(channel);
226
227         return rc;
228 }
229
230 void remote_server_subscribe(RemoteConnection *rc, RemoteReadFunc *func, gpointer data)
231 {
232         if (!rc || !rc->server) return;
233
234         rc->read_func = func;
235         rc->read_data = data;
236 }
237
238
239 RemoteConnection *remote_client_open(const gchar *path)
240 {
241         RemoteConnection *rc;
242         struct stat st;
243         struct sockaddr_un addr;
244         gint sun_path_len;
245         int fd;
246
247         if (stat(path, &st) != 0 || !S_ISSOCK(st.st_mode)) return NULL;
248
249         fd = socket(PF_UNIX, SOCK_STREAM, 0);
250         if (fd == -1) return NULL;
251
252         addr.sun_family = AF_UNIX;
253         sun_path_len = MIN(strlen(path) + 1, UNIX_PATH_MAX);
254         strncpy(addr.sun_path, path, sun_path_len);
255         if (connect(fd, &addr, sizeof(addr)) == -1)
256                 {
257                 DEBUG_1("error connecting to socket: %s", strerror(errno));
258                 close(fd);
259                 return NULL;
260                 }
261
262         rc = g_new0(RemoteConnection, 1);
263         rc->server = FALSE;
264         rc->fd = fd;
265         rc->path = g_strdup(path);
266
267         /* this might fix the freezes on freebsd, solaris, etc. - completely untested */
268         remote_client_send(rc, "\n");
269
270         return rc;
271 }
272
273 static sig_atomic_t sigpipe_occured = FALSE;
274
275 static void sighandler_sigpipe(int sig)
276 {
277         sigpipe_occured = TRUE;
278 }
279
280 gint remote_client_send(RemoteConnection *rc, const gchar *text)
281 {
282         struct sigaction new_action, old_action;
283         gint ret = FALSE;
284
285         if (!rc || rc->server) return FALSE;
286         if (!text) return TRUE;
287
288         sigpipe_occured = FALSE;
289
290         new_action.sa_handler = sighandler_sigpipe;
291         sigemptyset (&new_action.sa_mask);
292         new_action.sa_flags = 0;
293
294         /* setup our signal handler */
295         sigaction (SIGPIPE, &new_action, &old_action);
296
297         if (write(rc->fd, text, strlen(text)) == -1 ||
298             write(rc->fd, "\n", 1) == -1)
299                 {
300                 if (sigpipe_occured)
301                         {
302                         printf("SIGPIPE writing to socket: %s\n", rc->path);
303                         }
304                 else
305                         {
306                         printf("error writing to socket: %s\n", strerror(errno));
307                         }
308                 ret = FALSE;;
309                 }
310         else
311                 {
312                 ret = TRUE;
313                 }
314
315         /* restore the original signal handler */
316         sigaction (SIGPIPE, &old_action, NULL);
317
318         return ret;
319 }
320
321 void remote_close(RemoteConnection *rc)
322 {
323         if (!rc) return;
324
325         if (rc->server)
326                 {
327                 remote_server_clients_close(rc);
328
329                 g_source_remove(rc->channel_id);
330                 unlink(rc->path);
331                 }
332
333         close(rc->fd);
334
335         g_free(rc->path);
336         g_free(rc);
337 }