source: trunk/gtk/ipc.c @ 249

Last change on this file since 249 was 249, checked in by joshe, 16 years ago

Some minor code cleanups.
Handle things a little better when quitting.

File size: 11.1 KB
Line 
1/*
2  Copyright (c) 2006 Joshua Elsasser. All rights reserved.
3   
4  Redistribution and use in source and binary forms, with or without
5  modification, are permitted provided that the following conditions
6  are met:
7   
8   1. Redistributions of source code must retain the above copyright
9      notice, this list of conditions and the following disclaimer.
10   2. Redistributions in binary form must reproduce the above copyright
11      notice, this list of conditions and the following disclaimer in the
12      documentation and/or other materials provided with the distribution.
13   
14  THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS "AS IS" AND
15  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  POSSIBILITY OF SUCH DAMAGE.
25*/
26
27#include <sys/types.h>
28#include <sys/socket.h>
29#include <sys/un.h>
30#include <errno.h>
31#include <fcntl.h>
32#include <stdlib.h>
33#include <string.h>
34#include <unistd.h>
35
36#include <gtk/gtk.h>
37#include <glib/gi18n.h>
38
39#include "transmission.h"
40#include "bencode.h"
41
42#include "conf.h"
43#include "io.h"
44#include "ipc.h"
45#include "util.h"
46
47#define PROTOVERS               1       /* IPC protocol version */
48
49/* int, IPC protocol version */
50#define MSG_VERSION             ("version")
51/* list of strings, full paths to torrent files to load */
52#define MSG_ADDFILES            ("addfiles")
53
54enum contype { CON_SERV, CON_ADDFILE };
55
56struct constate_serv {
57  void *wind;
58  add_torrents_func_t addfunc;
59  void *cbdata;
60};
61
62struct constate_addfile {
63  GMainLoop *loop;
64  GList *files;
65  gboolean *succeeded;
66  unsigned int addid;
67};
68
69struct constate;
70typedef void (*handler_func_t)(struct constate*, const char*, benc_val_t *);
71struct handlerdef {char *name; handler_func_t func;};
72
73struct constate {
74  GSource *source;
75  int fd;
76  const struct handlerdef *funcs;
77  enum contype type;
78  union {
79    struct constate_serv serv;
80    struct constate_addfile addfile;
81  } u;
82};
83
84void
85ipc_socket_setup(void *parent, add_torrents_func_t addfunc, void *cbdata);
86gboolean
87ipc_sendfiles_blocking(GList *files);
88static void
89serv_bind(struct constate *con);
90static void
91rmsock(void);
92static gboolean
93client_connect(char *path, struct constate *con);
94static void
95srv_io_accept(GSource *source, int fd, struct sockaddr *sa, socklen_t len,
96              void *vdata);
97static int
98send_msg(struct constate *con, const char *name, benc_val_t *val);
99static int
100send_msg_int(struct constate *con, const char *name, int num);
101static unsigned int
102all_io_received(GSource *source, char *data, unsigned int len, void *vdata);
103static void
104destroycon(struct constate *con);
105static void
106all_io_closed(GSource *source, void *vdata);
107static void
108srv_addfile(struct constate *con, const char *name, benc_val_t *val);
109static void
110afc_version(struct constate *con, const char *name, benc_val_t *val);
111static void
112afc_io_sent(GSource *source, unsigned int id, void *vdata);
113
114static const struct handlerdef gl_funcs_serv[] = {
115  {MSG_ADDFILES, srv_addfile},
116  {NULL, NULL}
117};
118
119static const struct handlerdef gl_funcs_addfile[] = {
120  {MSG_VERSION, afc_version},
121  {NULL, NULL}
122};
123
124/* this is only used on the server */
125static char *gl_sockpath = NULL;
126
127void
128ipc_socket_setup(void *parent, add_torrents_func_t addfunc, void *cbdata) {
129  struct constate *con;
130
131  con = g_new0(struct constate, 1);
132  con->source = NULL;
133  con->fd = -1;
134  con->funcs = gl_funcs_serv;
135  con->type = CON_SERV;
136  con->u.serv.wind = parent;
137  con->u.serv.addfunc = addfunc;
138  con->u.serv.cbdata = cbdata;
139 
140  serv_bind(con);
141}
142
143gboolean
144ipc_sendfiles_blocking(GList *files) {
145  struct constate *con;
146  char *path;
147  gboolean ret = FALSE;
148
149  con = g_new0(struct constate, 1);
150  con->source = NULL;
151  con->fd = -1;
152  con->funcs = gl_funcs_addfile;
153  con->type = CON_ADDFILE;
154  con->u.addfile.loop = g_main_loop_new(NULL, TRUE);
155  con->u.addfile.files = files;
156  con->u.addfile.succeeded = &ret;
157  con->u.addfile.addid = 0;
158
159  path = cf_sockname();
160  if(!client_connect(path, con)) {
161    g_free(path);
162    destroycon(con);
163    return FALSE;
164  }
165
166  g_main_loop_run(con->u.addfile.loop);
167
168  return ret;
169}
170
171/* open a local socket for clients connections */
172static void
173serv_bind(struct constate *con) {
174  struct sockaddr_un sa;
175
176  rmsock();
177  gl_sockpath = cf_sockname();
178
179  if(0 > (con->fd = socket(AF_LOCAL, SOCK_STREAM, 0)))
180    goto fail;
181
182  bzero(&sa, sizeof(sa));
183  sa.sun_family = AF_LOCAL;
184  strncpy(sa.sun_path, gl_sockpath, sizeof(sa.sun_path) - 1);
185
186  /* unlink any existing socket file before trying to create ours */
187  unlink(gl_sockpath);
188  if(0 > bind(con->fd, (struct sockaddr *)&sa, SUN_LEN(&sa))) {
189    /* bind may fail if there was already a socket, so try twice */
190    unlink(gl_sockpath);
191    if(0 > bind(con->fd, (struct sockaddr *)&sa, SUN_LEN(&sa)))
192      goto fail;
193  }
194
195  if(0 > listen(con->fd, 5))
196    goto fail;
197
198  con->source = io_new_listening(con->fd, sizeof(struct sockaddr_un),
199                                 srv_io_accept, all_io_closed, con);
200
201  g_atexit(rmsock);
202
203  return;
204
205 fail:
206  errmsg(con->u.serv.wind, _("Failed to set up socket:\n%s"),
207         strerror(errno));
208  if(0 <= con->fd)
209    close(con->fd);
210  con->fd = -1;
211  rmsock();
212}
213
214static void
215rmsock(void) {
216  if(NULL != gl_sockpath) {
217    unlink(gl_sockpath);
218    g_free(gl_sockpath);
219  }
220}
221
222static gboolean
223client_connect(char *path, struct constate *con) {
224  struct sockaddr_un addr;
225
226  if(0 > (con->fd = socket(AF_UNIX, SOCK_STREAM, 0))) {
227    fprintf(stderr, _("failed to create socket: %s\n"), strerror(errno));
228    return FALSE;
229  }
230
231  bzero(&addr, sizeof(addr));
232  addr.sun_family = AF_UNIX;
233  strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
234
235  if(0 > connect(con->fd, (struct sockaddr*)&addr, SUN_LEN(&addr))) {
236    fprintf(stderr, _("failed to connect to %s: %s\n"), path, strerror(errno));
237    return FALSE;
238  }
239
240  con->source = io_new(con->fd, afc_io_sent, all_io_received,
241                       all_io_closed, con);
242
243  return TRUE;
244}
245
246static void
247srv_io_accept(GSource *source SHUTUP, int fd, struct sockaddr *sa SHUTUP,
248              socklen_t len SHUTUP, void *vdata) {
249  struct constate *con = vdata;
250  struct constate *newcon;
251
252  newcon = g_new(struct constate, 1);
253  memcpy(newcon, con, sizeof(*newcon));
254  newcon->fd = fd;
255  newcon->source = io_new(fd, NULL, all_io_received, all_io_closed, newcon);
256
257  if(NULL != newcon->source)
258    send_msg_int(newcon, MSG_VERSION, PROTOVERS);
259  else {
260    g_free(newcon);
261    close(fd);
262  }
263}
264
265static int
266send_msg(struct constate *con, const char *name, benc_val_t *val) {
267  char *buf;
268  size_t used, total;
269  benc_val_t dict;
270  char stupid;
271
272  /* construct a dictionary value */
273  /* XXX I shouldn't be constructing benc_val_t's by hand */
274  bzero(&dict, sizeof(dict));
275  dict.type = TYPE_DICT;
276  dict.val.l.alloc = 2;
277  dict.val.l.count = 2;
278  dict.val.l.vals = g_new0(benc_val_t, 2);
279  dict.val.l.vals[0].type = TYPE_STR;
280  dict.val.l.vals[0].val.s.i = strlen(name);
281  dict.val.l.vals[0].val.s.s = (char*)name;
282  dict.val.l.vals[1] = *val;
283
284  /* bencode the dictionary, starting at offset 8 in the buffer */
285  buf = malloc(9);
286  g_assert(NULL != buf);
287  total = 9;
288  used = 8;
289  if(tr_bencSave(&dict, &buf, &used, &total)) {
290    g_assert_not_reached();
291  }
292  g_free(dict.val.l.vals);
293
294  /* write the bencoded data length into the first 8 bytes of the buffer */
295  stupid = buf[8];
296  snprintf(buf, 9, "%08X", used - 8);
297  buf[8] = stupid;
298
299  /* send the data */
300  return io_send_keepdata(con->source, buf, used);
301}
302
303static int
304send_msg_int(struct constate *con, const char *name, int num) {
305  benc_val_t val;
306
307  bzero(&val, sizeof(val));
308  val.type = TYPE_INT;
309  val.val.i = num;
310
311  return send_msg(con, name, &val);
312}
313
314static unsigned int
315all_io_received(GSource *source, char *data, unsigned int len, void *vdata) {
316  struct constate *con = vdata;
317  size_t size;
318  char stupid;
319  char *end;
320  benc_val_t msgs;
321  int ii, jj;
322
323  if(9 > len)
324    return 0;
325
326  stupid = data[8];
327  data[8] = '\0';
328  size = strtoul(data, NULL, 16);
329  data[8] = stupid;
330
331  if(size + 8 > len)
332    return 0;
333
334  if(!tr_bencLoad(data + 8, size, &msgs, &end) && TYPE_DICT == msgs.type) {
335    for(ii = 0; msgs.val.l.count > ii + 1; ii += 2)
336      if(TYPE_STR == msgs.val.l.vals[ii].type)
337        for(jj = 0; NULL != con->funcs[jj].name; jj++)
338          if(0 == strcmp(msgs.val.l.vals[ii].val.s.s, con->funcs[jj].name)) {
339            con->funcs[jj].func(con, msgs.val.l.vals[ii].val.s.s,
340                                msgs.val.l.vals + ii + 1);
341            break;
342          }
343    tr_bencFree(&msgs);
344  }
345
346  return size + 8 +
347    all_io_received(source, data + size + 8, len - size - 8, con);
348}
349
350static void
351destroycon(struct constate *con) {
352  con->source = NULL;
353
354  if(0 <= con->fd)
355    close(con->fd);
356  con->fd = -1;
357
358  switch(con->type) {
359    case CON_SERV:
360      break;
361    case CON_ADDFILE:
362      freestrlist(con->u.addfile.files);
363      g_main_loop_quit(con->u.addfile.loop);
364      break;
365  }
366}
367
368static void
369all_io_closed(GSource *source SHUTUP, void *vdata) {
370  struct constate *con = vdata;
371
372  destroycon(con);
373}
374
375static void
376srv_addfile(struct constate *con, const char *name SHUTUP, benc_val_t *val) {
377  struct constate_serv *srv = &con->u.serv;
378  GList *files;
379  int ii;
380
381  if(TYPE_LIST == val->type) {
382    files = NULL;
383    for(ii = 0; ii < val->val.l.count; ii++)
384      if(TYPE_STR == val->val.l.vals[ii].type &&
385         /* XXX somehow escape invalid utf-8 */
386         g_utf8_validate(val->val.l.vals[ii].val.s.s, -1, NULL))
387        files = g_list_append(files, val->val.l.vals[ii].val.s.s);
388    srv->addfunc(srv->cbdata, NULL, files, NULL, NULL);
389    g_list_free(files);
390  }
391}
392
393static void
394afc_version(struct constate *con, const char *name SHUTUP, benc_val_t *val) {
395  struct constate_addfile *afc = &con->u.addfile;
396  GList *file;
397  benc_val_t list, *str;
398
399  if(TYPE_INT != val->type || PROTOVERS != val->val.i) {
400    fprintf(stderr, _("bad IPC protocol version\n"));
401    destroycon(con);
402  } else {
403    /* XXX handle getting a non-version tag, invalid data,
404           or nothing (read timeout) */
405    bzero(&list, sizeof(list));
406    list.type = TYPE_LIST;
407    list.val.l.alloc = g_list_length(afc->files);
408    list.val.l.vals = g_new0(benc_val_t, list.val.l.alloc);
409    for(file = afc->files; NULL != file; file = file->next) {
410      str = list.val.l.vals + list.val.l.count;
411      str->type = TYPE_STR;
412      str->val.s.i = strlen(file->data);
413      str->val.s.s = file->data;
414      list.val.l.count++;
415    }
416    g_list_free(afc->files);
417    afc->files = NULL;
418    afc->addid = send_msg(con, MSG_ADDFILES, &list);
419    tr_bencFree(&list);
420  }
421}
422
423static void
424afc_io_sent(GSource *source SHUTUP, unsigned int id, void *vdata) {
425  struct constate_addfile *afc = &((struct constate*)vdata)->u.addfile;
426
427  if(0 < id && afc->addid == id) {
428    *(afc->succeeded) = TRUE;
429    destroycon(vdata);
430  }
431}
Note: See TracBrowser for help on using the repository browser.