The client should give some state information to the server.
[gliv.git] / src / ipc.c
blob593b35b4e1fa01e310fda830befd5396783da313
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <guichaz@yahoo.fr>
21 /************************************************
22 * IPC between gliv processes to reuse a window *
23 ************************************************/
25 #include <unistd.h> /* unlink(), close(), write() */
26 #include <sys/types.h> /* struct sockaddr */
27 #include <sys/socket.h> /* socket(), ... */
28 #include <sys/un.h> /* AF_LOCAL */
29 #include <string.h> /* strncpy() */
30 #include <stdio.h> /* perror(), fread(), stdin */
31 #include <sys/stat.h>
33 #include "gliv.h"
34 #include "ipc.h"
35 #include "cmdline.h"
36 #include "files_list.h"
37 #include "next_image.h"
39 static GIOChannel *make_channel(gint fd)
41 GIOChannel *channel;
42 GError *err = NULL;
44 channel = g_io_channel_unix_new(fd);
45 g_io_channel_set_close_on_unref(channel, TRUE);
46 g_io_channel_set_encoding(channel, NULL, &err);
47 if (err != NULL) {
48 g_printerr("%s\n", err->message);
49 g_error_free(err);
50 g_io_channel_unref(channel);
51 channel = NULL;
54 return channel;
57 /*** Where do we keep this socket? ***/
59 static gchar *get_socket_name(void)
61 gchar *user_name, *repl, *res;
63 /* Do people have '/' in their user names. */
64 repl = user_name = g_strdup(g_get_user_name());
66 while ((repl = strchr(repl, '/')) != NULL)
67 *repl = '_';
69 res = g_strdup_printf("%s/.19830128gliv_%s", g_get_tmp_dir(), user_name);
70 g_free(user_name);
72 return res;
75 /*** State encoding by the client for the server. ***/
77 #define PARAM(func, param) \
78 do { \
79 gsize bytes = 0; \
80 GError *err = NULL; \
82 func(channel, (gpointer) param, sizeof(gboolean), &bytes, &err); \
83 if (bytes != sizeof(gboolean) && err != NULL) { \
84 g_printerr("%s\n", err->message); \
85 g_error_free(err); \
86 return FALSE; \
87 } \
88 } while (0)
90 static gboolean write_params(GIOChannel * channel, gboolean recursive,
91 gboolean shuffle, gboolean sort)
93 gchar *current_dir;
94 gint length;
95 gboolean res = TRUE;
96 gsize bytes = 0;
97 GError *err = NULL;
99 PARAM(g_io_channel_write_chars, &recursive);
100 PARAM(g_io_channel_write_chars, &shuffle);
101 PARAM(g_io_channel_write_chars, &sort);
103 current_dir = g_get_current_dir();
104 length = strlen(current_dir) + 1;
106 g_io_channel_write_chars(channel, current_dir, length, &bytes, &err);
107 if (bytes != length) {
108 res = FALSE;
109 if (err != NULL) {
110 g_printerr("%s\n", err->message);
111 g_error_free(err);
115 g_free(current_dir);
117 return res;
120 static gboolean read_params(GIOChannel * channel, gchar ** client_dir,
121 gboolean * recursive,
122 gboolean * shuffle, gboolean * sort)
124 size_t size = 0;
125 gchar *server_dir;
126 struct stat client_stat, server_stat;
127 GError *err = NULL;
129 PARAM(g_io_channel_read_chars, recursive);
130 PARAM(g_io_channel_read_chars, shuffle);
131 PARAM(g_io_channel_read_chars, sort);
133 *client_dir = NULL;
134 g_io_channel_read_line(channel, client_dir, &size, NULL, &err);
135 if (err != NULL) {
136 g_printerr("%s\n", err->message);
137 g_error_free(err);
138 return FALSE;
141 server_dir = g_get_current_dir();
142 if (stat(*client_dir, &client_stat) < 0 ||
143 stat(server_dir, &server_stat) < 0) {
145 g_free(server_dir);
146 g_free(*client_dir);
147 return FALSE;
150 if (client_stat.st_dev == server_stat.st_dev &&
151 client_stat.st_ino == server_stat.st_ino) {
153 * The client and server are in the same directory, we can use relative
154 * filenames without problems.
156 g_free(*client_dir);
157 *client_dir = NULL;
160 g_free(server_dir);
161 return TRUE;
164 /*** Server ***/
166 static gint create_server_socket(void)
168 struct sockaddr_un name;
169 gint sock;
170 gchar *filename;
171 size_t size;
173 sock = socket(PF_LOCAL, SOCK_STREAM, 0);
174 if (sock < 0) {
175 perror("socket");
176 return -1;
179 name.sun_family = AF_LOCAL;
181 filename = get_socket_name();
182 strncpy(name.sun_path, filename, sizeof(name.sun_path) - 1);
183 name.sun_path[sizeof(name.sun_path) - 1] = '\0';
185 /* The latest launched gliv is the server. */
186 unlink(filename);
188 g_free(filename);
189 size = SUN_LEN(&name);
191 if (bind(sock, (struct sockaddr *) &name, size) < 0) {
192 perror("bind");
193 close(sock);
194 sock = -1;
195 } else if (listen(sock, 5) < 0) {
196 perror("listen");
197 close(sock);
198 sock = -1;
201 return sock;
204 /* Put the '\0' separated strings in an array. */
205 static gchar **rebuild_args_array(gchar * data, gint length, gint * count,
206 gchar * client_dir)
208 gint i;
209 gchar *last = data + length, *ptr = data;
210 gchar **tab;
212 /* How many strings? */
213 *count = 0;
214 while (ptr < last) {
215 ptr += strlen(ptr) + 1;
216 (*count)++;
219 /* Cut them. */
220 tab = g_new(gchar *, *count);
221 for (i = 0; i < *count; i++) {
222 if (client_dir == NULL || data[0] == '/')
223 tab[i] = g_strdup(data);
224 else
225 tab[i] = g_build_filename(client_dir, data, NULL);
227 data += strlen(data) + 1;
230 return tab;
233 /* Add the filenames and open the next image. */
234 static void process_data(gchar * data, gint length, gchar * client_dir,
235 gboolean recursive, gboolean shuffle, gboolean sort)
237 gint i, count, nb_inserted;
238 gchar **tab;
240 tab = rebuild_args_array(data, length, &count, client_dir);
241 nb_inserted = insert_after_current(tab, count, recursive, shuffle, sort);
243 if (nb_inserted > 0)
244 open_next_image(nb_inserted == 1);
246 for (i = 0; i < count; i++)
247 g_free(tab[i]);
249 g_free(tab);
252 /* Called when there is something to read. */
253 static gboolean handle_connect(GIOChannel * source, GIOCondition unused1,
254 gpointer unused2)
256 gchar *data;
257 gint length = 0, fd;
258 GError *err = NULL;
259 GIOChannel *channel;
260 gboolean recursive, shuffle, sort;
261 gchar *client_dir;
263 fd = accept(g_io_channel_unix_get_fd(source), NULL, &length);
264 if (fd < 0) {
265 perror("accept");
266 return TRUE;
269 channel = make_channel(fd);
270 if (channel == NULL)
271 return TRUE;
273 /* Fill the flags. */
274 if (read_params(channel, &client_dir, &recursive, &shuffle, &sort) == FALSE) {
275 g_io_channel_unref(channel);
276 return TRUE;
279 g_io_channel_read_to_end(channel, &data, &length, &err);
280 if (err != NULL) {
281 g_printerr("%s\n", err->message);
282 g_error_free(err);
283 g_io_channel_unref(channel);
284 return TRUE;
287 process_data(data, length, client_dir, recursive, shuffle, sort);
288 g_io_channel_unref(channel);
289 return TRUE;
292 void start_server(void)
294 gint socket;
295 GIOChannel *channel;
297 socket = create_server_socket();
298 if (socket < 0)
299 return;
301 channel = make_channel(socket);
302 if (channel == NULL)
303 return;
305 /* Let GTK+ inform us about clients. */
306 g_io_add_watch(channel, G_IO_IN, (GIOFunc) handle_connect, NULL);
309 /*** Client ***/
311 static gint create_client_socket(void)
313 struct sockaddr_un name;
314 gint sock;
315 gchar *filename;
316 size_t size;
318 sock = socket(PF_LOCAL, SOCK_STREAM, 0);
319 if (sock < 0) {
320 perror("socket");
321 return -1;
324 name.sun_family = AF_LOCAL;
326 filename = get_socket_name();
327 strncpy(name.sun_path, filename, sizeof(name.sun_path) - 1);
328 name.sun_path[sizeof(name.sun_path) - 1] = '\0';
329 g_free(filename);
331 size = SUN_LEN(&name);
332 if (connect(sock, (struct sockaddr *) &name, size) < 0) {
333 perror("connect");
334 close(sock);
335 sock = -1;
338 return sock;
341 /* Used when we read the filenames from stdin. */
342 static void send_filenames(GIOChannel * channel)
344 gchar buffer[4096];
345 gint size;
347 while ((size = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
348 g_io_channel_write_chars(channel, buffer, size, NULL, NULL);
351 gboolean connect_server(gint argc, gchar ** argv)
353 gint fd, i;
354 struct gengetopt_args_info ggo;
355 GIOChannel *channel;
357 fd = create_client_socket();
358 if (fd < 0)
359 return FALSE;
361 cmdline_parser(argc, argv, &ggo);
363 channel = make_channel(fd);
364 if (channel == NULL)
365 return TRUE;
367 if (write_params(channel, ggo.recursive_flag,
368 ggo.shuffle_flag, ggo.sort_flag) == FALSE) {
369 g_io_channel_unref(channel);
370 return FALSE;
373 if (ggo.null_flag)
374 send_filenames(channel);
376 for (i = 0; i < ggo.inputs_num; i++) {
377 gsize size;
378 gint length = strlen(ggo.inputs[i]);
379 GError *err = NULL;
381 g_io_channel_write_chars(channel, ggo.inputs[i], length, &size, &err);
382 if (err != NULL) {
383 g_printerr("%s\n", err->message);
384 g_error_free(err);
385 g_io_channel_unref(channel);
386 return TRUE;
390 g_io_channel_unref(channel);
391 return TRUE;