Handle the newline in DEBUG_N() macro instead of adding one
[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.");
101                 DEBUG_1("client count %d", g_list_length(rc->clients));
102                 
103                 g_source_remove(client->channel_id);
104                 close(client->fd);
105                 g_free(client);
106                 }
107
108         return TRUE;
109 }
110
111 static void remote_server_client_add(RemoteConnection *rc, int fd)
112 {
113         RemoteClient *client;
114         GIOChannel *channel;
115
116         if (g_list_length(rc->clients) > SERVER_MAX_CLIENTS)
117                 {
118                 printf("maximum remote clients of %d exceeded, closing connection\n", SERVER_MAX_CLIENTS);
119                 close(fd);
120                 return;
121                 }
122
123         client = g_new0(RemoteClient, 1);
124         client->rc = rc;
125         client->fd = fd;
126
127         channel = g_io_channel_unix_new(fd);
128         client->channel_id = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP,
129                                                  remote_server_client_cb, client, NULL);
130         g_io_channel_unref(channel);
131
132         rc->clients = g_list_append(rc->clients, client);
133         DEBUG_1("client count %d", g_list_length(rc->clients));
134 }
135
136 static void remote_server_clients_close(RemoteConnection *rc)
137 {
138         while (rc->clients)
139                 {
140                 RemoteClient *client = rc->clients->data;
141
142                 rc->clients = g_list_remove(rc->clients, client);
143
144                 g_source_remove(client->channel_id);
145                 close(client->fd);
146                 g_free(client);
147                 }
148 }
149
150 static gboolean remote_server_read_cb(GIOChannel *source, GIOCondition condition, gpointer data)
151 {
152         RemoteConnection *rc = data;
153         int fd;
154         unsigned int alen;
155
156         fd = accept(rc->fd, NULL, &alen);
157         if (fd == -1)
158                 {
159                 printf("error accepting socket: %s\n", strerror(errno));
160                 return TRUE;
161                 }
162
163         remote_server_client_add(rc, fd);
164
165         return TRUE;
166 }
167
168 static gint remote_server_exists(const gchar *path)
169 {
170         RemoteConnection *rc;
171
172         /* verify server up */
173         rc = remote_client_open(path);
174         remote_close(rc);
175
176         if (rc) return TRUE;
177
178         /* unable to connect, remove socket file to free up address */
179         unlink(path);
180         return FALSE;
181 }
182
183 RemoteConnection *remote_server_open(const gchar *path)
184 {
185         RemoteConnection *rc;
186         struct sockaddr_un addr;
187         gint sun_path_len;
188         int fd;
189         GIOChannel *channel;
190
191         if (remote_server_exists(path))
192                 {
193                 printf("Address already in use: %s\n", path);
194                 return NULL;
195                 }
196
197         fd = socket(PF_UNIX, SOCK_STREAM, 0);
198         if (fd == -1) return NULL;
199
200         addr.sun_family = AF_UNIX;
201         sun_path_len = MIN(strlen(path) + 1, UNIX_PATH_MAX);
202         strncpy(addr.sun_path, path, sun_path_len);
203         if (bind(fd, &addr, sizeof(addr)) == -1 ||
204             listen(fd, REMOTE_SERVER_BACKLOG) == -1)
205                 {
206                 printf("error subscribing to socket: %s\n", strerror(errno));
207                 close(fd);
208                 return NULL;
209                 }
210
211         rc = g_new0(RemoteConnection, 1);
212         rc->server = TRUE;
213         rc->fd = fd;
214         rc->path = g_strdup(path);
215
216         rc->read_func = NULL;
217         rc->read_data = NULL;
218
219         rc->clients = NULL;
220
221         channel = g_io_channel_unix_new(rc->fd);
222         rc->channel_id = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN,
223                                              remote_server_read_cb, rc, NULL);
224         g_io_channel_unref(channel);
225
226         return rc;
227 }
228
229 void remote_server_subscribe(RemoteConnection *rc, RemoteReadFunc *func, gpointer data)
230 {
231         if (!rc || !rc->server) return;
232
233         rc->read_func = func;
234         rc->read_data = data;
235 }
236
237
238 RemoteConnection *remote_client_open(const gchar *path)
239 {
240         RemoteConnection *rc;
241         struct stat st;
242         struct sockaddr_un addr;
243         gint sun_path_len;
244         int fd;
245
246         if (stat(path, &st) != 0 || !S_ISSOCK(st.st_mode)) return NULL;
247
248         fd = socket(PF_UNIX, SOCK_STREAM, 0);
249         if (fd == -1) return NULL;
250
251         addr.sun_family = AF_UNIX;
252         sun_path_len = MIN(strlen(path) + 1, UNIX_PATH_MAX);
253         strncpy(addr.sun_path, path, sun_path_len);
254         if (connect(fd, &addr, sizeof(addr)) == -1)
255                 {
256                 DEBUG_1("error connecting to socket: %s", strerror(errno));
257                 close(fd);
258                 return NULL;
259                 }
260
261         rc = g_new0(RemoteConnection, 1);
262         rc->server = FALSE;
263         rc->fd = fd;
264         rc->path = g_strdup(path);
265
266         /* this might fix the freezes on freebsd, solaris, etc. - completely untested */
267         remote_client_send(rc, "\n");
268
269         return rc;
270 }
271
272 static sig_atomic_t sigpipe_occured = FALSE;
273
274 static void sighandler_sigpipe(int sig)
275 {
276         sigpipe_occured = TRUE;
277 }
278
279 gint remote_client_send(RemoteConnection *rc, const gchar *text)
280 {
281         struct sigaction new_action, old_action;
282         gint ret = FALSE;
283
284         if (!rc || rc->server) return FALSE;
285         if (!text) return TRUE;
286
287         sigpipe_occured = FALSE;
288
289         new_action.sa_handler = sighandler_sigpipe;
290         sigemptyset (&new_action.sa_mask);
291         new_action.sa_flags = 0;
292
293         /* setup our signal handler */
294         sigaction (SIGPIPE, &new_action, &old_action);
295
296         if (write(rc->fd, text, strlen(text)) == -1 ||
297             write(rc->fd, "\n", 1) == -1)
298                 {
299                 if (sigpipe_occured)
300                         {
301                         printf("SIGPIPE writing to socket: %s\n", rc->path);
302                         }
303                 else
304                         {
305                         printf("error writing to socket: %s\n", strerror(errno));
306                         }
307                 ret = FALSE;;
308                 }
309         else
310                 {
311                 ret = TRUE;
312                 }
313
314         /* restore the original signal handler */
315         sigaction (SIGPIPE, &old_action, NULL);
316
317         return ret;
318 }
319
320 void remote_close(RemoteConnection *rc)
321 {
322         if (!rc) return;
323
324         if (rc->server)
325                 {
326                 remote_server_clients_close(rc);
327
328                 g_source_remove(rc->channel_id);
329                 unlink(rc->path);
330                 }
331
332         close(rc->fd);
333
334         g_free(rc->path);
335         g_free(rc);
336 }