gdp / apps / gdp-rest.c @ master
History | View | Annotate | Download (34.5 KB)
1 | 4e425382 | Eric Allman | /* vim: set ai sw=4 sts=4 ts=4 :*/
|
---|---|---|---|
2 | 47c6ea64 | Eric Allman | /*
|
3 | 4e425382 | Eric Allman | ** RESTful interface to GDP
|
4 | 174e9f6d | Eric Allman | **
|
5 | 4e425382 | Eric Allman | ** Uses SCGI between the web server and this process. We link
|
6 | ** with Sam Alexander's SCGI C Library, http://www.xamuel.com/scgilib/
|
||
7 | 055d3009 | Eric Allman | **
|
8 | ** ----- BEGIN LICENSE BLOCK -----
|
||
9 | ** Applications for the Global Data Plane
|
||
10 | ** From the Ubiquitous Swarm Lab, 490 Cory Hall, U.C. Berkeley.
|
||
11 | **
|
||
12 | c87dd166 | Eric Allman | ** Copyright (c) 2015-2019, Regents of the University of California.
|
13 | 6bd5476b | Eric Allman | ** All rights reserved.
|
14 | 055d3009 | Eric Allman | **
|
15 | 6bd5476b | Eric Allman | ** Permission is hereby granted, without written agreement and without
|
16 | ** license or royalty fees, to use, copy, modify, and distribute this
|
||
17 | ** software and its documentation for any purpose, provided that the above
|
||
18 | ** copyright notice and the following two paragraphs appear in all copies
|
||
19 | ** of this software.
|
||
20 | 055d3009 | Eric Allman | **
|
21 | 6bd5476b | Eric Allman | ** IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
|
22 | ** SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST
|
||
23 | ** PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
|
||
24 | ** EVEN IF REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
25 | 055d3009 | Eric Allman | **
|
26 | 6bd5476b | Eric Allman | ** REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
|
27 | 055d3009 | Eric Allman | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
28 | 6bd5476b | Eric Allman | ** FOR A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION,
|
29 | ** IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO
|
||
30 | ** OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
|
||
31 | ** OR MODIFICATIONS.
|
||
32 | 055d3009 | Eric Allman | ** ----- END LICENSE BLOCK -----
|
33 | 174e9f6d | Eric Allman | */
|
34 | 47c6ea64 | Eric Allman | |
35 | #include <ep/ep.h> |
||
36 | 9f48568c | Eric Allman | #include <ep/ep_app.h> |
37 | #include <ep/ep_hash.h> |
||
38 | 47c6ea64 | Eric Allman | #include <ep/ep_dbg.h> |
39 | #include <ep/ep_pcvt.h> |
||
40 | a0672b22 | Eric Allman | #include <ep/ep_sd.h> |
41 | 9f48568c | Eric Allman | #include <ep/ep_stat.h> |
42 | 47c6ea64 | Eric Allman | #include <ep/ep_xlate.h> |
43 | 1b267ec1 | Eric Allman | #include <gdp/gdp.h> |
44 | 63b801e4 | Eric Allman | #include <pthread.h> |
45 | 47c6ea64 | Eric Allman | #include <unistd.h> |
46 | #include <stdio.h> |
||
47 | #include <sysexits.h> |
||
48 | #include <string.h> |
||
49 | #include <ctype.h> |
||
50 | #include <errno.h> |
||
51 | 4e425382 | Eric Allman | #include <getopt.h> |
52 | #include <sys/socket.h> |
||
53 | 47c6ea64 | Eric Allman | #include <scgilib/scgilib.h> |
54 | #include <event2/event.h> |
||
55 | 80c5389d | Eric Allman | #include <jansson.h> |
56 | 04ad7859 | Rick Pratt | #include <sys/types.h> |
57 | #include <sys/wait.h> |
||
58 | 47c6ea64 | Eric Allman | |
59 | 174e9f6d | Eric Allman | static EP_DBG Dbg = EP_DBG_INIT("gdp.rest", "RESTful interface to GDP"); |
60 | 47c6ea64 | Eric Allman | |
61 | 18188fcc | Eric Allman | #define DEF_URI_PREFIX "/gdp/v1" |
62 | 0c663d10 | Eric Allman | |
63 | 4e425382 | Eric Allman | const char *GclUriPrefix; // prefix on all REST calls |
64 | EP_HASH *OpenGclCache; // cache of open GCLs
|
||
65 | 47c6ea64 | Eric Allman | |
66 | a555e771 | Rick Pratt | gdp_open_info_t *shared_gdp_open_info; |
67 | 1b267ec1 | Eric Allman | |
68 | eae1d3ec | Eric Allman | // most gdp-create parameters are available through gdp-rest
|
69 | 0cb06b6b | Rick Pratt | #define GCL_C_PARAM_SERV_e 0 |
70 | #define GCL_C_PARAM_SERV_S 1 |
||
71 | 55ebd220 | Rick Pratt | #define GCL_C_PARAM_SERV_K 2 |
72 | #define GCL_C_PARAM_SERV_MAX 3 |
||
73 | 0cb06b6b | Rick Pratt | #define GCL_C_PARAM_MAX (GCL_C_PARAM_SERV_MAX + 6) |
74 | 04ad7859 | Rick Pratt | |
75 | eae1d3ec | Eric Allman | const char *exec_gdp_create_param[GCL_C_PARAM_MAX] = |
76 | 04ad7859 | Rick Pratt | { |
77 | 0cb06b6b | Rick Pratt | /* 3 server controlled options */
|
78 | "-e", "-S", "-K", |
||
79 | /* 6 client options */
|
||
80 | "-C", "-D", "-h", "-k", "-b", "-c" |
||
81 | /* remaining unsupported options: "-G", "-q", "-s", "-w", "-W" */
|
||
82 | 04ad7859 | Rick Pratt | }; |
83 | eae1d3ec | Eric Allman | const char *exec_gdp_create = "/usr/bin/gdp-create"; |
84 | 04ad7859 | Rick Pratt | |
85 | eae1d3ec | Eric Allman | // WARNING: gdp-rest parses gdp-create text output (which will be kept stable!)
|
86 | 04ad7859 | Rick Pratt | #define GCL_C_OUT_GCL_NAME 16 |
87 | #define GCL_C_OUT_GCL_NAME_END 59 |
||
88 | #define GCL_C_OUT_LOGD_NAME 75 |
||
89 | #define GCL_C_OUT_BUF_SIZE 256 // likely output * 2, rounded up |
||
90 | eae1d3ec | Eric Allman | // gdp-create text output expectation (which will be kept stable!)
|
91 | const char *gdp_c_min_out = |
||
92 | 04ad7859 | Rick Pratt | "Created new GCL 0123456789012345678901234567890123456789012\n" //60 |
93 | "\ton log server N\n"; // N is a variable length > 0 log server name |
||
94 | |||
95 | 1b267ec1 | Eric Allman | /*
|
96 | ** LOG_ERROR --- generic error logging routine
|
||
97 | */
|
||
98 | |||
99 | void
|
||
100 | log_error(const char *fmt, ...) |
||
101 | 47c6ea64 | Eric Allman | { |
102 | 1b267ec1 | Eric Allman | va_list av; |
103 | |||
104 | va_start(av, fmt); |
||
105 | vfprintf(stderr, fmt, av); |
||
106 | fprintf(stderr, ": %s\n", strerror(errno));
|
||
107 | } |
||
108 | 47c6ea64 | Eric Allman | |
109 | /*
|
||
110 | 4e425382 | Eric Allman | ** SCGI_METHOD_NAME --- return printable name of method
|
111 | 174e9f6d | Eric Allman | **
|
112 | 4e425382 | Eric Allman | ** Arguably this should be in SCGILIB.
|
113 | 174e9f6d | Eric Allman | */
|
114 | 47c6ea64 | Eric Allman | |
115 | const char * |
||
116 | scgi_method_name(types_of_methods_for_http_protocol meth) |
||
117 | { |
||
118 | 4e425382 | Eric Allman | switch (meth)
|
119 | { |
||
120 | case SCGI_METHOD_UNSPECIFIED:
|
||
121 | return "unspecified"; |
||
122 | case SCGI_METHOD_UNKNOWN:
|
||
123 | return "unknown"; |
||
124 | case SCGI_METHOD_GET:
|
||
125 | return "GET"; |
||
126 | case SCGI_METHOD_POST:
|
||
127 | return "POST"; |
||
128 | case SCGI_METHOD_PUT:
|
||
129 | return "PUT"; |
||
130 | case SCGI_METHOD_DELETE:
|
||
131 | return "DELETE"; |
||
132 | case SCGI_METHOD_HEAD:
|
||
133 | return "HEAD"; |
||
134 | 2ffaa038 | Rick Pratt | case SCGI_METHOD_OPTIONS:
|
135 | return "OPTIONS"; |
||
136 | 4e425382 | Eric Allman | } |
137 | return "impossible"; |
||
138 | 47c6ea64 | Eric Allman | } |
139 | |||
140 | 174e9f6d | Eric Allman | |
141 | 47c6ea64 | Eric Allman | void
|
142 | 174e9f6d | Eric Allman | write_scgi(scgi_request *req, |
143 | 4e425382 | Eric Allman | char *sbuf)
|
144 | 47c6ea64 | Eric Allman | { |
145 | 4e425382 | Eric Allman | int dead = 0; |
146 | int i;
|
||
147 | 80c5389d | Eric Allman | char xbuf[1024]; |
148 | char *xbase = xbuf;
|
||
149 | char *xp;
|
||
150 | char *sp;
|
||
151 | |||
152 | // first translate any lone newlines into crlfs (pity jansson won't do this)
|
||
153 | for (i = 0, sp = sbuf; (sp = strchr(sp, '\n')) != NULL; sp++) |
||
154 | if (sp == sbuf || sp[-1] != '\r') |
||
155 | i++; |
||
156 | |||
157 | // i is now the count of newlines without carriage returns
|
||
158 | sp = sbuf; |
||
159 | if (i > 0) |
||
160 | { |
||
161 | bool cr = false; |
||
162 | |||
163 | // find the total number of bytes we need, using malloc if necessary
|
||
164 | i += strlen(sbuf) + 1;
|
||
165 | if (i > sizeof xbuf) |
||
166 | xbase = ep_mem_malloc(i); |
||
167 | xp = xbase; |
||
168 | |||
169 | // xp now points to a large enough buffer, possibly malloced
|
||
170 | while (*sp != '\0') |
||
171 | { |
||
172 | if (*sp == '\n' && !cr) |
||
173 | *xp++ = '\r';
|
||
174 | cr = *sp == '\r';
|
||
175 | *xp++ = *sp++; |
||
176 | } |
||
177 | *xp = '\0';
|
||
178 | |||
179 | sp = xbase; |
||
180 | } |
||
181 | |||
182 | 4e425382 | Eric Allman | |
183 | // I don't quite understand what "dead" is all about. It's copied
|
||
184 | // from the "helloworld" example. Something about memory management,
|
||
185 | // but his example only seems to use it to print messages.
|
||
186 | req->dead = &dead; |
||
187 | 80c5389d | Eric Allman | i = scgi_write(req, sp); |
188 | |||
189 | // free buffer memory if necessary
|
||
190 | if (xbase != xbuf)
|
||
191 | ep_mem_free(xbase); |
||
192 | 4e425382 | Eric Allman | |
193 | ep_dbg_cprintf(Dbg, 10, "scgi_write => %d, dead = %d\n", i, dead); |
||
194 | if (i == 0) |
||
195 | { |
||
196 | char obuf[40]; |
||
197 | 5bc1cdfb | Eric Allman | char nbuf[40]; |
198 | 4e425382 | Eric Allman | |
199 | bfecf573 | Eric Allman | (void) (0 == strerror_r(errno, nbuf, sizeof nbuf)); |
200 | 4e425382 | Eric Allman | ep_app_error("scgi_write (%s) failed: %s",
|
201 | 5bc1cdfb | Eric Allman | ep_pcvt_str(obuf, sizeof obuf, sbuf), nbuf);
|
202 | 4e425382 | Eric Allman | } |
203 | else if (dead) |
||
204 | { |
||
205 | ep_dbg_cprintf(Dbg, 1, "dead is set\n"); |
||
206 | } |
||
207 | req->dead = NULL;
|
||
208 | efad251c | Siqi Lin | } |
209 | 47c6ea64 | Eric Allman | |
210 | 174e9f6d | Eric Allman | |
211 | 47c6ea64 | Eric Allman | EP_STAT |
212 | gdp_failure(scgi_request *req, char *code, char *msg, char *fmt, ...) |
||
213 | { |
||
214 | 80c5389d | Eric Allman | char buf[SCGI_MAX_OUTBUF_SIZE];
|
215 | 4e425382 | Eric Allman | va_list av; |
216 | char c;
|
||
217 | 80c5389d | Eric Allman | json_t *j; |
218 | char *jbuf;
|
||
219 | 4e425382 | Eric Allman | |
220 | 80c5389d | Eric Allman | // set up the JSON object
|
221 | j = json_object(); |
||
222 | de9d8389 | Rick Pratt | // set_new steals new json_string (or auto-decrefs if !j || !key)
|
223 | json_object_set_new_nocheck(j, "error", json_string(msg));
|
||
224 | json_object_set_new_nocheck(j, "code", json_string(code));
|
||
225 | json_object_set_new_nocheck(j, "uri", json_string(req->request_uri));
|
||
226 | json_object_set_new_nocheck(j, "method",
|
||
227 | 4f2d2174 | Eric Allman | json_string(scgi_method_name(req->request_method))); |
228 | 4e425382 | Eric Allman | |
229 | va_start(av, fmt); |
||
230 | while ((c = *fmt++) != '\0') |
||
231 | 47c6ea64 | Eric Allman | { |
232 | 80c5389d | Eric Allman | char *key = va_arg(av, char *); |
233 | 4e425382 | Eric Allman | |
234 | switch (c)
|
||
235 | { |
||
236 | case 's': |
||
237 | de9d8389 | Rick Pratt | // set_new steals new json_string (or auto-decrefs if !j || !key)
|
238 | json_object_set_new(j, key, json_string(va_arg(av, char *)));
|
||
239 | 4e425382 | Eric Allman | break;
|
240 | |||
241 | case 'd': |
||
242 | de9d8389 | Rick Pratt | // set_new steals new json_integer (or auto-decrefs if !j || !key)
|
243 | json_object_set_new(j, key, json_integer((long) va_arg(av, int))); |
||
244 | 4e425382 | Eric Allman | break;
|
245 | |||
246 | default:
|
||
247 | 80c5389d | Eric Allman | { |
248 | char pbuf[40]; |
||
249 | |||
250 | snprintf(pbuf, sizeof pbuf, "Unknown format `%c'", c); |
||
251 | de9d8389 | Rick Pratt | // set_new steals new json_string (auto-decrefs if !j || !key)
|
252 | json_object_set_new(j, key, json_string(pbuf)); |
||
253 | 80c5389d | Eric Allman | } |
254 | 4e425382 | Eric Allman | break;
|
255 | } |
||
256 | 47c6ea64 | Eric Allman | } |
257 | 4e425382 | Eric Allman | va_end(av); |
258 | 80c5389d | Eric Allman | |
259 | de9d8389 | Rick Pratt | // get it (malloc'ed) in string format (will return NULL if !j)
|
260 | 80c5389d | Eric Allman | jbuf = json_dumps(j, JSON_INDENT(4));
|
261 | |||
262 | // create the entire SCGI return message
|
||
263 | snprintf(buf, sizeof buf,
|
||
264 | de9d8389 | Rick Pratt | "HTTP/1.1 %s %s\r\n"
|
265 | "Content-Type: application/json\r\n"
|
||
266 | "\r\n"
|
||
267 | "%s\r\n",
|
||
268 | code, msg, jbuf ? jbuf : "");
|
||
269 | 4e425382 | Eric Allman | write_scgi(req, buf); |
270 | |||
271 | 80c5389d | Eric Allman | // clean up
|
272 | json_decref(j); |
||
273 | free(jbuf); |
||
274 | |||
275 | 4e425382 | Eric Allman | // should chose something more appropriate here
|
276 | return EP_STAT_ERROR;
|
||
277 | 47c6ea64 | Eric Allman | } |
278 | |||
279 | 174e9f6d | Eric Allman | |
280 | 4e425382 | Eric Allman | /*
|
281 | 1b267ec1 | Eric Allman | ** GDP_SCGI_RECV --- guarded version of scgi_recv().
|
282 | **
|
||
283 | ** The SCGI library isn't reentrant, so we have to avoid conflict
|
||
284 | ** here.
|
||
285 | */
|
||
286 | |||
287 | EP_THR_MUTEX ScgiRecvMutex EP_THR_MUTEX_INITIALIZER; |
||
288 | |||
289 | scgi_request * |
||
290 | gdp_scgi_recv(void)
|
||
291 | { |
||
292 | scgi_request *req; |
||
293 | |||
294 | ep_thr_mutex_lock(&ScgiRecvMutex); |
||
295 | req = scgi_recv(); |
||
296 | ep_thr_mutex_unlock(&ScgiRecvMutex); |
||
297 | return req;
|
||
298 | } |
||
299 | |||
300 | |||
301 | 18188fcc | Eric Allman | bool
|
302 | is_integer_string(const char *s) |
||
303 | { |
||
304 | do
|
||
305 | { |
||
306 | if (!isdigit(*s))
|
||
307 | return false; |
||
308 | } while (*++s != '\0'); |
||
309 | return true; |
||
310 | } |
||
311 | |||
312 | |||
313 | /*
|
||
314 | ** PARSE_QUERY --- break up the query into key/value pairs
|
||
315 | */
|
||
316 | |||
317 | struct qkvpair
|
||
318 | { |
||
319 | char *key;
|
||
320 | char *val;
|
||
321 | }; |
||
322 | |||
323 | int
|
||
324 | parse_query(char *qtext, struct qkvpair *qkvs, int nqkvs) |
||
325 | { |
||
326 | int n = 0; |
||
327 | |||
328 | for (n = 0; qtext != NULL && n < nqkvs; n++) |
||
329 | { |
||
330 | char *p;
|
||
331 | |||
332 | if (*qtext == '\0') |
||
333 | break;
|
||
334 | |||
335 | // skip to the next kv pair separator
|
||
336 | qkvs[n].key = strsep(&qtext, "&;");
|
||
337 | |||
338 | // separate the key and the value (if any)
|
||
339 | p = strchr(qkvs[n].key, '=');
|
||
340 | if (p != NULL && *p != '\0') |
||
341 | *p++ = '\0';
|
||
342 | qkvs[n].val = p; |
||
343 | } |
||
344 | |||
345 | return n;
|
||
346 | } |
||
347 | |||
348 | |||
349 | /*
|
||
350 | ** FIND_QUERY_KV --- find a key-value pair in query
|
||
351 | */
|
||
352 | |||
353 | char *
|
||
354 | find_query_kv(const char *key, struct qkvpair *qkvs) |
||
355 | { |
||
356 | while (qkvs->key != NULL) |
||
357 | { |
||
358 | if (strcasecmp(key, qkvs->key) == 0) |
||
359 | return qkvs->val;
|
||
360 | qkvs++; |
||
361 | } |
||
362 | return NULL; |
||
363 | } |
||
364 | |||
365 | 2ffaa038 | Rick Pratt | /*
|
366 | ** CORS200 --- issue an OPTIONS response supportive of javascript CORS needs
|
||
367 | */
|
||
368 | EP_STAT |
||
369 | cors200(scgi_request *req) |
||
370 | { |
||
371 | char buf[SCGI_MAX_OUTBUF_SIZE];
|
||
372 | snprintf(buf, sizeof buf,
|
||
373 | "HTTP/1.1 200 OK\r\n"
|
||
374 | "Content-Type: text/plain\r\n"
|
||
375 | "\r\n"
|
||
376 | "\r\n");
|
||
377 | write_scgi(req, buf); |
||
378 | return EP_STAT_OK;
|
||
379 | } |
||
380 | 18188fcc | Eric Allman | |
381 | /*
|
||
382 | 04ad7859 | Rick Pratt | ** ERROR400 --- issue a 400 "Bad Request" error
|
383 | */
|
||
384 | |||
385 | EP_STAT |
||
386 | error400(scgi_request *req, const char *detail) |
||
387 | { |
||
388 | return gdp_failure(req, "400", "Bad Request", "s", "detail", detail); |
||
389 | } |
||
390 | |||
391 | /*
|
||
392 | 18188fcc | Eric Allman | ** ERROR404 --- issue a 404 "Not Found" error
|
393 | */
|
||
394 | |||
395 | EP_STAT |
||
396 | error404(scgi_request *req, const char *detail) |
||
397 | { |
||
398 | 4f2d2174 | Eric Allman | return gdp_failure(req, "404", "Not Found", "s", |
399 | 18188fcc | Eric Allman | "detail", detail);
|
400 | } |
||
401 | |||
402 | |||
403 | 1b267ec1 | Eric Allman | /*
|
404 | 18188fcc | Eric Allman | ** ERROR405 --- issue a 405 "Method Not Allowed" error
|
405 | */
|
||
406 | |||
407 | EP_STAT |
||
408 | error405(scgi_request *req, const char *detail) |
||
409 | { |
||
410 | 4f2d2174 | Eric Allman | return gdp_failure(req, "405", "Method Not Allowed", "s", |
411 | 18188fcc | Eric Allman | "detail", detail);
|
412 | } |
||
413 | |||
414 | 04ad7859 | Rick Pratt | /*
|
415 | ** ERROR409 --- issue a 409 "Conflict" error
|
||
416 | */
|
||
417 | |||
418 | EP_STAT |
||
419 | error409(scgi_request *req, const char *detail) |
||
420 | { |
||
421 | return gdp_failure(req, "409", "Conflict", "s", "detail", detail); |
||
422 | } |
||
423 | 18188fcc | Eric Allman | |
424 | /*
|
||
425 | ** ERROR500 --- issue a 500 "Internal Server Error" error
|
||
426 | */
|
||
427 | |||
428 | EP_STAT |
||
429 | error500(scgi_request *req, const char *detail, int eno) |
||
430 | { |
||
431 | char nbuf[40]; |
||
432 | |||
433 | bfecf573 | Eric Allman | (void) (0 == strerror_r(eno, nbuf, sizeof nbuf)); |
434 | 4f2d2174 | Eric Allman | (void) gdp_failure(req, "500", "Internal Server Error", "ss", |
435 | 18188fcc | Eric Allman | "errno", nbuf,
|
436 | "detail", detail);
|
||
437 | return ep_stat_from_errno(eno);
|
||
438 | } |
||
439 | |||
440 | |||
441 | /*
|
||
442 | ** ERROR501 --- issue a 501 "Not Implemented" error
|
||
443 | */
|
||
444 | |||
445 | EP_STAT |
||
446 | error501(scgi_request *req, const char *detail) |
||
447 | { |
||
448 | 4f2d2174 | Eric Allman | return gdp_failure(req, "501", "Not Implemented", "s", |
449 | 18188fcc | Eric Allman | "detail", detail);
|
450 | } |
||
451 | |||
452 | |||
453 | /*
|
||
454 | a555e771 | Rick Pratt | ** PROCESS_GDP_CREATE_REQ --- create new log via gdp-create, parse stdout msg
|
455 | 18188fcc | Eric Allman | */
|
456 | |||
457 | EP_STAT |
||
458 | eae1d3ec | Eric Allman | process_gdp_create_req(scgi_request *req, const char *gclxname, |
459 | 04ad7859 | Rick Pratt | json_t *j, json_t *j_meta, size_t options_max) |
460 | 18188fcc | Eric Allman | { |
461 | 04ad7859 | Rick Pratt | EP_STAT estat = EP_STAT_OK; |
462 | int opt;
|
||
463 | const char *options[options_max]; |
||
464 | size_t j_unprocessed; |
||
465 | size_t j_meta_unprocessed; |
||
466 | int o_pipe[2]; |
||
467 | pid_t pid; |
||
468 | int status;
|
||
469 | int exit_code;
|
||
470 | json_t *j_temp; |
||
471 | json_t *j_resp; |
||
472 | char *jbuf;
|
||
473 | char sbuf[SCGI_MAX_OUTBUF_SIZE];
|
||
474 | d56dfbff | Christopher Brooks | int p;
|
475 | d0a04e62 | Eric Allman | |
476 | 04ad7859 | Rick Pratt | // mandatory gclxname json obj already processed by caller to arrive here
|
477 | j_unprocessed = json_object_size(j) - 1;
|
||
478 | |||
479 | // process name in slot zero
|
||
480 | opt = 0;
|
||
481 | eae1d3ec | Eric Allman | options[opt++] = exec_gdp_create; |
482 | d0a04e62 | Eric Allman | |
483 | 04ad7859 | Rick Pratt | // then add server controlled parameters
|
484 | eae1d3ec | Eric Allman | options[opt++] = exec_gdp_create_param[GCL_C_PARAM_SERV_e]; |
485 | 04ad7859 | Rick Pratt | options[opt++] = "none";
|
486 | 0cb06b6b | Rick Pratt | options[opt++] = exec_gdp_create_param[GCL_C_PARAM_SERV_S]; |
487 | eae1d3ec | Eric Allman | options[opt++] = exec_gdp_create_param[GCL_C_PARAM_SERV_K]; |
488 | 04ad7859 | Rick Pratt | options[opt++] = "/etc/gdp/keys";
|
489 | |||
490 | // then add client requested options
|
||
491 | d56dfbff | Christopher Brooks | for (p = GCL_C_PARAM_SERV_MAX; p < GCL_C_PARAM_MAX; p++)
|
492 | 04ad7859 | Rick Pratt | { |
493 | // borrowed
|
||
494 | eae1d3ec | Eric Allman | if ((j_temp = json_object_get(j, exec_gdp_create_param[p])) != NULL) |
495 | 04ad7859 | Rick Pratt | { |
496 | eae1d3ec | Eric Allman | options[opt++] = exec_gdp_create_param[p]; |
497 | 04ad7859 | Rick Pratt | // borrowed
|
498 | options[opt++] = json_string_value(j_temp); |
||
499 | if (options[opt - 1] != NULL) |
||
500 | j_unprocessed--; |
||
501 | } |
||
502 | } |
||
503 | 18188fcc | Eric Allman | |
504 | de9d8389 | Rick Pratt | // client optional "META" array elements (size is 0 if !j_meta or !array)
|
505 | 04ad7859 | Rick Pratt | j_meta_unprocessed = json_array_size(j_meta); |
506 | if (j_meta_unprocessed > 0) |
||
507 | { |
||
508 | d0a04e62 | Eric Allman | |
509 | 04ad7859 | Rick Pratt | size_t j_meta_i; |
510 | |||
511 | json_array_foreach(j_meta, j_meta_i, j_temp) |
||
512 | { |
||
513 | if (json_is_string(j_temp))
|
||
514 | { |
||
515 | // borrowed
|
||
516 | options[opt] = json_string_value(j_temp); |
||
517 | if (options[opt] != NULL && strchr(options[opt], '=') != NULL) |
||
518 | { |
||
519 | opt++; |
||
520 | j_meta_unprocessed--; |
||
521 | } |
||
522 | } |
||
523 | } |
||
524 | // json key "META" has been processed
|
||
525 | j_unprocessed--; |
||
526 | } |
||
527 | |||
528 | // then add the external-name if present (implies HTTP PUT)
|
||
529 | if (gclxname != NULL) |
||
530 | options[opt++] = gclxname; |
||
531 | d0a04e62 | Eric Allman | |
532 | 04ad7859 | Rick Pratt | // and finally terminate the argv-style options pointer array
|
533 | options[opt++] = NULL;
|
||
534 | 18188fcc | Eric Allman | |
535 | 04ad7859 | Rick Pratt | // unprocessed client content is treated as an error
|
536 | if (j_unprocessed > 0 || j_meta_unprocessed > 0) |
||
537 | 18188fcc | Eric Allman | { |
538 | 04ad7859 | Rick Pratt | estat = error400(req, "request contains unrecognized json objects");
|
539 | return estat;
|
||
540 | } |
||
541 | 18188fcc | Eric Allman | |
542 | 04ad7859 | Rick Pratt | // prep to capture child's stdout
|
543 | if ((pipe(o_pipe)) == -1) |
||
544 | { |
||
545 | estat = error500(req, "gdp-rest pipe", errno);
|
||
546 | return estat;
|
||
547 | 18188fcc | Eric Allman | } |
548 | 04ad7859 | Rick Pratt | |
549 | if ((pid = fork()) == -1) |
||
550 | 18188fcc | Eric Allman | { |
551 | 04ad7859 | Rick Pratt | estat = error500(req, "gdp-rest fork", errno);
|
552 | close(o_pipe[STDOUT_FILENO]); |
||
553 | close(o_pipe[STDIN_FILENO]); |
||
554 | return estat;
|
||
555 | 18188fcc | Eric Allman | } |
556 | 04ad7859 | Rick Pratt | |
557 | if (pid == 0) |
||
558 | 18188fcc | Eric Allman | { |
559 | 04ad7859 | Rick Pratt | // child moves child side of pipe to own stdout
|
560 | dup2(o_pipe[STDOUT_FILENO], STDOUT_FILENO); |
||
561 | close(o_pipe[STDOUT_FILENO]); |
||
562 | // child closes parent side of pipe
|
||
563 | close(o_pipe[STDIN_FILENO]); |
||
564 | eae1d3ec | Eric Allman | // child execv gdp-create
|
565 | execv(exec_gdp_create, (char * const*)options); |
||
566 | 04ad7859 | Rick Pratt | // child execv launch failed
|
567 | perror("gdp-rest execv failure");
|
||
568 | exit(EXIT_FAILURE); |
||
569 | } |
||
570 | 18188fcc | Eric Allman | |
571 | 04ad7859 | Rick Pratt | // parent closes child side of pipe
|
572 | close(o_pipe[STDOUT_FILENO]); |
||
573 | 80c5389d | Eric Allman | |
574 | 04ad7859 | Rick Pratt | // wait for child
|
575 | if (waitpid(pid, &status, 0) != pid) |
||
576 | { |
||
577 | estat = error500(req, "gdp-rest waitpid", errno);
|
||
578 | close(o_pipe[STDIN_FILENO]); |
||
579 | return estat;
|
||
580 | } |
||
581 | |||
582 | // check child status for unexpected exits
|
||
583 | if (! WIFEXITED(status) ||
|
||
584 | (exit_code = WEXITSTATUS(status)) == EXIT_FAILURE) |
||
585 | { |
||
586 | estat = gdp_failure(req, "500", "Internal Server Error", "sds", |
||
587 | eae1d3ec | Eric Allman | "detail", "gdp-rest execv gdp-create failure", |
588 | 04ad7859 | Rick Pratt | "status", status,
|
589 | "request", req->body);
|
||
590 | close(o_pipe[STDIN_FILENO]); |
||
591 | return estat;
|
||
592 | } |
||
593 | eae1d3ec | Eric Allman | ep_dbg_cprintf(Dbg, 5, "gdp-rest exec gdp-create exited(%d)\n", exit_code); |
594 | d0a04e62 | Eric Allman | |
595 | 04ad7859 | Rick Pratt | if (exit_code == EX_OK)
|
596 | { |
||
597 | ssize_t bytes; |
||
598 | a555e771 | Rick Pratt | char *created;
|
599 | 04ad7859 | Rick Pratt | char obuf[GCL_C_OUT_BUF_SIZE];
|
600 | |||
601 | bytes = read(o_pipe[STDIN_FILENO], obuf, sizeof obuf - 1); |
||
602 | if (bytes < 0) |
||
603 | { |
||
604 | estat = error500(req, "gdp-rest pipe read", errno);
|
||
605 | close(o_pipe[STDIN_FILENO]); |
||
606 | return estat;
|
||
607 | } |
||
608 | obuf[bytes] = '\0';
|
||
609 | ep_dbg_cprintf(Dbg, 5, "pipe read %ld bytes = {\n%s}\n", bytes, obuf); |
||
610 | eae1d3ec | Eric Allman | if (bytes < sizeof gdp_c_min_out) |
611 | 04ad7859 | Rick Pratt | { |
612 | estat = gdp_failure(req, "500", "Internal Server Error", "ss", |
||
613 | "detail", "gdp-rest pipe read bytes", |
||
614 | "read", obuf);
|
||
615 | close(o_pipe[STDIN_FILENO]); |
||
616 | return estat;
|
||
617 | } |
||
618 | 80c5389d | Eric Allman | |
619 | 04ad7859 | Rick Pratt | // new
|
620 | if ((j_resp = json_object()) == NULL) |
||
621 | { |
||
622 | estat = error500(req, "gdp-rest response", ENOMEM);
|
||
623 | close(o_pipe[STDIN_FILENO]); |
||
624 | return estat;
|
||
625 | } |
||
626 | |||
627 | a555e771 | Rick Pratt | obuf[bytes - 1] = '\0'; // terminate obuf |
628 | |||
629 | // validate input (i.e. find "Created new GCL ...", which is not first)
|
||
630 | created = strstr((const char *)obuf, "Created"); |
||
631 | if (created == NULL) |
||
632 | { |
||
633 | estat = gdp_failure(req, "500", "Internal Server Error", "ss", |
||
634 | "detail", "gdp-rest pipe read created", |
||
635 | "read", obuf);
|
||
636 | de9d8389 | Rick Pratt | json_decref(j_resp); |
637 | a555e771 | Rick Pratt | close(o_pipe[STDIN_FILENO]); |
638 | return estat;
|
||
639 | } |
||
640 | d0a04e62 | Eric Allman | |
641 | eae1d3ec | Eric Allman | // gdp-create EX_OK output is kept stable to permit string extraction
|
642 | a555e771 | Rick Pratt | created[GCL_C_OUT_GCL_NAME_END] = '\0'; // terminate a newline of line 1 |
643 | 04ad7859 | Rick Pratt | |
644 | // new
|
||
645 | a555e771 | Rick Pratt | j_temp = json_string(&created[GCL_C_OUT_GCL_NAME]); |
646 | 04ad7859 | Rick Pratt | if (j_temp == NULL) |
647 | { |
||
648 | a555e771 | Rick Pratt | estat = error500(req, "gdp-rest response gcl_name", EIO);
|
649 | 04ad7859 | Rick Pratt | json_decref(j_resp); |
650 | close(o_pipe[STDIN_FILENO]); |
||
651 | return estat;
|
||
652 | } |
||
653 | de9d8389 | Rick Pratt | // set_new steals new json_string (or auto-decrefs if !j || !key)
|
654 | a555e771 | Rick Pratt | if ((json_object_set_new_nocheck(j_resp, "gcl_name", j_temp)) == -1) |
655 | 04ad7859 | Rick Pratt | { |
656 | estat = gdp_failure(req, "500", "Internal Server Error", "ss", |
||
657 | "detail", "gdp-rest response", |
||
658 | a555e771 | Rick Pratt | "gcl_name", &created[GCL_C_OUT_GCL_NAME]);
|
659 | 04ad7859 | Rick Pratt | json_decref(j_resp); |
660 | close(o_pipe[STDIN_FILENO]); |
||
661 | return estat;
|
||
662 | } |
||
663 | 0cb06b6b | Rick Pratt | |
664 | 04ad7859 | Rick Pratt | // malloc
|
665 | if ((jbuf = json_dumps(j_resp, JSON_INDENT(4))) == NULL) |
||
666 | { |
||
667 | estat = error500(req, "gdp-rest response reformat", EIO);
|
||
668 | json_decref(j_resp); |
||
669 | close(o_pipe[STDIN_FILENO]); |
||
670 | return estat;
|
||
671 | } |
||
672 | 18188fcc | Eric Allman | snprintf(sbuf, sizeof sbuf,
|
673 | 04ad7859 | Rick Pratt | "HTTP/1.1 201 GCL created\r\n"
|
674 | "Content-Type: application/json\r\n"
|
||
675 | "\r\n"
|
||
676 | "%s\r\n",
|
||
677 | jbuf); |
||
678 | 18188fcc | Eric Allman | write_scgi(req, sbuf); |
679 | 04ad7859 | Rick Pratt | free(jbuf); |
680 | json_decref(j_resp); |
||
681 | } |
||
682 | else if ((exit_code == EX_CANTCREAT) && |
||
683 | (req->request_method == SCGI_METHOD_PUT)) |
||
684 | estat = error409(req, "external-name already exists on gdplogd server");
|
||
685 | else if (exit_code == EX_CANTCREAT) |
||
686 | estat = error409(req, "generated-name conflict on gdplogd server");
|
||
687 | else if (exit_code == EX_NOHOST) |
||
688 | estat = error400(req, "log server host not found");
|
||
689 | else if (exit_code == EX_UNAVAILABLE) |
||
690 | estat = error400(req, "key length selection insecure, denied");
|
||
691 | else
|
||
692 | estat = gdp_failure(req, "500", "Internal Server Error", "sd", |
||
693 | eae1d3ec | Eric Allman | "detail", "gdp-create unexpected error", |
694 | 04ad7859 | Rick Pratt | "exit_code", exit_code);
|
695 | 80c5389d | Eric Allman | |
696 | 04ad7859 | Rick Pratt | close(o_pipe[STDIN_FILENO]); |
697 | return estat;
|
||
698 | } |
||
699 | |||
700 | |||
701 | /*
|
||
702 | a555e771 | Rick Pratt | ** A_NEW_GOB --- create new GDP Object
|
703 | 04ad7859 | Rick Pratt | */
|
704 | |||
705 | EP_STAT |
||
706 | eae1d3ec | Eric Allman | a_new_gob(scgi_request *req) |
707 | 04ad7859 | Rick Pratt | { |
708 | EP_STAT estat = EP_STAT_OK; |
||
709 | json_t *j; |
||
710 | eae1d3ec | Eric Allman | const char *gobxname; |
711 | 04ad7859 | Rick Pratt | size_t options_max; |
712 | json_t *j_temp; |
||
713 | json_t *j_meta; |
||
714 | d0a04e62 | Eric Allman | |
715 | 04ad7859 | Rick Pratt | ep_dbg_cprintf(Dbg, 5, "=== Create new GCL (%s)\n", req->body); |
716 | |||
717 | // new
|
||
718 | if ((j = json_loads(req->body, JSON_REJECT_DUPLICATES, NULL)) == NULL) |
||
719 | { |
||
720 | estat = error400(req, "request body not recognized json format");
|
||
721 | return estat;
|
||
722 | } |
||
723 | d0a04e62 | Eric Allman | |
724 | 04ad7859 | Rick Pratt | // mandatory external-name obj, to prevent URL crawl-driven log creation
|
725 | // borrowed
|
||
726 | if ((j_temp = json_object_get(j, "external-name")) == NULL) |
||
727 | { |
||
728 | estat = error400(req, "mandatory external-name not found");
|
||
729 | 80c5389d | Eric Allman | json_decref(j); |
730 | 04ad7859 | Rick Pratt | return estat;
|
731 | 18188fcc | Eric Allman | } |
732 | de9d8389 | Rick Pratt | json_incref(j_temp); |
733 | 04ad7859 | Rick Pratt | // borrowed
|
734 | eae1d3ec | Eric Allman | gobxname = json_string_value(j_temp); |
735 | 04ad7859 | Rick Pratt | |
736 | // external-name obj value must be NULL for POST, non-NULL for PUT
|
||
737 | eae1d3ec | Eric Allman | if ((req->request_method == SCGI_METHOD_POST && gobxname != NULL) || |
738 | (req->request_method == SCGI_METHOD_PUT && gobxname == NULL))
|
||
739 | 4f2d2174 | Eric Allman | { |
740 | eae1d3ec | Eric Allman | if (gobxname != NULL) |
741 | 04ad7859 | Rick Pratt | estat = error400(req, "POST external-name must have null value");
|
742 | else
|
||
743 | estat = error400(req, "PUT external-name must have non-null value");
|
||
744 | de9d8389 | Rick Pratt | json_decref(j_temp); |
745 | 04ad7859 | Rick Pratt | json_decref(j); |
746 | return estat;
|
||
747 | 4f2d2174 | Eric Allman | } |
748 | 04ad7859 | Rick Pratt | |
749 | // procname + 2 * (server params + client params) + glxname + terminator
|
||
750 | options_max = 1 + 2 * (GCL_C_PARAM_SERV_MAX + json_object_size(j)) + 1 + 1; |
||
751 | |||
752 | // peek at META array now, to size options array, then set aside for later
|
||
753 | // borrowed
|
||
754 | de9d8389 | Rick Pratt | j_meta = json_object_get(j, "META");
|
755 | json_incref(j_meta); |
||
756 | |||
757 | // size is 0 if !j_meta or !array
|
||
758 | options_max += json_array_size(j_meta); |
||
759 | 18188fcc | Eric Allman | |
760 | a555e771 | Rick Pratt | estat = process_gdp_create_req(req, gobxname, j, j_meta, options_max); |
761 | 3e05a791 | Eric Allman | |
762 | de9d8389 | Rick Pratt | // calls handle NULL
|
763 | json_decref(j_meta); |
||
764 | json_decref(j_temp); |
||
765 | 04ad7859 | Rick Pratt | json_decref(j); |
766 | return estat;
|
||
767 | } |
||
768 | 18188fcc | Eric Allman | |
769 | /*
|
||
770 | a555e771 | Rick Pratt | ** A_SHOW_GOB --- show information about a GDP Object
|
771 | 18188fcc | Eric Allman | */
|
772 | |||
773 | EP_STAT |
||
774 | eae1d3ec | Eric Allman | a_show_gob(scgi_request *req, gdp_name_t gobiname) |
775 | 18188fcc | Eric Allman | { |
776 | return error501(req, "GCL status not implemented"); |
||
777 | } |
||
778 | |||
779 | |||
780 | /*
|
||
781 | 05e1cb19 | Eric Allman | ** A_APPEND --- append datum to GCL
|
782 | 18188fcc | Eric Allman | */
|
783 | |||
784 | EP_STAT |
||
785 | eae1d3ec | Eric Allman | a_append(scgi_request *req, gdp_name_t gobiname, gdp_datum_t *datum) |
786 | 18188fcc | Eric Allman | { |
787 | EP_STAT estat = EP_STAT_OK; |
||
788 | eae1d3ec | Eric Allman | gdp_gin_t *gin = NULL;
|
789 | de9d8389 | Rick Pratt | char rbuf[SCGI_MAX_OUTBUF_SIZE];
|
790 | json_t *j; |
||
791 | char *jbuf;
|
||
792 | EP_TIME_SPEC ts; |
||
793 | 18188fcc | Eric Allman | |
794 | 05e1cb19 | Eric Allman | ep_dbg_cprintf(Dbg, 5, "=== Append value to GCL\n"); |
795 | 18188fcc | Eric Allman | |
796 | a555e771 | Rick Pratt | estat = gdp_gin_open(gobiname, GDP_MODE_AO, shared_gdp_open_info, &gin); |
797 | e64cc272 | Rick Pratt | EP_STAT_CHECK(estat, goto fail_open);
|
798 | 18188fcc | Eric Allman | |
799 | a555e771 | Rick Pratt | estat = gdp_gin_append(gin, datum, NULL);
|
800 | e64cc272 | Rick Pratt | EP_STAT_CHECK(estat, goto fail_append);
|
801 | d0a04e62 | Eric Allman | |
802 | de9d8389 | Rick Pratt | // success: send a response
|
803 | j = json_object(); |
||
804 | 18188fcc | Eric Allman | |
805 | de9d8389 | Rick Pratt | // set_new steals new json_integer (or auto-decrefs if !j || !key)
|
806 | json_object_set_new_nocheck(j, "recno",
|
||
807 | e64cc272 | Rick Pratt | json_integer(gdp_datum_getrecno(datum))); |
808 | de9d8389 | Rick Pratt | gdp_datum_getts(datum, &ts); |
809 | if (EP_TIME_IS_VALID(&ts))
|
||
810 | { |
||
811 | char tbuf[100]; |
||
812 | 80c5389d | Eric Allman | |
813 | de9d8389 | Rick Pratt | ep_time_format(&ts, tbuf, sizeof tbuf, EP_TIME_FMT_DEFAULT);
|
814 | // set_new steals new json_string (or auto-decrefs if !j || !key)
|
||
815 | json_object_set_new_nocheck(j, "timestamp", json_string(tbuf));
|
||
816 | } |
||
817 | 80c5389d | Eric Allman | |
818 | de9d8389 | Rick Pratt | // get it (malloc'ed) in string format (will return NULL if !j)
|
819 | jbuf = json_dumps(j, JSON_INDENT(4));
|
||
820 | 80c5389d | Eric Allman | |
821 | de9d8389 | Rick Pratt | // create the entire SCGI return message
|
822 | snprintf(rbuf, sizeof rbuf,
|
||
823 | "HTTP/1.1 200 Successfully appended\r\n"
|
||
824 | "Content-Type: application/json\r\n"
|
||
825 | "\r\n"
|
||
826 | "%s\r\n",
|
||
827 | jbuf ? jbuf : "");
|
||
828 | write_scgi(req, rbuf); |
||
829 | |||
830 | // clean up
|
||
831 | json_decref(j); |
||
832 | free(jbuf); |
||
833 | e64cc272 | Rick Pratt | |
834 | // caller frees datum
|
||
835 | d0a04e62 | Eric Allman | |
836 | de9d8389 | Rick Pratt | fail_append:
|
837 | eae1d3ec | Eric Allman | gdp_gin_close(gin); |
838 | de9d8389 | Rick Pratt | fail_open:
|
839 | if (!EP_STAT_ISOK(estat))
|
||
840 | e64cc272 | Rick Pratt | { |
841 | char ebuf[200]; |
||
842 | eae1d3ec | Eric Allman | gdp_pname_t gobpname; |
843 | e64cc272 | Rick Pratt | |
844 | eae1d3ec | Eric Allman | gdp_printable_name(gobiname, gobpname); |
845 | e64cc272 | Rick Pratt | gdp_failure(req, "420", "Cannot append to GCL", "ss", |
846 | eae1d3ec | Eric Allman | "GCL", gobpname,
|
847 | e64cc272 | Rick Pratt | "error", ep_stat_tostr(estat, ebuf, sizeof ebuf)); |
848 | } |
||
849 | de9d8389 | Rick Pratt | |
850 | 18188fcc | Eric Allman | return estat;
|
851 | } |
||
852 | |||
853 | |||
854 | /*
|
||
855 | ** A_READ_DATUM --- read and return a datum from a GCL
|
||
856 | 4e425382 | Eric Allman | **
|
857 | ** XXX Currently doesn't use the GCL cache. To make that work
|
||
858 | ** long term we would have to have to implement LRU in that
|
||
859 | ** cache (which we probably need to do anyway).
|
||
860 | */
|
||
861 | |||
862 | 47c6ea64 | Eric Allman | EP_STAT |
863 | eae1d3ec | Eric Allman | a_read_datum(scgi_request *req, gdp_name_t gobiname, gdp_recno_t recno) |
864 | 47c6ea64 | Eric Allman | { |
865 | 4e425382 | Eric Allman | EP_STAT estat; |
866 | eae1d3ec | Eric Allman | gdp_gin_t *gin = NULL;
|
867 | 9509f13b | Eric Allman | gdp_datum_t *datum = gdp_datum_new(); |
868 | 0c663d10 | Eric Allman | |
869 | a555e771 | Rick Pratt | estat = gdp_gin_open(gobiname, GDP_MODE_RO, shared_gdp_open_info, &gin); |
870 | e64cc272 | Rick Pratt | EP_STAT_CHECK(estat, goto fail_open);
|
871 | 47c6ea64 | Eric Allman | |
872 | a555e771 | Rick Pratt | estat = gdp_gin_read_by_recno(gin, recno, datum); |
873 | 4e425382 | Eric Allman | if (!EP_STAT_ISOK(estat))
|
874 | e64cc272 | Rick Pratt | goto fail_read;
|
875 | efad251c | Siqi Lin | |
876 | 4e425382 | Eric Allman | // package up the results and send them back
|
877 | 174e9f6d | Eric Allman | { |
878 | 4e425382 | Eric Allman | char rbuf[1024]; |
879 | 35509bdc | Eric Allman | |
880 | 4e425382 | Eric Allman | // figure out the response header
|
881 | { |
||
882 | FILE *fp; |
||
883 | eae1d3ec | Eric Allman | gdp_pname_t gobpname; |
884 | c5699a87 | Eric Allman | EP_TIME_SPEC ts; |
885 | 4e425382 | Eric Allman | |
886 | a5a912e2 | Eric Allman | fp = ep_fopen_smem(rbuf, sizeof rbuf, "w"); |
887 | 4e425382 | Eric Allman | if (fp == NULL) |
888 | { |
||
889 | 5bc1cdfb | Eric Allman | char nbuf[40]; |
890 | |||
891 | bfecf573 | Eric Allman | (void) (0 == strerror_r(errno, nbuf, sizeof nbuf)); |
892 | eae1d3ec | Eric Allman | ep_app_abort("Cannot open memory for GDP read response: %s",
|
893 | 5bc1cdfb | Eric Allman | nbuf); |
894 | 4e425382 | Eric Allman | } |
895 | eae1d3ec | Eric Allman | gdp_printable_name(gobiname, gobpname); |
896 | 4e425382 | Eric Allman | fprintf(fp, "HTTP/1.1 200 GCL Message\r\n"
|
897 | f63dbddd | Rick Pratt | "Content-Type: application/json\r\n"
|
898 | "GDP-GOB-Name: %s\r\n"
|
||
899 | "GDP-Record-Number: %" PRIgdp_recno "\r\n", |
||
900 | gobpname, |
||
901 | gdp_datum_getrecno(datum)); |
||
902 | c5699a87 | Eric Allman | gdp_datum_getts(datum, &ts); |
903 | if (EP_TIME_IS_VALID(&ts))
|
||
904 | 4e425382 | Eric Allman | { |
905 | fprintf(fp, "GDP-Commit-Timestamp: ");
|
||
906 | c5699a87 | Eric Allman | ep_time_print(&ts, fp, EP_TIME_FMT_DEFAULT); |
907 | 4e425382 | Eric Allman | fprintf(fp, "\r\n");
|
908 | } |
||
909 | fprintf(fp, "\r\n"); // end of header |
||
910 | fputc('\0', fp);
|
||
911 | fclose(fp); |
||
912 | } |
||
913 | |||
914 | // finish up sending the data out --- the extra copy is annoying
|
||
915 | { |
||
916 | size_t rlen = strlen(rbuf); |
||
917 | c5699a87 | Eric Allman | size_t dlen = evbuffer_get_length(gdp_datum_getbuf(datum)); |
918 | 4e425382 | Eric Allman | char obuf[1024]; |
919 | char *obp = obuf;
|
||
920 | |||
921 | if (rlen + dlen > sizeof obuf) |
||
922 | obp = ep_mem_malloc(rlen + dlen); |
||
923 | |||
924 | if (obp == NULL) |
||
925 | 5bc1cdfb | Eric Allman | { |
926 | char nbuf[40]; |
||
927 | |||
928 | bfecf573 | Eric Allman | (void) (0 == strerror_r(errno, nbuf, sizeof nbuf)); |
929 | 4e425382 | Eric Allman | ep_app_abort("Cannot allocate memory for GCL read response: %s",
|
930 | 5bc1cdfb | Eric Allman | nbuf); |
931 | } |
||
932 | 4e425382 | Eric Allman | |
933 | memcpy(obp, rbuf, rlen); |
||
934 | c5699a87 | Eric Allman | gdp_buf_read(gdp_datum_getbuf(datum), obp + rlen, dlen); |
935 | 4e425382 | Eric Allman | scgi_send(req, obp, rlen + dlen); |
936 | if (obp != obuf)
|
||
937 | ep_mem_free(obp); |
||
938 | } |
||
939 | 47c6ea64 | Eric Allman | } |
940 | 174e9f6d | Eric Allman | |
941 | 4e425382 | Eric Allman | // finished
|
942 | 9509f13b | Eric Allman | gdp_datum_free(datum); |
943 | eae1d3ec | Eric Allman | gdp_gin_close(gin); |
944 | 4e425382 | Eric Allman | return estat;
|
945 | 174e9f6d | Eric Allman | |
946 | e64cc272 | Rick Pratt | fail_read:
|
947 | eae1d3ec | Eric Allman | gdp_gin_close(gin); |
948 | e64cc272 | Rick Pratt | fail_open:
|
949 | gdp_datum_free(datum); |
||
950 | 4e425382 | Eric Allman | { |
951 | char ebuf[200]; |
||
952 | eae1d3ec | Eric Allman | gdp_pname_t gobpname; |
953 | 4e425382 | Eric Allman | |
954 | eae1d3ec | Eric Allman | gdp_printable_name(gobiname, gobpname); |
955 | a1a8f4f7 | Eric Allman | gdp_failure(req, "404", "Cannot read GCL", "ss", |
956 | eae1d3ec | Eric Allman | "GOB", gobpname,
|
957 | 4e425382 | Eric Allman | "reason", ep_stat_tostr(estat, ebuf, sizeof ebuf)); |
958 | } |
||
959 | return estat;
|
||
960 | 47c6ea64 | Eric Allman | } |
961 | |||
962 | 174e9f6d | Eric Allman | |
963 | 18188fcc | Eric Allman | /*
|
964 | a555e771 | Rick Pratt | ** GOB_DO_GET --- helper routine for GET method on a GDP Object
|
965 | 18188fcc | Eric Allman | **
|
966 | ** Have to look at query to figure out the semantics.
|
||
967 | ** The query's the thing / wherein I'll catch the
|
||
968 | ** conscience of the king.
|
||
969 | */
|
||
970 | |||
971 | EP_STAT |
||
972 | a555e771 | Rick Pratt | gob_do_get(scgi_request *req, gdp_name_t gobiname, struct qkvpair *qkvs)
|
973 | 47c6ea64 | Eric Allman | { |
974 | 18188fcc | Eric Allman | EP_STAT estat; |
975 | char *qrecno = find_query_kv("recno", qkvs); |
||
976 | char *qnrecs = find_query_kv("nrecs", qkvs); |
||
977 | char *qtimeout = find_query_kv("timeout", qkvs); |
||
978 | |||
979 | if (qnrecs != NULL) |
||
980 | 4e425382 | Eric Allman | { |
981 | 18188fcc | Eric Allman | // not yet implemented
|
982 | estat = error501(req, "nrecs query not supported");
|
||
983 | } |
||
984 | else if (qtimeout != NULL) |
||
985 | { |
||
986 | // not yet implemented
|
||
987 | estat = error501(req, "timeout query not supported");
|
||
988 | } |
||
989 | else if (qrecno != NULL) |
||
990 | { |
||
991 | 591ae6e9 | Eric Allman | gdp_recno_t recno; |
992 | |||
993 | if (strcmp(qrecno, "last") == 0) |
||
994 | recno = -1;
|
||
995 | else
|
||
996 | recno = atol(qrecno); |
||
997 | eae1d3ec | Eric Allman | estat = a_read_datum(req, gobiname, recno); |
998 | 18188fcc | Eric Allman | } |
999 | else
|
||
1000 | { |
||
1001 | a555e771 | Rick Pratt | estat = a_show_gob(req, gobiname); |
1002 | 18188fcc | Eric Allman | } |
1003 | |||
1004 | return estat;
|
||
1005 | } |
||
1006 | |||
1007 | |||
1008 | /*
|
||
1009 | ** PFX_GCL --- process SCGI requests starting with /gdp/v1/gcl
|
||
1010 | */
|
||
1011 | |||
1012 | #define NQUERY_KVS 10 // max number of key-value pairs in query part |
||
1013 | |||
1014 | EP_STAT |
||
1015 | pfx_gcl(scgi_request *req, char *uri)
|
||
1016 | { |
||
1017 | EP_STAT estat; |
||
1018 | struct qkvpair qkvs[NQUERY_KVS + 1]; |
||
1019 | |||
1020 | if (*uri == '/') |
||
1021 | uri++; |
||
1022 | |||
1023 | ep_dbg_cprintf(Dbg, 3, " gcl=%s\n query=%s\n", uri, req->query_string); |
||
1024 | |||
1025 | // parse the query, if it exists
|
||
1026 | { |
||
1027 | int i = parse_query(req->query_string, qkvs, NQUERY_KVS);
|
||
1028 | qkvs[i].key = qkvs[i].val = NULL;
|
||
1029 | } |
||
1030 | |||
1031 | if (*uri == '\0') |
||
1032 | { |
||
1033 | 04ad7859 | Rick Pratt | // URI does not include a GCL name, implies GDP scoped operations
|
1034 | 18188fcc | Eric Allman | switch (req->request_method)
|
1035 | { |
||
1036 | case SCGI_METHOD_POST:
|
||
1037 | 04ad7859 | Rick Pratt | // fall-through
|
1038 | case SCGI_METHOD_PUT:
|
||
1039 | a555e771 | Rick Pratt | // create a new GDP Object
|
1040 | estat = a_new_gob(req); |
||
1041 | 18188fcc | Eric Allman | break;
|
1042 | |||
1043 | case SCGI_METHOD_GET:
|
||
1044 | // XXX if no GCL name, should we print all GCLs?
|
||
1045 | 0b88875d | Eric Allman | estat = error404(req, "listing GCLs not implemented (yet)");
|
1046 | break;
|
||
1047 | |||
1048 | 2ffaa038 | Rick Pratt | case SCGI_METHOD_OPTIONS:
|
1049 | estat = cors200(req); |
||
1050 | break;
|
||
1051 | |||
1052 | 18188fcc | Eric Allman | default:
|
1053 | // unknown URI/method
|
||
1054 | 04ad7859 | Rick Pratt | estat = error405(req, |
1055 | "only GET, POST, or PUT supported in GDP scope");
|
||
1056 | 18188fcc | Eric Allman | break;
|
1057 | } |
||
1058 | } |
||
1059 | else
|
||
1060 | { |
||
1061 | 549b367d | Eric Allman | gdp_name_t gcliname; |
1062 | 18188fcc | Eric Allman | |
1063 | // next component is the GCL id (name) in external format
|
||
1064 | 549b367d | Eric Allman | gdp_parse_name(uri, gcliname); |
1065 | 18188fcc | Eric Allman | |
1066 | 04ad7859 | Rick Pratt | // URI includes a GCL name, implies GCL scoped operations
|
1067 | 18188fcc | Eric Allman | switch (req->request_method)
|
1068 | { |
||
1069 | case SCGI_METHOD_GET:
|
||
1070 | a555e771 | Rick Pratt | estat = gob_do_get(req, gcliname, qkvs); |
1071 | 18188fcc | Eric Allman | break;
|
1072 | |||
1073 | case SCGI_METHOD_POST:
|
||
1074 | // append value to GCL
|
||
1075 | 80d9fdd5 | Eric Allman | { |
1076 | gdp_datum_t *datum = gdp_datum_new(); |
||
1077 | |||
1078 | 04ad7859 | Rick Pratt | gdp_buf_write(gdp_datum_getbuf(datum), req->body, |
1079 | req->scgi_content_length); |
||
1080 | 05e1cb19 | Eric Allman | estat = a_append(req, gcliname, datum); |
1081 | 80d9fdd5 | Eric Allman | gdp_datum_free(datum); |
1082 | } |
||
1083 | 18188fcc | Eric Allman | break;
|
1084 | |||
1085 | 2ffaa038 | Rick Pratt | case SCGI_METHOD_OPTIONS:
|
1086 | estat = cors200(req); |
||
1087 | break;
|
||
1088 | |||
1089 | 18188fcc | Eric Allman | default:
|
1090 | // unknown URI/method
|
||
1091 | 04ad7859 | Rick Pratt | estat = error405(req, "only GET or POST supported in GCL scope");
|
1092 | 18188fcc | Eric Allman | } |
1093 | } |
||
1094 | |||
1095 | return estat;
|
||
1096 | 47c6ea64 | Eric Allman | } |
1097 | |||
1098 | 174e9f6d | Eric Allman | |
1099 | 18188fcc | Eric Allman | /*
|
1100 | ** PFX_POST --- process SCGI requests starting with /gdp/v1/post
|
||
1101 | */
|
||
1102 | |||
1103 | EP_STAT |
||
1104 | pfx_post(scgi_request *req, char *uri)
|
||
1105 | { |
||
1106 | if (*uri == '/') |
||
1107 | uri++; |
||
1108 | |||
1109 | return error501(req, "/post/... URIs not yet supported"); |
||
1110 | } |
||
1111 | |||
1112 | 174e9f6d | Eric Allman | |
1113 | 939e1aee | Eric Allman | |
1114 | /**********************************************************************
|
||
1115 | **
|
||
1116 | ** KEY-VALUE STORE
|
||
1117 | **
|
||
1118 | ** This implementation is very sloppy right now.
|
||
1119 | **
|
||
1120 | **********************************************************************/
|
||
1121 | |||
1122 | |||
1123 | 80d9fdd5 | Eric Allman | json_t *KeyValStore = NULL;
|
1124 | a555e771 | Rick Pratt | gdp_gin_t *KeyValGcl = NULL;
|
1125 | 80d9fdd5 | Eric Allman | const char *KeyValStoreName; |
1126 | 549b367d | Eric Allman | gdp_name_t KeyValInternalName; |
1127 | 939e1aee | Eric Allman | |
1128 | |||
1129 | 80d9fdd5 | Eric Allman | EP_STAT |
1130 | 939e1aee | Eric Allman | insert_datum(gdp_datum_t *datum) |
1131 | { |
||
1132 | if (datum == NULL) |
||
1133 | 80d9fdd5 | Eric Allman | return GDP_STAT_INTERNAL_ERROR;
|
1134 | 939e1aee | Eric Allman | |
1135 | gdp_buf_t *buf = gdp_datum_getbuf(datum); |
||
1136 | size_t len = gdp_buf_getlength(buf); |
||
1137 | unsigned char *p = gdp_buf_getptr(buf, len); |
||
1138 | |||
1139 | 0f868b09 | Eric Allman | json_t *j = json_loadb((char *) p, len, 0, NULL); |
1140 | 939e1aee | Eric Allman | if (!json_is_object(j))
|
1141 | 80d9fdd5 | Eric Allman | return GDP_STAT_MSGFMT;
|
1142 | 939e1aee | Eric Allman | |
1143 | json_object_update(KeyValStore, j); |
||
1144 | json_decref(j); |
||
1145 | 80d9fdd5 | Eric Allman | return EP_STAT_OK;
|
1146 | 939e1aee | Eric Allman | } |
1147 | |||
1148 | |||
1149 | EP_STAT |
||
1150 | kv_initialize(void)
|
||
1151 | { |
||
1152 | EP_STAT estat; |
||
1153 | gdp_event_t *gev; |
||
1154 | |||
1155 | 80d9fdd5 | Eric Allman | if (KeyValStore != NULL) |
1156 | return EP_STAT_OK;
|
||
1157 | |||
1158 | 939e1aee | Eric Allman | // get space for our internal database
|
1159 | KeyValStore = json_object(); |
||
1160 | |||
1161 | a29668f9 | Eric Allman | KeyValStoreName = ep_adm_getstrparam("swarm.rest.kvstore.gclname",
|
1162 | "swarm.rest.kvstore.gclname");
|
||
1163 | 80d9fdd5 | Eric Allman | |
1164 | 939e1aee | Eric Allman | // open the "KeyVal" GCL
|
1165 | 549b367d | Eric Allman | gdp_parse_name(KeyValStoreName, KeyValInternalName); |
1166 | a555e771 | Rick Pratt | estat = gdp_gin_open(KeyValInternalName, GDP_MODE_AO, NULL, &KeyValGcl);
|
1167 | 939e1aee | Eric Allman | EP_STAT_CHECK(estat, goto fail0);
|
1168 | |||
1169 | // read all the data
|
||
1170 | a555e771 | Rick Pratt | estat = gdp_gin_read_by_recno_async(KeyValGcl, 1, 0, NULL, NULL); |
1171 | 939e1aee | Eric Allman | EP_STAT_CHECK(estat, goto fail1);
|
1172 | 55b8c1ad | Eric Allman | while ((gev = gdp_event_next(KeyValGcl, 0)) != NULL) |
1173 | 939e1aee | Eric Allman | { |
1174 | 5ced6d34 | Eric Allman | if (gdp_event_gettype(gev) == GDP_EVENT_DONE)
|
1175 | 939e1aee | Eric Allman | { |
1176 | // end of multiread --- we have it all
|
||
1177 | gdp_event_free(gev); |
||
1178 | break;
|
||
1179 | } |
||
1180 | else if (gdp_event_gettype(gev) == GDP_EVENT_DATA) |
||
1181 | { |
||
1182 | // update the current stat of the key-value store
|
||
1183 | 80d9fdd5 | Eric Allman | estat = insert_datum(gdp_event_getdatum(gev)); |
1184 | 939e1aee | Eric Allman | } |
1185 | gdp_event_free(gev); |
||
1186 | } |
||
1187 | |||
1188 | // now start up the subscription (will be read in main loop)
|
||
1189 | a555e771 | Rick Pratt | estat = gdp_gin_subscribe_by_recno(KeyValGcl, 0, 0, NULL, NULL, NULL); |
1190 | 939e1aee | Eric Allman | goto done;
|
1191 | |||
1192 | fail0:
|
||
1193 | // couldn't open; try create?
|
||
1194 | 3e05a791 | Eric Allman | //estat = gdp_gcl_create(KeyValInternalName, NULL, &KeyValGcl);
|
1195 | 939e1aee | Eric Allman | |
1196 | fail1:
|
||
1197 | // couldn't read GCL
|
||
1198 | |||
1199 | done:
|
||
1200 | return estat;
|
||
1201 | } |
||
1202 | |||
1203 | |||
1204 | EP_STAT |
||
1205 | pfx_kv(scgi_request *req, char *uri)
|
||
1206 | { |
||
1207 | EP_STAT estat; |
||
1208 | |||
1209 | if (*uri == '/') |
||
1210 | uri++; |
||
1211 | |||
1212 | if (KeyValStore == NULL) |
||
1213 | { |
||
1214 | estat = kv_initialize(); |
||
1215 | EP_STAT_CHECK(estat, goto fail0);
|
||
1216 | } |
||
1217 | |||
1218 | if (req->request_method == SCGI_METHOD_POST)
|
||
1219 | { |
||
1220 | 80d9fdd5 | Eric Allman | // get the datum out of the SCGI request
|
1221 | 939e1aee | Eric Allman | gdp_datum_t *datum = gdp_datum_new(); |
1222 | c5699a87 | Eric Allman | gdp_buf_write(gdp_datum_getbuf(datum), req->body, req->scgi_content_length); |
1223 | 80d9fdd5 | Eric Allman | |
1224 | // try to merge it into the in-memory representation
|
||
1225 | estat = insert_datum(datum); |
||
1226 | |||
1227 | // if that succeeded, append the record to the GCL
|
||
1228 | if (EP_STAT_ISOK(estat))
|
||
1229 | 05e1cb19 | Eric Allman | a_append(req, KeyValInternalName, datum); |
1230 | 80d9fdd5 | Eric Allman | |
1231 | // don't forget to mop up!
|
||
1232 | 939e1aee | Eric Allman | gdp_datum_free(datum); |
1233 | } |
||
1234 | else if (req->request_method == SCGI_METHOD_GET) |
||
1235 | { |
||
1236 | FILE *fp; |
||
1237 | char rbuf[2000]; |
||
1238 | |||
1239 | a5a912e2 | Eric Allman | fp = ep_fopen_smem(rbuf, sizeof rbuf, "w"); |
1240 | 939e1aee | Eric Allman | if (fp == NULL) |
1241 | { |
||
1242 | char nbuf[40]; |
||
1243 | |||
1244 | bfecf573 | Eric Allman | (void) (0 == strerror_r(errno, nbuf, sizeof nbuf)); |
1245 | 939e1aee | Eric Allman | ep_app_abort("Cannot open memory for GCL read response: %s",
|
1246 | nbuf); |
||
1247 | } |
||
1248 | |||
1249 | json_t *j0 = json_object_get(KeyValStore, uri); |
||
1250 | if (j0 == NULL) |
||
1251 | return error404(req, "No such key"); |
||
1252 | |||
1253 | json_t *j1 = json_object(); |
||
1254 | json_object_set(j1, uri, j0); |
||
1255 | fprintf(fp, "HTTP/1.1 200 Data\r\n"
|
||
1256 | "Content-Type: application/json\r\n"
|
||
1257 | "\r\n"
|
||
1258 | "%s\r\n",
|
||
1259 | json_dumps(j1, 0));
|
||
1260 | fputc('\0', fp);
|
||
1261 | fclose(fp); |
||
1262 | json_decref(j1); |
||
1263 | |||
1264 | scgi_send(req, rbuf, strlen(rbuf)); |
||
1265 | } |
||
1266 | else
|
||
1267 | { |
||
1268 | return error405(req, "unknown method"); |
||
1269 | } |
||
1270 | |||
1271 | return EP_STAT_OK;
|
||
1272 | |||
1273 | fail0:
|
||
1274 | return error500(req, "Couldn't initialize Key-Value store", errno); |
||
1275 | } |
||
1276 | |||
1277 | |||
1278 | void
|
||
1279 | process_event(gdp_event_t *gev) |
||
1280 | { |
||
1281 | if (gdp_event_gettype(gev) != GDP_EVENT_DATA)
|
||
1282 | return;
|
||
1283 | |||
1284 | if (KeyValStore == NULL) |
||
1285 | return;
|
||
1286 | |||
1287 | // for the time being, assume it comes from the correct GCL
|
||
1288 | insert_datum(gdp_event_getdatum(gev)); |
||
1289 | } |
||
1290 | |||
1291 | |||
1292 | /**********************************************************************/
|
||
1293 | |||
1294 | 47c6ea64 | Eric Allman | /*
|
1295 | 4e425382 | Eric Allman | ** PROCESS_SCGI_REQ --- process an already-read SCGI request
|
1296 | 174e9f6d | Eric Allman | **
|
1297 | 4e425382 | Eric Allman | ** This is generally called as an event
|
1298 | 174e9f6d | Eric Allman | */
|
1299 | 47c6ea64 | Eric Allman | |
1300 | EP_STAT |
||
1301 | 1b267ec1 | Eric Allman | process_scgi_req(scgi_request *req) |
1302 | 47c6ea64 | Eric Allman | { |
1303 | 4e425382 | Eric Allman | char *uri; // the URI of the request |
1304 | EP_STAT estat = EP_STAT_OK; |
||
1305 | |||
1306 | if (ep_dbg_test(Dbg, 3)) |
||
1307 | 47c6ea64 | Eric Allman | { |
1308 | 4e425382 | Eric Allman | ep_dbg_printf("Got connection on port %d from %s:\n",
|
1309 | req->descriptor->port->port, req->remote_addr); |
||
1310 | ep_dbg_printf(" %s %s\n", scgi_method_name(req->request_method),
|
||
1311 | req->request_uri); |
||
1312 | 47c6ea64 | Eric Allman | } |
1313 | 4e425382 | Eric Allman | |
1314 | 18188fcc | Eric Allman | // strip query string off of URI (I'm surprised it's not already done)
|
1315 | uri = strchr(req->request_uri, '?');
|
||
1316 | if (uri != NULL) |
||
1317 | *uri = '\0';
|
||
1318 | |||
1319 | // strip off leading "/gdp/v1/" prefix (error if not there)
|
||
1320 | 4e425382 | Eric Allman | if (GclUriPrefix == NULL) |
1321 | 126f8146 | Eric Allman | GclUriPrefix = ep_adm_getstrparam("swarm.rest.prefix", DEF_URI_PREFIX);
|
1322 | 4e425382 | Eric Allman | uri = req->request_uri; |
1323 | if (strncmp(uri, GclUriPrefix, strlen(GclUriPrefix)) != 0) |
||
1324 | 18188fcc | Eric Allman | { |
1325 | estat = error404(req, "improper URI prefix");
|
||
1326 | goto finis;
|
||
1327 | } |
||
1328 | 4e425382 | Eric Allman | uri += strlen(GclUriPrefix); |
1329 | if (*uri == '/') |
||
1330 | uri++; |
||
1331 | |||
1332 | 18188fcc | Eric Allman | // next component is "gcl" for RESTful or "post" for RESTish
|
1333 | if (strncmp(uri, "gcl", 3) == 0 && (uri[3] == '/' || uri[3] == '\0')) |
||
1334 | 47c6ea64 | Eric Allman | { |
1335 | 18188fcc | Eric Allman | // looking at "/gdp/v1/gcl/" prefix; next component is the GCL name
|
1336 | estat = pfx_gcl(req, uri + 3);
|
||
1337 | 47c6ea64 | Eric Allman | } |
1338 | 18188fcc | Eric Allman | else if (strncmp(uri, "post", 4) == 0 && (uri[4] == '/' || uri[4] == '\0')) |
1339 | 4e425382 | Eric Allman | { |
1340 | 18188fcc | Eric Allman | // looking at "/gdp/v1/post" prefix
|
1341 | estat = pfx_post(req, uri + 4);
|
||
1342 | 4e425382 | Eric Allman | } |
1343 | 939e1aee | Eric Allman | else if (strncmp(uri, "kv", 2) == 0 && (uri[2] == '/' || uri[4] == '\0')) |
1344 | { |
||
1345 | // looking at "/gdp/v1/kv" prefix
|
||
1346 | estat = pfx_kv(req, uri + 2);
|
||
1347 | } |
||
1348 | 4e425382 | Eric Allman | else
|
1349 | { |
||
1350 | 18188fcc | Eric Allman | // looking at "/gdp/v1/<unknown>" prefix
|
1351 | estat = error404(req, "unknown resource");
|
||
1352 | 4e425382 | Eric Allman | } |
1353 | 174e9f6d | Eric Allman | |
1354 | 18188fcc | Eric Allman | finis:
|
1355 | 4e425382 | Eric Allman | return estat;
|
1356 | 47c6ea64 | Eric Allman | } |
1357 | |||
1358 | |||
1359 | int
|
||
1360 | main(int argc, char **argv, char **env) |
||
1361 | { |
||
1362 | 4e425382 | Eric Allman | int opt;
|
1363 | int listenport = -1; |
||
1364 | 1b267ec1 | Eric Allman | int64_t poll_delay; |
1365 | 5cd53c1a | Eric Allman | char *gdpd_addr = NULL; |
1366 | 591ae6e9 | Eric Allman | bool show_usage = false; |
1367 | 4e425382 | Eric Allman | extern void run_scgi_protocol(void); |
1368 | |||
1369 | 04ad7859 | Rick Pratt | while ((opt = getopt(argc, argv, "D:G:p:u:C:")) > 0) |
1370 | 47c6ea64 | Eric Allman | { |
1371 | 4e425382 | Eric Allman | switch (opt)
|
1372 | { |
||
1373 | case 'D': // turn on debugging |
||
1374 | ep_dbg_set(optarg); |
||
1375 | break;
|
||
1376 | 47c6ea64 | Eric Allman | |
1377 | 5cd53c1a | Eric Allman | case 'G': // gdp daemon host:port |
1378 | gdpd_addr = optarg; |
||
1379 | break;
|
||
1380 | |||
1381 | 4e425382 | Eric Allman | case 'p': // select listen port |
1382 | listenport = atoi(optarg); |
||
1383 | break;
|
||
1384 | 47c6ea64 | Eric Allman | |
1385 | 4e425382 | Eric Allman | case 'u': // URI prefix |
1386 | GclUriPrefix = optarg; |
||
1387 | break;
|
||
1388 | 591ae6e9 | Eric Allman | |
1389 | 04ad7859 | Rick Pratt | case 'C': // (development) gcl-create alternative |
1390 | a555e771 | Rick Pratt | exec_gdp_create = optarg; |
1391 | 04ad7859 | Rick Pratt | break;
|
1392 | |||
1393 | 591ae6e9 | Eric Allman | default:
|
1394 | show_usage = true;
|
||
1395 | break;
|
||
1396 | 4e425382 | Eric Allman | } |
1397 | 47c6ea64 | Eric Allman | } |
1398 | 4e425382 | Eric Allman | argc -= optind; |
1399 | argv += optind; |
||
1400 | 47c6ea64 | Eric Allman | |
1401 | 591ae6e9 | Eric Allman | if (show_usage || argc > 0) |
1402 | { |
||
1403 | fprintf(stderr, |
||
1404 | 04ad7859 | Rick Pratt | "Usage: %s [-D dbgspec] [-G host:port] [-p port] [-u prefix] "
|
1405 | "[-C gcl-create]\n", ep_app_getprogname());
|
||
1406 | 591ae6e9 | Eric Allman | exit(EX_USAGE); |
1407 | } |
||
1408 | |||
1409 | 4e425382 | Eric Allman | if (listenport < 0) |
1410 | cc7db91c | Eric Allman | listenport = ep_adm_getintparam("swarm.rest.scgi.port", 8001); |
1411 | 47c6ea64 | Eric Allman | |
1412 | 4e425382 | Eric Allman | // Initialize the GDP library
|
1413 | 1b267ec1 | Eric Allman | // Also initializes the EVENT library and starts the I/O thread
|
1414 | f7d9ac86 | Rick Pratt | // Initialize shared gcl open info with caching enabled
|
1415 | 4e425382 | Eric Allman | { |
1416 | 5cd53c1a | Eric Allman | EP_STAT estat = gdp_init(gdpd_addr); |
1417 | 4e425382 | Eric Allman | char ebuf[100]; |
1418 | 47c6ea64 | Eric Allman | |
1419 | 4e425382 | Eric Allman | if (!EP_STAT_ISOK(estat))
|
1420 | { |
||
1421 | 8404f1db | Rick Pratt | // DNS lookup failure is usually an external network
|
1422 | // issue, so exit with an errorcode and let the system
|
||
1423 | // control restart attempts
|
||
1424 | if (EP_STAT_IS_SAME(estat, EP_STAT_DNS_FAILURE))
|
||
1425 | { |
||
1426 | char nbuf[40]; |
||
1427 | |||
1428 | bfecf573 | Eric Allman | (void) (0 == strerror_r(errno, nbuf, sizeof nbuf)); |
1429 | 8404f1db | Rick Pratt | ep_app_error("Cannot initialize gdp library: %s", nbuf);
|
1430 | return EX_TEMPFAIL;
|
||
1431 | } |
||
1432 | |||
1433 | 4e425382 | Eric Allman | ep_app_abort("Cannot initialize gdp library: %s",
|
1434 | ep_stat_tostr(estat, ebuf, sizeof ebuf));
|
||
1435 | } |
||
1436 | |||
1437 | a555e771 | Rick Pratt | shared_gdp_open_info = gdp_open_info_new(); |
1438 | estat = gdp_open_info_set_caching(shared_gdp_open_info, true);
|
||
1439 | f7d9ac86 | Rick Pratt | |
1440 | if (!EP_STAT_ISOK(estat))
|
||
1441 | { |
||
1442 | ep_app_abort("Cannot initialize gdp gcl cache preference: %s",
|
||
1443 | ep_stat_tostr(estat, ebuf, sizeof ebuf));
|
||
1444 | } |
||
1445 | } |
||
1446 | d0a04e62 | Eric Allman | |
1447 | 0b88875d | Eric Allman | ep_dbg_cprintf(Dbg, 9, "GDP initialized\n"); |
1448 | |||
1449 | 4e425382 | Eric Allman | // Initialize SCGI library
|
1450 | 1d79b58a | Eric Allman | scgi_debug = ep_dbg_level(Dbg) / 10;
|
1451 | 4e425382 | Eric Allman | if (scgi_initialize(listenport))
|
1452 | efad251c | Siqi Lin | { |
1453 | 1d79b58a | Eric Allman | ep_dbg_cprintf(Dbg, 1,
|
1454 | "%s: listening for SCGI on port %d, scgi_debug %d\n",
|
||
1455 | ep_app_getprogname(), listenport, scgi_debug); |
||
1456 | 4e425382 | Eric Allman | } |
1457 | else
|
||
1458 | { |
||
1459 | 5bc1cdfb | Eric Allman | char nbuf[40]; |
1460 | |||
1461 | bfecf573 | Eric Allman | (void) (0 == strerror_r(errno, nbuf, sizeof nbuf)); |
1462 | 4e425382 | Eric Allman | ep_app_error("could not initialize SCGI port %d: %s",
|
1463 | 5bc1cdfb | Eric Allman | listenport, nbuf); |
1464 | 4e425382 | Eric Allman | return EX_OSERR;
|
1465 | 47c6ea64 | Eric Allman | } |
1466 | 63b801e4 | Eric Allman | |
1467 | 4e425382 | Eric Allman | // start looking for SCGI connections
|
1468 | // XXX This should really be done through the event library
|
||
1469 | // rather than by polling. To do this right there should
|
||
1470 | // be a pool of worker threads that would have the SCGI
|
||
1471 | // connection handed off to them.
|
||
1472 | // XXX May be able to cheat by changing the select() in
|
||
1473 | // scgi_update_connections_port to wait. It's OK if this
|
||
1474 | // thread hangs since the other work happens in a different
|
||
1475 | // thread.
|
||
1476 | f34c3e4e | Rick Pratt | //
|
1477 | // originally 100000 x 1000LL gave > 200ms response time for EMG demo tests
|
||
1478 | // reducing the poll to 1000 x 1000LL gave 110.83 ms average responses
|
||
1479 | // reducing the poll to 10 x 1000LL improve minimum but widen standard dev.
|
||
1480 | // dialed back the poll to 100 x 1000LL for the 2017 EC Annual Review
|
||
1481 | // Ali/Andy sent a video showing EMG control of the waffle robot with
|
||
1482 | // this setting -- too cool! -- commit the new setting to the repo!
|
||
1483 | poll_delay = ep_adm_getlongparam("swarm.rest.scgi.pollinterval", 100); |
||
1484 | a0672b22 | Eric Allman | ep_sd_notifyf("READY=1\n");
|
1485 | 4e425382 | Eric Allman | for (;;)
|
1486 | 63b801e4 | Eric Allman | { |
1487 | 939e1aee | Eric Allman | gdp_event_t *gev; |
1488 | 1d79b58a | Eric Allman | EP_TIME_SPEC to = { 0, 0, 0.0 }; |
1489 | 939e1aee | Eric Allman | |
1490 | 1d79b58a | Eric Allman | while ((gev = gdp_event_next(NULL, &to)) != NULL) |
1491 | 939e1aee | Eric Allman | { |
1492 | process_event(gev); |
||
1493 | gdp_event_free(gev); |
||
1494 | } |
||
1495 | |||
1496 | 1b267ec1 | Eric Allman | scgi_request *req = gdp_scgi_recv(); |
1497 | 4e425382 | Eric Allman | int dead = 0; |
1498 | |||
1499 | if (req == NULL) |
||
1500 | { |
||
1501 | (void) ep_time_nanosleep(poll_delay * 1000LL); |
||
1502 | continue;
|
||
1503 | } |
||
1504 | req->dead = &dead; |
||
1505 | 1b267ec1 | Eric Allman | process_scgi_req(req); |
1506 | 63b801e4 | Eric Allman | } |
1507 | 47c6ea64 | Eric Allman | } |