1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2004 Evan Martin <martine@danga.com>
5 /* network-fork.c: provide a net_post_mainloop that uses an
6 * external net_post_blocking via a fork. used by network-curl and
18 #include <sys/types.h>
23 #include "network-internal.h"
32 NetStatusCallback user_cb
;
37 static int pipe_write (int pipe
, NetStatusType type
, int len
, void *data
) {
39 if (write(pipe
, &t
, 1) < 1) return -1;
40 if (write(pipe
, &len
, sizeof(int)) < sizeof(int)) return -1;
41 if (data
&& write(pipe
, data
, len
) < len
) return -1;
46 static void fork_cb (NetStatusType status
, gpointer statusdata
, gpointer data
) {
47 ForkData
*forkdata
= data
;
48 if (status
== NET_STATUS_PROGRESS
) pipe_write(forkdata
->pipefds
[1], NET_STATUS_PROGRESS
, sizeof(NetStatusProgress
), statusdata
);
52 static gboolean
readall (int fd
, void *buf
, int len
) {
56 ret
= read(fd
, ((char *)buf
)+rlen
, len
-rlen
);
57 if (ret
< 0) return FALSE
;
59 if (ret
== 0 && rlen
< len
) return FALSE
;
65 static void pipe_cb (ForkData
*forkdata
, gint pipe
, GdkInputCondition cond
) {
69 NetStatusProgress progress
;
70 len
= read(pipe
, &t
, 1);
71 if (len
== 0) g_print("Pipe unexpectedly closed.\n");
72 else if (len
< 0) perror("read");
73 if (!readall(pipe
, &len
, sizeof(int))) return;
75 case NET_STATUS_SUCCESS
:
76 buf
= g_new0(char, len
+1);
77 readall(pipe
, buf
, len
);
79 waitpid(forkdata
->pid
, NULL
, 0);
82 forkdata
->response
= g_string_new_len(buf
, len
);
86 case NET_STATUS_ERROR
:
87 buf
= g_new0(char, len
+ 1);
88 readall(pipe
, buf
, len
);
90 waitpid(forkdata
->pid
, NULL
, 0);
93 g_set_error(forkdata
->err
, NET_ERROR
, NET_ERROR_GENERIC
, "%s", buf
);
97 case NET_STATUS_PROGRESS
:
98 readall(pipe
, &progress
, sizeof(NetStatusProgress
));
99 if (forkdata
->user_cb
)
100 forkdata
->user_cb(NET_STATUS_PROGRESS
, &progress
, forkdata
->user_data
);
103 g_warning("pipe_cb: unhandled status %d.\n", t
);
108 void net_mainloop_cancel (NetMainloopHandle handle
) {
109 ForkData
*forkdata
= (ForkData
*)handle
;
110 if (forkdata
->pid
> 0) {
111 kill(forkdata
->pid
, SIGKILL
);
112 gtk_input_remove(forkdata
->pipe_tag
);
113 forkdata
->pipe_tag
= 0;
114 waitpid(forkdata
->pid
, NULL
, 0);
116 close(forkdata
->pipefds
[0]);
117 close(forkdata
->pipefds
[1]);
118 g_set_error(forkdata
->err
, NET_ERROR
, NET_ERROR_CANCELLED
, "%s", _("Cancelled."));
124 GString
*net_post_mainloop (const char *url
, GSList
*headers
, GString
*post
, NetStatusCallback cb
, gpointer data
, GError
**err
) {
125 ForkData forkdata
= { 0 };
127 forkdata
.user_cb
= cb
;
128 forkdata
.user_data
= data
;
129 /* fork, run the request, then pipe the data back out of the fork. */
130 if (pipe(forkdata
.pipefds
) < 0) {
131 g_set_error(err
, NET_ERROR
, NET_ERROR_GENERIC
, _("Error creating pipe (pipe(): %s)."), g_strerror(errno
));
134 forkdata
.pid
= fork();
135 if (forkdata
.pid
< 0) {
136 g_set_error(err
, 0, NET_ERROR_GENERIC
, _("Error forking (fork(): %s)."), g_strerror(errno
));
138 } else if (forkdata
.pid
== 0) {
142 response
= net_post_blocking(url
, headers
, post
, fork_cb
, &forkdata
, &err
);
143 if (response
== NULL
) {
144 int len
= strlen(err
->message
);
145 pipe_write(forkdata
.pipefds
[1], NET_STATUS_ERROR
, len
, err
->message
);
148 pipe_write(forkdata
.pipefds
[1], NET_STATUS_SUCCESS
, response
->len
, response
->str
);
149 g_string_free(response
, TRUE
);
151 close(forkdata
.pipefds
[0]);
152 close(forkdata
.pipefds
[1]);
155 /* otherwise, we're the parent. */
156 forkdata
.pipe_tag
= gtk_input_add_full(forkdata
.pipefds
[0], GDK_INPUT_READ
, (GdkInputFunction
) pipe_cb
, NULL
, &forkdata
, NULL
);
157 if (cb
) cb(NET_STATUS_BEGIN
, &forkdata
, data
);
158 gtk_main(); /* wait for the response. */
159 if (forkdata
.pipe_tag
) {
160 gtk_input_remove(forkdata
.pipe_tag
);
161 forkdata
.pipe_tag
= 0;
164 close(forkdata
.pipefds
[0]);
165 close(forkdata
.pipefds
[1]);
166 if (cb
) cb(NET_STATUS_DONE
, &forkdata
, data
);
167 return forkdata
.response
;