about.c: cosmetix
[k8lowj.git] / src / network-fork.c
blob28e3a9afa0a574699cbf0dd55f973fa9acdb8a6e
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2004 Evan Martin <martine@danga.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
5 */
7 /* network-fork.c: provide a net_post_mainloop that uses an
8 * external net_post_blocking via a fork. used by network-curl and
9 * network-libxml.
11 #ifdef HAVE_GTK
13 #include "gtk-all.h"
15 #ifndef G_OS_WIN32
16 #include <unistd.h>
17 #endif
18 #include <errno.h>
19 #include <string.h>
21 #include <sys/types.h>
22 #include <sys/wait.h> /* waitpid */
23 #include <signal.h> /* kill */
25 #include "conf.h"
26 #include "network.h"
27 #include "network-internal.h"
29 typedef struct {
30 GString *response;
32 GError **err;
34 guint pipe_tag;
35 int pipefds[2];
37 pid_t pid;
39 NetStatusCallback user_cb;
40 gpointer user_data;
41 } ForkData;
43 static int
44 pipe_write(int pipe, NetStatusType type, int len, void *data) {
45 char t = type;
46 if (write(pipe, &t, 1) < 1)
47 return -1;
48 if (write(pipe, &len, sizeof(int)) < sizeof(int))
49 return -1;
50 if (data) {
51 if (write(pipe, data, len) < len)
52 return -1;
54 return 0;
57 static void
58 fork_cb(NetStatusType status, gpointer statusdata, gpointer data) {
59 ForkData *forkdata = data;
60 if (status == NET_STATUS_PROGRESS)
61 pipe_write(forkdata->pipefds[1], NET_STATUS_PROGRESS,
62 sizeof(NetStatusProgress), statusdata);
65 static gboolean
66 readall(int fd, void *buf, int len) {
67 int ret;
68 int rlen = 0;
69 do {
70 ret = read(fd, ((char*)buf)+rlen, len-rlen);
71 if (ret < 0)
72 return FALSE;
73 rlen += ret;
74 if (ret == 0 && rlen < len)
75 return FALSE;
76 } while (rlen < len);
77 return TRUE;
80 static void
81 pipe_cb(ForkData *forkdata, gint pipe, GdkInputCondition cond) {
82 char t;
83 int len;
84 char *buf;
85 NetStatusProgress progress;
87 len = read(pipe, &t, 1);
88 if (len == 0)
89 g_print("Pipe unexpectedly closed.\n");
90 else if (len < 0)
91 perror("read");
93 if (!readall(pipe, &len, sizeof(int)))
94 return;
96 switch (t) {
97 case NET_STATUS_SUCCESS:
98 buf = g_new0(char, len+1);
99 readall(pipe, buf, len);
100 buf[len] = 0;
101 waitpid(forkdata->pid, NULL, 0);
102 forkdata->pid = 0;
103 close(pipe);
104 forkdata->response = g_string_new_len(buf, len);
105 g_free(buf);
106 gtk_main_quit();
107 break;
108 case NET_STATUS_ERROR:
109 buf = g_new0(char, len+1);
110 readall(pipe, buf, len);
111 buf[len] = 0;
112 waitpid(forkdata->pid, NULL, 0);
113 forkdata->pid = 0;
114 close(pipe);
115 g_set_error(forkdata->err, NET_ERROR, NET_ERROR_GENERIC, "%s", buf);
116 g_free(buf);
117 gtk_main_quit();
118 break;
119 case NET_STATUS_PROGRESS:
120 readall(pipe, &progress, sizeof(NetStatusProgress));
121 if (forkdata->user_cb)
122 forkdata->user_cb(NET_STATUS_PROGRESS, &progress, forkdata->user_data);
123 break;
124 default:
125 g_warning("pipe_cb: unhandled status %d.\n", t);
129 void
130 net_mainloop_cancel(NetMainloopHandle handle) {
131 ForkData *forkdata = (ForkData*)handle;
133 if (forkdata->pid > 0) {
134 kill(forkdata->pid, SIGKILL);
135 gtk_input_remove(forkdata->pipe_tag);
136 forkdata->pipe_tag = 0;
137 waitpid(forkdata->pid, NULL, 0);
138 forkdata->pid = 0;
140 close(forkdata->pipefds[0]);
141 close(forkdata->pipefds[1]);
142 g_set_error(forkdata->err, NET_ERROR, NET_ERROR_CANCELLED, "%s",
143 _("Cancelled."));
145 gtk_main_quit();
148 GString*
149 net_post_mainloop(const char *url, GSList *headers, GString *post,
150 NetStatusCallback cb, gpointer data,
151 GError **err) {
152 ForkData forkdata = {0};
154 forkdata.err = err;
155 forkdata.user_cb = cb;
156 forkdata.user_data = data;
158 /* fork, run the request, then pipe the data back out of the fork. */
159 if (pipe(forkdata.pipefds) < 0) {
160 g_set_error(err, NET_ERROR, NET_ERROR_GENERIC,
161 _("Error creating pipe (pipe(): %s)."), g_strerror(errno));
162 return NULL;
164 forkdata.pid = fork();
165 if (forkdata.pid < 0) {
166 g_set_error(err, 0, NET_ERROR_GENERIC,
167 _("Error forking (fork(): %s)."), g_strerror(errno));
168 return NULL;
169 } else if (forkdata.pid == 0) { /* child. */
170 GString *response;
171 GError *err = NULL;
173 response = net_post_blocking(url, headers, post, fork_cb, &forkdata, &err);
174 if (response == NULL) {
175 int len = strlen(err->message);
176 pipe_write(forkdata.pipefds[1], NET_STATUS_ERROR, len, err->message);
177 g_error_free(err);
178 } else {
179 pipe_write(forkdata.pipefds[1], NET_STATUS_SUCCESS, response->len, response->str);
180 g_string_free(response, TRUE);
183 close(forkdata.pipefds[0]);
184 close(forkdata.pipefds[1]);
186 _exit(0);
188 /* otherwise, we're the parent. */
189 forkdata.pipe_tag = gtk_input_add_full(forkdata.pipefds[0], GDK_INPUT_READ,
190 (GdkInputFunction)pipe_cb, NULL, &forkdata, NULL);
191 if (cb)
192 cb(NET_STATUS_BEGIN, &forkdata, data);
194 gtk_main(); /* wait for the response. */
195 if (forkdata.pipe_tag) {
196 gtk_input_remove(forkdata.pipe_tag);
197 forkdata.pipe_tag = 0;
199 forkdata.pid = 0;
201 close(forkdata.pipefds[0]);
202 close(forkdata.pipefds[1]);
204 if (cb)
205 cb(NET_STATUS_DONE, &forkdata, data);
207 return forkdata.response;
210 #endif