xref: /lighttpd1.4/src/mod_fastcgi.c (revision fc01b820)
1 #include "first.h"
2 
3 #include <sys/types.h>
4 #include <stdlib.h>
5 #include <string.h>
6 
7 #include "gw_backend.h"
8 typedef gw_plugin_config plugin_config;
9 typedef gw_plugin_data   plugin_data;
10 typedef gw_handler_ctx   handler_ctx;
11 
12 #include "base.h"
13 #include "buffer.h"
14 #include "fdevent.h"
15 #include "http_chunk.h"
16 #include "log.h"
17 #include "status_counter.h"
18 
19 #include "compat/fastcgi.h"
20 
21 #if GW_RESPONDER  != FCGI_RESPONDER
22 #error "mismatched defines: (GW_RESPONDER != FCGI_RESPONDER)"
23 #endif
24 #if GW_AUTHORIZER != FCGI_AUTHORIZER
25 #error "mismatched defines: (GW_AUTHORIZER != FCGI_AUTHORIZER)"
26 #endif
27 #if GW_FILTER     != FCGI_FILTER
28 #error "mismatched defines: (GW_FILTER != FCGI_FILTER)"
29 #endif
30 
31 static void mod_fastcgi_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
32     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
33       case 0: /* fastcgi.server */
34         if (cpv->vtype == T_CONFIG_LOCAL) {
35             gw_plugin_config * const gw = cpv->v.v;
36             pconf->exts      = gw->exts;
37             pconf->exts_auth = gw->exts_auth;
38             pconf->exts_resp = gw->exts_resp;
39         }
40         break;
41       case 1: /* fastcgi.balance */
42         /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/
43             pconf->balance = (int)cpv->v.u;
44         break;
45       case 2: /* fastcgi.debug */
46         pconf->debug = (int)cpv->v.u;
47         break;
48       case 3: /* fastcgi.map-extensions */
49         pconf->ext_mapping = cpv->v.a;
50         break;
51       default:/* should not happen */
52         return;
53     }
54 }
55 
56 static void mod_fastcgi_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
57     do {
58         mod_fastcgi_merge_config_cpv(pconf, cpv);
59     } while ((++cpv)->k_id != -1);
60 }
61 
62 static void mod_fastcgi_patch_config(request_st * const r, plugin_data * const p) {
63     memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
64     for (int i = 1, used = p->nconfig; i < used; ++i) {
65         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
66             mod_fastcgi_merge_config(&p->conf,p->cvlist + p->cvlist[i].v.u2[0]);
67     }
68 }
69 
70 SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
71     static const config_plugin_keys_t cpk[] = {
72       { CONST_STR_LEN("fastcgi.server"),
73         T_CONFIG_ARRAY_KVARRAY,
74         T_CONFIG_SCOPE_CONNECTION }
75      ,{ CONST_STR_LEN("fastcgi.balance"),
76         T_CONFIG_STRING,
77         T_CONFIG_SCOPE_CONNECTION }
78      ,{ CONST_STR_LEN("fastcgi.debug"),
79         T_CONFIG_INT,
80         T_CONFIG_SCOPE_CONNECTION }
81      ,{ CONST_STR_LEN("fastcgi.map-extensions"),
82         T_CONFIG_ARRAY_KVSTRING,
83         T_CONFIG_SCOPE_CONNECTION }
84      ,{ NULL, 0,
85         T_CONFIG_UNSET,
86         T_CONFIG_SCOPE_UNSET }
87     };
88 
89     plugin_data * const p = p_d;
90     if (!config_plugin_values_init(srv, p, cpk, "mod_fastcgi"))
91         return HANDLER_ERROR;
92 
93     /* process and validate config directives
94      * (init i to 0 if global context; to 1 to skip empty global context) */
95     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
96         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
97         for (; -1 != cpv->k_id; ++cpv) {
98             switch (cpv->k_id) {
99               case 0:{/* fastcgi.server */
100                 gw_plugin_config *gw = calloc(1, sizeof(gw_plugin_config));
101                 force_assert(gw);
102                 if (!gw_set_defaults_backend(srv, p, cpv->v.a, gw, 0,
103                                              cpk[cpv->k_id].k)) {
104                     gw_plugin_config_free(gw);
105                     return HANDLER_ERROR;
106                 }
107                 cpv->v.v = gw;
108                 cpv->vtype = T_CONFIG_LOCAL;
109                 break;
110               }
111               case 1: /* fastcgi.balance */
112                 cpv->v.u = (unsigned int)gw_get_defaults_balance(srv, cpv->v.b);
113                 break;
114               case 2: /* fastcgi.debug */
115               case 3: /* fastcgi.map-extensions */
116                 break;
117               default:/* should not happen */
118                 break;
119             }
120         }
121     }
122 
123     /* default is 0 */
124     /*p->defaults.balance = (unsigned int)gw_get_defaults_balance(srv, NULL);*/
125 
126     /* initialize p->defaults from global config context */
127     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
128         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
129         if (-1 != cpv->k_id)
130             mod_fastcgi_merge_config(&p->defaults, cpv);
131     }
132 
133     return HANDLER_GO_ON;
134 }
135 
136 
137 static int fcgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) {
138 	buffer *env = venv;
139 	size_t len;
140 	char len_enc[8];
141 	size_t len_enc_len = 0;
142 	char *dst;
143 
144 	if (!key || (!val && val_len)) return -1;
145 
146 	len = key_len + val_len;
147 
148 	len += key_len > 127 ? 4 : 1;
149 	len += val_len > 127 ? 4 : 1;
150 
151 	if (buffer_string_length(env) + len >= FCGI_MAX_LENGTH + sizeof(FCGI_BeginRequestRecord) + sizeof(FCGI_Header)) {
152 		/**
153 		 * we can't append more headers, ignore it
154 		 */
155 		return -1;
156 	}
157 
158 	/**
159 	 * field length can be 31bit max
160 	 *
161 	 * HINT: this can't happen as FCGI_MAX_LENGTH is only 16bit
162 	 */
163 	force_assert(key_len < 0x7fffffffu);
164 	force_assert(val_len < 0x7fffffffu);
165 
166 	if (buffer_string_space(env) < len) {
167 		size_t extend = env->size * 2 - buffer_string_length(env);
168 		extend = extend > len ? extend : len + 4095;
169 		buffer_string_prepare_append(env, extend);
170 	}
171 
172 	if (key_len > 127) {
173 		len_enc[len_enc_len++] = ((key_len >> 24) & 0xff) | 0x80;
174 		len_enc[len_enc_len++] = (key_len >> 16) & 0xff;
175 		len_enc[len_enc_len++] = (key_len >> 8) & 0xff;
176 		len_enc[len_enc_len++] = (key_len >> 0) & 0xff;
177 	} else {
178 		len_enc[len_enc_len++] = (key_len >> 0) & 0xff;
179 	}
180 
181 	if (val_len > 127) {
182 		len_enc[len_enc_len++] = ((val_len >> 24) & 0xff) | 0x80;
183 		len_enc[len_enc_len++] = (val_len >> 16) & 0xff;
184 		len_enc[len_enc_len++] = (val_len >> 8) & 0xff;
185 		len_enc[len_enc_len++] = (val_len >> 0) & 0xff;
186 	} else {
187 		len_enc[len_enc_len++] = (val_len >> 0) & 0xff;
188 	}
189 
190 	dst = buffer_string_prepare_append(env, len);
191 	memcpy(dst, len_enc, len_enc_len);
192 	memcpy(dst + len_enc_len, key, key_len);
193 	memcpy(dst + len_enc_len + key_len, val, val_len);
194 	buffer_commit(env, len);
195 
196 	return 0;
197 }
198 
199 static void fcgi_header(FCGI_Header * header, unsigned char type, int request_id, int contentLength, unsigned char paddingLength) {
200 	force_assert(contentLength <= FCGI_MAX_LENGTH);
201 
202 	header->version = FCGI_VERSION_1;
203 	header->type = type;
204 	header->requestIdB0 = request_id & 0xff;
205 	header->requestIdB1 = (request_id >> 8) & 0xff;
206 	header->contentLengthB0 = contentLength & 0xff;
207 	header->contentLengthB1 = (contentLength >> 8) & 0xff;
208 	header->paddingLength = paddingLength;
209 	header->reserved = 0;
210 }
211 
212 static handler_t fcgi_stdin_append(handler_ctx *hctx) {
213 	FCGI_Header header;
214 	chunkqueue * const req_cq = &hctx->r->reqbody_queue;
215 	off_t offset, weWant;
216 	off_t req_cqlen = chunkqueue_length(req_cq);
217 	int request_id = hctx->request_id;
218 	if (req_cqlen > MAX_WRITE_LIMIT) req_cqlen = MAX_WRITE_LIMIT;
219 
220 	/* something to send ? */
221 	for (offset = 0; offset != req_cqlen; offset += weWant) {
222 		weWant = req_cqlen - offset > FCGI_MAX_LENGTH ? FCGI_MAX_LENGTH : req_cqlen - offset;
223 
224 		if (-1 != hctx->wb_reqlen) {
225 			if (hctx->wb_reqlen >= 0) {
226 				hctx->wb_reqlen += sizeof(header);
227 			} else {
228 				hctx->wb_reqlen -= sizeof(header);
229 			}
230 		}
231 
232 		fcgi_header(&(header), FCGI_STDIN, request_id, weWant, 0);
233 		(chunkqueue_is_empty(&hctx->wb) || hctx->wb.first->type == MEM_CHUNK) /* else FILE_CHUNK for temp file */
234 		  ? chunkqueue_append_mem(&hctx->wb, (const char *)&header, sizeof(header))
235 		  : chunkqueue_append_mem_min(&hctx->wb, (const char *)&header, sizeof(header));
236 		chunkqueue_steal(&hctx->wb, req_cq, weWant);
237 		/*(hctx->wb_reqlen already includes reqbody_length)*/
238 	}
239 
240 	if (hctx->wb.bytes_in == hctx->wb_reqlen) {
241 		/* terminate STDIN */
242 		/* (future: must defer ending FCGI_STDIN
243 		 *  if might later upgrade protocols
244 		 *  and then have more data to send) */
245 		fcgi_header(&(header), FCGI_STDIN, request_id, 0, 0);
246 		chunkqueue_append_mem(&hctx->wb, (const char *)&header, sizeof(header));
247 		hctx->wb_reqlen += (int)sizeof(header);
248 	}
249 
250 	return HANDLER_GO_ON;
251 }
252 
253 static handler_t fcgi_create_env(handler_ctx *hctx) {
254 	FCGI_BeginRequestRecord beginRecord;
255 	FCGI_Header header;
256 	int request_id;
257 
258 	gw_host *host = hctx->host;
259 	request_st * const r = hctx->r;
260 
261 	http_cgi_opts opts = {
262 	  (hctx->gw_mode == FCGI_AUTHORIZER),
263 	  host->break_scriptfilename_for_php,
264 	  host->docroot,
265 	  host->strip_request_uri
266 	};
267 
268 	size_t rsz = (size_t)(r->read_queue.bytes_out - hctx->wb.bytes_in);
269 	if (rsz >= 65536) rsz = r->rqst_header_len;
270 	buffer * const b = chunkqueue_prepend_buffer_open_sz(&hctx->wb, rsz);
271 
272 	/* send FCGI_BEGIN_REQUEST */
273 
274 	if (hctx->request_id == 0) {
275 		hctx->request_id = 1; /* always use id 1 as we don't use multiplexing */
276 	} else {
277 		log_error(r->conf.errh, __FILE__, __LINE__,
278 		  "fcgi-request is already in use: %d", hctx->request_id);
279 	}
280 	request_id = hctx->request_id;
281 
282 	fcgi_header(&(beginRecord.header), FCGI_BEGIN_REQUEST, request_id, sizeof(beginRecord.body), 0);
283 	beginRecord.body.roleB0 = hctx->gw_mode;
284 	beginRecord.body.roleB1 = 0;
285 	beginRecord.body.flags = 0;
286 	memset(beginRecord.body.reserved, 0, sizeof(beginRecord.body.reserved));
287 
288 	buffer_copy_string_len(b, (const char *)&beginRecord, sizeof(beginRecord));
289 	fcgi_header(&header, FCGI_PARAMS, request_id, 0, 0); /*(set aside space to fill in later)*/
290 	buffer_append_string_len(b, (const char *)&header, sizeof(header));
291 
292 	/* send FCGI_PARAMS */
293 
294 	if (0 != http_cgi_headers(r, &opts, fcgi_env_add, b)) {
295 		r->http_status = 400;
296 		r->handler_module = NULL;
297 		buffer_clear(b);
298 		chunkqueue_remove_finished_chunks(&hctx->wb);
299 		return HANDLER_FINISHED;
300 	} else {
301 		fcgi_header(&(header), FCGI_PARAMS, request_id,
302 			    buffer_string_length(b) - sizeof(FCGI_BeginRequestRecord) - sizeof(FCGI_Header), 0);
303 		memcpy(b->ptr+sizeof(FCGI_BeginRequestRecord), (const char *)&header, sizeof(header));
304 
305 		fcgi_header(&(header), FCGI_PARAMS, request_id, 0, 0);
306 		buffer_append_string_len(b, (const char *)&header, sizeof(header));
307 
308 		hctx->wb_reqlen = buffer_string_length(b);
309 		chunkqueue_prepend_buffer_commit(&hctx->wb);
310 	}
311 
312 	if (r->reqbody_length) {
313 		/*chunkqueue_append_chunkqueue(&hctx->wb, &r->reqbody_queue);*/
314 		if (r->reqbody_length > 0)
315 			hctx->wb_reqlen += r->reqbody_length;/* (eventual) (minimal) total request size, not necessarily including all fcgi_headers around content length yet */
316 		else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/
317 			hctx->wb_reqlen = -hctx->wb_reqlen;
318 	}
319 	fcgi_stdin_append(hctx);
320 
321 	status_counter_inc(CONST_STR_LEN("fastcgi.requests"));
322 	return HANDLER_GO_ON;
323 }
324 
325 typedef struct {
326 	unsigned int len;
327 	int      type;
328 	int      padding;
329 	int      request_id;
330 } fastcgi_response_packet;
331 
332 static int fastcgi_get_packet(handler_ctx *hctx, fastcgi_response_packet *packet) {
333 	FCGI_Header header;
334 	off_t rblen = chunkqueue_length(hctx->rb);
335 	if (rblen < (off_t)sizeof(FCGI_Header)) {
336 		/* no header */
337 		if (hctx->conf.debug && 0 != rblen) {
338 			log_error(hctx->r->conf.errh, __FILE__, __LINE__,
339 			  "FastCGI: header too small: %lld bytes < %zu bytes, "
340 			  "waiting for more data", (long long)rblen, sizeof(FCGI_Header));
341 		}
342 		return -1;
343 	}
344 	char *ptr = (char *)&header;
345 	uint32_t rd = sizeof(FCGI_Header);
346 	if (chunkqueue_peek_data(hctx->rb, &ptr, &rd, hctx->r->conf.errh) < 0)
347 		return -1;
348 	if (rd != sizeof(FCGI_Header))
349 		return -1;
350 	if (ptr != (char *)&header) /* copy into aligned struct */
351 		memcpy(&header, ptr, sizeof(FCGI_Header));
352 
353 	/* we have at least a header, now check how much we have to fetch */
354 	packet->len = (header.contentLengthB0 | (header.contentLengthB1 << 8)) + header.paddingLength;
355 	packet->request_id = (header.requestIdB0 | (header.requestIdB1 << 8));
356 	packet->type = header.type;
357 	packet->padding = header.paddingLength;
358 
359 	if (packet->len > (unsigned int)rblen-sizeof(FCGI_Header)) {
360 		return -1; /* we didn't get the full packet */
361 	}
362 
363 	chunkqueue_mark_written(hctx->rb, sizeof(FCGI_Header));
364 	return 0;
365 }
366 
367 static void fastcgi_get_packet_body(buffer * const b, handler_ctx * const hctx, const fastcgi_response_packet * const packet) {
368     /* copy content; hctx->rb must contain at least packet->len content */
369     /* (read entire packet and then truncate padding, if present) */
370     const uint32_t blen = buffer_string_length(b);
371     if (chunkqueue_read_data(hctx->rb,
372                              buffer_string_prepare_append(b, packet->len),
373                              packet->len, hctx->r->conf.errh) < 0)
374         return; /*(should not happen; should all be in memory)*/
375     buffer_string_set_length(b, blen + packet->len - packet->padding);
376 }
377 
378 __attribute_cold__
379 __attribute_noinline__
380 static int
381 mod_fastcgi_chunk_decode_transfer_cqlen (request_st * const r, chunkqueue * const src, const unsigned int len)
382 {
383     if (0 == len) return 0;
384 
385     /* specialized for mod_fastcgi to decode chunked encoding;
386      * FastCGI packet data is all type MEM_CHUNK
387      * entire src cq is processed, minus packet.padding at end
388      * (This extra work can be avoided if FastCGI backend does not send
389      *  Transfer-Encoding: chunked, which FastCGI is not supposed to do) */
390     uint32_t remain = len, wr;
391     for (const chunk *c = src->first; c && remain; c = c->next, remain -= wr) {
392         /*assert(c->type == MEM_CHUNK);*/
393         wr = buffer_string_length(c->mem) - c->offset;
394         if (wr > remain) wr = remain;
395         if (0 != http_chunk_decode_append_mem(r, c->mem->ptr+c->offset, wr))
396             return -1;
397     }
398     chunkqueue_mark_written(src, len);
399     return 0;
400 }
401 
402 static int
403 mod_fastcgi_transfer_cqlen (request_st * const r, chunkqueue * const src, const unsigned int len)
404 {
405     return (!r->resp_decode_chunked)
406       ? http_chunk_transfer_cqlen(r, src, len)
407       : mod_fastcgi_chunk_decode_transfer_cqlen(r, src, len);
408 }
409 
410 static handler_t fcgi_recv_parse(request_st * const r, struct http_response_opts_t *opts, buffer *b, size_t n) {
411 	handler_ctx *hctx = (handler_ctx *)opts->pdata;
412 	int fin = 0;
413 
414 	if (0 == n) {
415 		if (-1 == hctx->request_id) return HANDLER_FINISHED; /*(flag request ended)*/
416 		if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)
417 		    && !(r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_POLLRDHUP))
418 			return HANDLER_GO_ON;
419 		log_error(r->conf.errh, __FILE__, __LINE__,
420 		  "unexpected end-of-file (perhaps the fastcgi process died):"
421 		  "pid: %d socket: %s",
422 		  hctx->proc->pid, hctx->proc->connection_name->ptr);
423 
424 		return HANDLER_ERROR;
425 	}
426 
427 	chunkqueue_append_buffer(hctx->rb, b);
428 
429 	/*
430 	 * parse the fastcgi packets and forward the content to the write-queue
431 	 *
432 	 */
433 	while (fin == 0) {
434 		fastcgi_response_packet packet;
435 
436 		/* check if we have at least one packet */
437 		if (0 != fastcgi_get_packet(hctx, &packet)) {
438 			/* no full packet */
439 			break;
440 		}
441 
442 		switch(packet.type) {
443 		case FCGI_STDOUT:
444 			if (packet.len == 0) break;
445 
446 			/* is the header already finished */
447 			if (0 == r->resp_body_started) {
448 				/* split header from body */
449 				buffer *hdrs = hctx->response;
450 				if (NULL == hdrs) {
451 					hdrs = r->tmp_buf;
452 					buffer_clear(hdrs);
453 				}
454 				fastcgi_get_packet_body(hdrs, hctx, &packet);
455 				if (HANDLER_GO_ON != http_response_parse_headers(r, &hctx->opts, hdrs)) {
456 					hctx->send_content_body = 0;
457 					fin = 1;
458 					break;
459 				}
460 				if (0 == r->resp_body_started) {
461 					if (!hctx->response) {
462 						hctx->response = chunk_buffer_acquire();
463 						buffer_copy_buffer(hctx->response, hdrs);
464 					}
465 				}
466 				else if (hctx->gw_mode == GW_AUTHORIZER &&
467 					 (r->http_status == 0 || r->http_status == 200)) {
468 					/* authorizer approved request; ignore the content here */
469 					hctx->send_content_body = 0;
470 				}
471 			} else if (hctx->send_content_body) {
472 				if (0 != mod_fastcgi_transfer_cqlen(r, hctx->rb, packet.len - packet.padding)) {
473 					/* error writing to tempfile;
474 					 * truncate response or send 500 if nothing sent yet */
475 					fin = 1;
476 				}
477 				if (packet.padding) chunkqueue_mark_written(hctx->rb, packet.padding);
478 			} else {
479 				chunkqueue_mark_written(hctx->rb, packet.len);
480 			}
481 			break;
482 		case FCGI_STDERR:
483 			if (packet.len) {
484 				buffer * const tb = r->tmp_buf;
485 				buffer_clear(tb);
486 				fastcgi_get_packet_body(tb, hctx, &packet);
487 				log_error_multiline_buffer(r->conf.errh, __FILE__, __LINE__, tb,
488 				  "FastCGI-stderr:");
489 			}
490 			break;
491 		case FCGI_END_REQUEST:
492 			hctx->request_id = -1; /*(flag request ended)*/
493 			fin = 1;
494 			break;
495 		default:
496 			log_error(r->conf.errh, __FILE__, __LINE__,
497 			  "FastCGI: header.type not handled: %d", packet.type);
498 			chunkqueue_mark_written(hctx->rb, packet.len);
499 			break;
500 		}
501 	}
502 
503 	return 0 == fin ? HANDLER_GO_ON : HANDLER_FINISHED;
504 }
505 
506 static handler_t fcgi_check_extension(request_st * const r, void *p_d, int uri_path_handler) {
507 	plugin_data *p = p_d;
508 	handler_t rc;
509 
510 	if (NULL != r->handler_module) return HANDLER_GO_ON;
511 
512 	mod_fastcgi_patch_config(r, p);
513 	if (NULL == p->conf.exts) return HANDLER_GO_ON;
514 
515 	rc = gw_check_extension(r, p, uri_path_handler, 0);
516 	if (HANDLER_GO_ON != rc) return rc;
517 
518 	if (r->handler_module == p->self) {
519 		handler_ctx *hctx = r->plugin_ctx[p->id];
520 		hctx->opts.backend = BACKEND_FASTCGI;
521 		hctx->opts.parse = fcgi_recv_parse;
522 		hctx->opts.pdata = hctx;
523 		hctx->stdin_append = fcgi_stdin_append;
524 		hctx->create_env = fcgi_create_env;
525 		if (!hctx->rb) {
526 			hctx->rb = chunkqueue_init(NULL);
527 		}
528 		else {
529 			chunkqueue_reset(hctx->rb);
530 		}
531 	}
532 
533 	return HANDLER_GO_ON;
534 }
535 
536 /* uri-path handler */
537 static handler_t fcgi_check_extension_1(request_st * const r, void *p_d) {
538 	return fcgi_check_extension(r, p_d, 1);
539 }
540 
541 /* start request handler */
542 static handler_t fcgi_check_extension_2(request_st * const r, void *p_d) {
543 	return fcgi_check_extension(r, p_d, 0);
544 }
545 
546 
547 int mod_fastcgi_plugin_init(plugin *p);
548 int mod_fastcgi_plugin_init(plugin *p) {
549 	p->version      = LIGHTTPD_VERSION_ID;
550 	p->name         = "fastcgi";
551 
552 	p->init         = gw_init;
553 	p->cleanup      = gw_free;
554 	p->set_defaults = mod_fastcgi_set_defaults;
555 	p->handle_request_reset    = gw_handle_request_reset;
556 	p->handle_uri_clean        = fcgi_check_extension_1;
557 	p->handle_subrequest_start = fcgi_check_extension_2;
558 	p->handle_subrequest       = gw_handle_subrequest;
559 	p->handle_trigger          = gw_handle_trigger;
560 	p->handle_waitpid          = gw_handle_waitpid_cb;
561 
562 	return 0;
563 }
564