source: trunk/gtk/ipc.c @ 920

Last change on this file since 920 was 920, checked in by joshe, 15 years ago

Merge nat-traversal branch to trunk.

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