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