Mercurial > geeqie.yaz
annotate src/remote.c @ 637:8a1202efa51e
Add some wrapper functions for not yet common code.
author | zas_ |
---|---|
date | Mon, 12 May 2008 00:22:38 +0000 |
parents | f9bf33be53ff |
children | 3097880d7d95 |
rev | line source |
---|---|
9 | 1 /* |
196 | 2 * Geeqie |
9 | 3 * (C) 2004 John Ellis |
475 | 4 * Copyright (C) 2008 The Geeqie Team |
9 | 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 | |
281 | 14 #include "main.h" |
9 | 15 #include "remote.h" |
16 | |
507 | 17 #include "debug.h" |
9 | 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; | |
64
04ff0df3ad2f
Mon Aug 15 17:13:57 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
57 guint termpos; |
9 | 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 | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
101 DEBUG_1("HUP detected, closing client."); |
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
102 DEBUG_1("client count %d", g_list_length(rc->clients)); |
495 | 103 |
9 | 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); | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
134 DEBUG_1("client count %d", g_list_length(rc->clients)); |
9 | 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; | |
64
04ff0df3ad2f
Mon Aug 15 17:13:57 2005 John Ellis <johne@verizon.net>
gqview
parents:
9
diff
changeset
|
155 unsigned int alen; |
9 | 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) | |
442 | 256 { |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
257 DEBUG_1("error connecting to socket: %s", strerror(errno)); |
442 | 258 close(fd); |
259 return NULL; | |
260 } | |
9 | 261 |
262 rc = g_new0(RemoteConnection, 1); | |
263 rc->server = FALSE; | |
264 rc->fd = fd; | |
265 rc->path = g_strdup(path); | |
266 | |
371
7997b6704fdb
this might fix the freezes on freebsd, solaris, etc.
nadvornik
parents:
281
diff
changeset
|
267 /* this might fix the freezes on freebsd, solaris, etc. - completely untested */ |
442 | 268 remote_client_send(rc, "\n"); |
371
7997b6704fdb
this might fix the freezes on freebsd, solaris, etc.
nadvornik
parents:
281
diff
changeset
|
269 |
9 | 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; | |
512
f9bf33be53ff
Remove whitespace between function name and first parenthesis for the sake of consistency.
zas_
parents:
507
diff
changeset
|
291 sigemptyset(&new_action.sa_mask); |
9 | 292 new_action.sa_flags = 0; |
293 | |
294 /* setup our signal handler */ | |
512
f9bf33be53ff
Remove whitespace between function name and first parenthesis for the sake of consistency.
zas_
parents:
507
diff
changeset
|
295 sigaction(SIGPIPE, &new_action, &old_action); |
9 | 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 */ | |
512
f9bf33be53ff
Remove whitespace between function name and first parenthesis for the sake of consistency.
zas_
parents:
507
diff
changeset
|
316 sigaction(SIGPIPE, &old_action, NULL); |
9 | 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 } |