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