tasks.c (7314B)
1 /*
2 * Copyright (c) 2014 Joris Vink <joris@coders.se>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 /*
18 * In this example, we use the background tasks available in Kore
19 * to fire off a POST to our /post_back page handler containing
20 * the user argument that was passed to us in our GET request to /.
21 *
22 * This illustrates how Kore its background tasks in effect work and
23 * how to operate on the channel in order to pass data back and forth.
24 *
25 * You need libcurl installed for this to build (including headers)
26 *
27 * Read README.md on how to build and run this example.
28 */
29
30 #include <curl/curl.h>
31
32 #include <kore/kore.h>
33 #include <kore/http.h>
34 #include <kore/tasks.h>
35
36 /* We need to allow some more syscalls on linux. */
37 #if defined(__linux__)
38 #include <kore/seccomp.h>
39
40 KORE_SECCOMP_FILTER("tasks",
41 /* Allow sockets and libcurl to call connect. */
42 KORE_SYSCALL_ALLOW(bind),
43 KORE_SYSCALL_ALLOW(ioctl),
44 KORE_SYSCALL_ALLOW(connect),
45 KORE_SYSCALL_ALLOW(getsockopt),
46 KORE_SYSCALL_ALLOW(getsockname),
47 KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_INET),
48 KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_INET6),
49 KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_UNIX),
50 KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_NETLINK),
51
52 /* Other */
53 KORE_SYSCALL_ALLOW(ioctl),
54 KORE_SYSCALL_ALLOW(madvise),
55 KORE_SYSCALL_ALLOW(recvmsg),
56 KORE_SYSCALL_ALLOW(sendmmsg),
57 KORE_SYSCALL_ALLOW(getpeername),
58 KORE_SYSCALL_ALLOW(rseq),
59 KORE_SYSCALL_ALLOW(clone3),
60 );
61 #endif
62
63 int run_curl(struct kore_task *);
64 int post_back(struct http_request *);
65 int page_handler(struct http_request *);
66 size_t curl_write_cb(char *, size_t, size_t, void *);
67
68 struct rstate {
69 struct kore_task task;
70 };
71
72 int
73 page_handler(struct http_request *req)
74 {
75 u_int32_t len;
76 struct rstate *state;
77 char *user, result[64];
78
79 /*
80 * Lets check if a task has been created yet, this is important
81 * as we only want to fire this off once and we will be called
82 * again once it has been created.
83 *
84 * In this example, we'll store our state with our task in hdlr_extra.
85 */
86 if (req->hdlr_extra == NULL) {
87 /* Grab the user argument */
88 http_populate_get(req);
89 if (!http_argument_get_string(req, "user", &user)) {
90 http_response(req, 500, "ERROR\n", 6);
91 return (KORE_RESULT_OK);
92 }
93
94 /*
95 * Allocate rstate and bind it to the hdlr_extra field.
96 * Kore automatically frees this when freeing the result.
97 */
98 state = kore_malloc(sizeof(*state));
99 req->hdlr_extra = state;
100
101 /*
102 * Create a new task that will execute the run_curl()
103 * function and bind it to our request.
104 *
105 * Binding a task to a request means Kore will reschedule
106 * the page handler for that request to refire after the
107 * task has completed or when it writes on the task channel.
108 */
109 kore_task_create(&state->task, run_curl);
110 kore_task_bind_request(&state->task, req);
111
112 /*
113 * Start the task and write the user we received in our
114 * GET request to its channel.
115 */
116 kore_task_run(&state->task);
117 kore_task_channel_write(&state->task, user, strlen(user));
118
119 /*
120 * Tell Kore to retry us later.
121 */
122 return (KORE_RESULT_RETRY);
123 } else {
124 state = req->hdlr_extra;
125 }
126
127 /*
128 * Our page handler is scheduled to be called when either the
129 * task finishes or has written data onto the channel.
130 *
131 * In order to distinguish between the two we can inspect the
132 * state of the task.
133 */
134 if (kore_task_state(&state->task) != KORE_TASK_STATE_FINISHED) {
135 http_request_sleep(req);
136 return (KORE_RESULT_RETRY);
137 }
138
139 /*
140 * Task is finished, check the result.
141 */
142 if (kore_task_result(&state->task) != KORE_RESULT_OK) {
143 kore_task_destroy(&state->task);
144 http_response(req, 500, NULL, 0);
145 return (KORE_RESULT_OK);
146 }
147
148 /*
149 * Lets read what our task has written to the channel.
150 *
151 * kore_task_channel_read() will return the amount of bytes
152 * that it received for that read. If the returned bytes is
153 * larger then the buffer you passed this is a sign of truncation
154 * and should be treated carefully.
155 */
156 len = kore_task_channel_read(&state->task, result, sizeof(result));
157 if (len > sizeof(result)) {
158 http_response(req, 500, NULL, 0);
159 } else {
160 http_response(req, 200, result, len);
161 }
162
163 /* We good, destroy the task. */
164 kore_task_destroy(&state->task);
165
166 return (KORE_RESULT_OK);
167 }
168
169 int
170 post_back(struct http_request *req)
171 {
172 char *user;
173
174 if (req->method != HTTP_METHOD_POST) {
175 http_response(req, 500, NULL, 0);
176 return (KORE_RESULT_OK);
177 }
178
179 http_populate_post(req);
180 if (!http_argument_get_string(req, "user", &user)) {
181 http_response(req, 500, NULL, 0);
182 return (KORE_RESULT_OK);
183 }
184
185 /* Simply echo the supplied user argument back. */
186 http_response(req, 200, user, strlen(user));
187
188 return (KORE_RESULT_OK);
189 }
190
191 /*
192 * This is the function that is executed by our task which is created
193 * in the page_handler() callback.
194 *
195 * It sets up a CURL POST request to /post_back passing along the
196 * user argument which it receives from its channel from page_handler().
197 */
198 int
199 run_curl(struct kore_task *t)
200 {
201 struct kore_buf *b;
202 size_t len;
203 CURLcode res;
204 u_int8_t *data;
205 CURL *curl;
206 char user[64], fields[128];
207
208 /*
209 * Read the channel in order to obtain the user argument
210 * that was written to it by page_handler().
211 */
212 len = kore_task_channel_read(t, user, sizeof(user));
213 if (len > sizeof(user))
214 return (KORE_RESULT_ERROR);
215
216 if (!kore_snprintf(fields, sizeof(fields),
217 NULL, "user=%.*s", len, user))
218 return (KORE_RESULT_ERROR);
219
220 if ((curl = curl_easy_init()) == NULL)
221 return (KORE_RESULT_ERROR);
222
223 b = kore_buf_alloc(128);
224
225 /* Do CURL magic. */
226 curl_easy_setopt(curl, CURLOPT_POST, 1);
227 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
228 curl_easy_setopt(curl, CURLOPT_WRITEDATA, b);
229 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
230 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
231 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, fields);
232 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_cb);
233 #if !defined(KORE_NO_TLS)
234 curl_easy_setopt(curl, CURLOPT_URL, "https://127.0.0.1:8888/post_back");
235 #else
236 curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:8888/post_back");
237 #endif
238
239 res = curl_easy_perform(curl);
240 if (res != CURLE_OK) {
241 kore_buf_free(b);
242 curl_easy_cleanup(curl);
243 return (KORE_RESULT_ERROR);
244 }
245
246 /*
247 * Grab the response from the CURL request and write the
248 * result back to the task channel.
249 */
250 data = kore_buf_release(b, &len);
251 kore_task_channel_write(t, data, len);
252 kore_free(data);
253
254 return (KORE_RESULT_OK);
255 }
256
257 size_t
258 curl_write_cb(char *ptr, size_t size, size_t nmemb, void *udata)
259 {
260 struct kore_buf *b = udata;
261
262 kore_buf_append(b, ptr, size * nmemb);
263 return (size * nmemb);
264 }