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