poll.h: cosmetix
[k8lowj.git] / src / network-fork.c
blobe556ae999a34f8e637048464c96fbde947d21aca
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2004 Evan Martin <martine@danga.com>
3 */
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
7 * network-libxml.
8 */
9 #ifdef HAVE_GTK
11 #include "gtk-all.h"
13 #include <errno.h>
14 #include <unistd.h>
15 #include <signal.h>
16 #include <string.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
21 #include "conf.h"
22 #include "network.h"
23 #include "network-internal.h"
26 typedef struct {
27 GString *response;
28 GError **err;
29 guint pipe_tag;
30 int pipefds[2];
31 pid_t pid;
32 NetStatusCallback user_cb;
33 gpointer user_data;
34 } ForkData;
37 static int pipe_write (int pipe, NetStatusType type, int len, void *data) {
38 char t = type;
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;
42 return 0;
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) {
53 int ret;
54 int rlen = 0;
55 do {
56 ret = read(fd, ((char *)buf)+rlen, len-rlen);
57 if (ret < 0) return FALSE;
58 rlen += ret;
59 if (ret == 0 && rlen < len) return FALSE;
60 } while (rlen < len);
61 return TRUE;
65 static void pipe_cb (ForkData *forkdata, gint pipe, GdkInputCondition cond) {
66 char t;
67 int len;
68 char *buf;
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;
74 switch (t) {
75 case NET_STATUS_SUCCESS:
76 buf = g_new0(char, len+1);
77 readall(pipe, buf, len);
78 buf[len] = 0;
79 waitpid(forkdata->pid, NULL, 0);
80 forkdata->pid = 0;
81 close(pipe);
82 forkdata->response = g_string_new_len(buf, len);
83 g_free(buf);
84 gtk_main_quit();
85 break;
86 case NET_STATUS_ERROR:
87 buf = g_new0(char, len + 1);
88 readall(pipe, buf, len);
89 buf[len] = 0;
90 waitpid(forkdata->pid, NULL, 0);
91 forkdata->pid = 0;
92 close(pipe);
93 g_set_error(forkdata->err, NET_ERROR, NET_ERROR_GENERIC, "%s", buf);
94 g_free(buf);
95 gtk_main_quit();
96 break;
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);
101 break;
102 default:
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);
115 forkdata->pid = 0;
116 close(forkdata->pipefds[0]);
117 close(forkdata->pipefds[1]);
118 g_set_error(forkdata->err, NET_ERROR, NET_ERROR_CANCELLED, "%s", _("Cancelled."));
120 gtk_main_quit();
124 GString *net_post_mainloop (const char *url, GSList *headers, GString *post, NetStatusCallback cb, gpointer data, GError **err) {
125 ForkData forkdata = { 0 };
126 forkdata.err = err;
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));
132 return NULL;
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));
137 return NULL;
138 } else if (forkdata.pid == 0) {
139 /* child */
140 GString *response;
141 GError *err = NULL;
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);
146 g_error_free(err);
147 } else {
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]);
153 _exit(0);
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;
163 forkdata.pid = 0;
164 close(forkdata.pipefds[0]);
165 close(forkdata.pipefds[1]);
166 if (cb) cb(NET_STATUS_DONE, &forkdata, data);
167 return forkdata.response;
170 #endif