source: bin/varnishd/cache_center.c @ 0b06c85

Revision 0b06c85, 32.7 KB checked in by Poul-Henning Kamp <phk@…>, 3 years ago (diff)

Replay of SVN r5764:

Retire the old ESI code.

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2006 Verdens Gang AS
3 * Copyright (c) 2006-2010 Redpill Linpro AS
4 * All rights reserved.
5 *
6 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * This file contains the central state machine for pushing requests.
30 *
31 * We cannot just use direct calls because it is possible to kick a
32 * request back to the lookup stage (usually after a rewrite).  The
33 * state engine also allows us to break the processing up into some
34 * logical chunks which improves readability a little bit.
35 *
36 * Since the states are rather nasty in detail, I have decided to embedd
37 * a dot(1) graph in the source code comments.  So to see the big picture,
38 * extract the DOT lines and run though dot(1), for instance with the
39 * command:
40 *      sed -n '/^DOT/s///p' cache_center.c | dot -Tps > /tmp/_.ps
41 */
42
43/*
44DOT digraph vcl_center {
45xDOT    page="8.2,11.5"
46DOT     size="7.2,10.5"
47DOT     margin="0.5"
48DOT     center="1"
49DOT acceptor [
50DOT     shape=hexagon
51DOT     label="Request received"
52DOT ]
53DOT ERROR [shape=plaintext]
54DOT RESTART [shape=plaintext]
55DOT acceptor -> start [style=bold,color=green,weight=4]
56 */
57
58#include "config.h"
59
60#include "svnid.h"
61SVNID("$Id$")
62
63#include <stdio.h>
64#include <errno.h>
65#include <math.h>
66#include <poll.h>
67#include <stdlib.h>
68#include <string.h>
69#include <unistd.h>
70
71#ifndef HAVE_SRANDOMDEV
72#include "compat/srandomdev.h"
73#endif
74
75#include "vcl.h"
76#include "cli_priv.h"
77#include "cache.h"
78#include "hash_slinger.h"
79#include "stevedore.h"
80#include "vsha256.h"
81
82static unsigned xids;
83
84/*--------------------------------------------------------------------
85 * WAIT
86 * Wait (briefly) until we have a full request in our htc.
87 */
88
89static int
90cnt_wait(struct sess *sp)
91{
92        int i;
93        struct pollfd pfd[1];
94
95        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
96        AZ(sp->vcl);
97        AZ(sp->obj);
98        assert(sp->xid == 0);
99
100        i = HTC_Complete(sp->htc);
101        if (i == 0 && params->session_linger > 0) {
102                pfd[0].fd = sp->fd;
103                pfd[0].events = POLLIN;
104                pfd[0].revents = 0;
105                i = poll(pfd, 1, params->session_linger);
106                if (i)
107                        i = HTC_Rx(sp->htc);
108        }
109        if (i == 0) {
110                WSL(sp->wrk, SLT_Debug, sp->fd, "herding");
111                sp->wrk->stats.sess_herd++;
112                SES_Charge(sp);
113                sp->wrk = NULL;
114                vca_return_session(sp);
115                return (1);
116        }
117        if (i == 1) {
118                sp->step = STP_START;
119                return (0);
120        }
121        if (i == -2) {
122                vca_close_session(sp, "overflow");
123                return (0);
124        }
125        if (i == -1 && Tlen(sp->htc->rxbuf) == 0 &&
126            (errno == 0 || errno == ECONNRESET))
127                vca_close_session(sp, "EOF");
128        else
129                vca_close_session(sp, "error");
130        sp->step = STP_DONE;
131        return (0);
132}
133
134/*--------------------------------------------------------------------
135 * We have a refcounted object on the session, now deliver it.
136 *
137DOT subgraph xcluster_deliver {
138DOT     deliver [
139DOT             shape=ellipse
140DOT             label="Filter obj.->resp."
141DOT     ]
142DOT     vcl_deliver [
143DOT             shape=record
144DOT             label="vcl_deliver()|resp."
145DOT     ]
146DOT     deliver2 [
147DOT             shape=ellipse
148DOT             label="Send resp + body"
149DOT     ]
150DOT     deliver -> vcl_deliver [style=bold,color=green,weight=4]
151DOT     vcl_deliver -> deliver2 [style=bold,color=green,weight=4,label=deliver]
152DOT     vcl_deliver -> errdeliver [label="error"]
153DOT     errdeliver [label="ERROR",shape=plaintext]
154DOT     vcl_deliver -> rstdeliver [label="restart",color=purple]
155DOT     rstdeliver [label="RESTART",shape=plaintext]
156DOT }
157DOT deliver2 -> DONE [style=bold,color=green,weight=4]
158 *
159 */
160
161static int
162cnt_deliver(struct sess *sp)
163{
164
165        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
166        CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
167        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
168
169        sp->wrk->res_mode = RES_LEN;
170
171        if (!sp->disable_esi && sp->obj->esidata != NULL) {
172                /* In ESI mode, we don't know the aggregate length */
173                sp->wrk->res_mode &= ~RES_LEN;
174                sp->wrk->res_mode |= RES_ESI;
175        }
176
177        if (sp->esi_level > 0) {
178                sp->wrk->res_mode &= ~RES_LEN;
179                sp->wrk->res_mode |= RES_ESI_CHILD;
180        }
181
182        if (params->http_gzip_support &&
183            http_HdrIs(sp->obj->http, H_Content_Encoding, "gzip") &&
184            !RFC2616_Req_Gzip(sp)) {
185                /*
186                 * We don't know what it uncompresses to
187                 * XXX: we could cache that
188                 */
189                sp->wrk->res_mode &= ~RES_LEN;
190                sp->wrk->res_mode |= RES_GUNZIP;
191        }
192
193        if (!(sp->wrk->res_mode & (RES_LEN|RES_CHUNKED|RES_EOF))) {
194                if (sp->obj->len == 0)
195                        /*
196                         * If the object is empty, neither ESI nor GUNZIP
197                         * can make it any different size
198                         */
199                        sp->wrk->res_mode |= RES_LEN;
200                else if (!sp->wantbody) {
201                        /* Nothing */
202                } else if (sp->http->protover >= 1.1) {
203                        sp->wrk->res_mode |= RES_CHUNKED;
204                } else {
205                        sp->wrk->res_mode |= RES_EOF;
206                        sp->doclose = "EOF mode";
207                }
208        }
209
210        sp->t_resp = TIM_real();
211        if (sp->obj->objcore != NULL) {
212                if ((sp->t_resp - sp->obj->last_lru) > params->lru_timeout)
213                        EXP_Touch(sp->obj, sp->t_resp);
214                sp->obj->last_use = sp->t_resp; /* XXX: locking ? */
215        }
216        sp->wrk->resp = sp->wrk->http[2];
217        http_Setup(sp->wrk->resp, sp->wrk->ws);
218        RES_BuildHttp(sp);
219        VCL_deliver_method(sp);
220        switch (sp->handling) {
221        case VCL_RET_DELIVER:
222                break;
223        case VCL_RET_RESTART:
224                if (sp->restarts >= params->max_restarts)
225                        break;
226                (void)HSH_Deref(sp->wrk, NULL, &sp->obj);
227                AZ(sp->obj);
228                sp->restarts++;
229                sp->director = NULL;
230                sp->wrk->bereq = NULL;
231                sp->wrk->beresp = NULL;
232                sp->wrk->beresp1 = NULL;
233                sp->wrk->resp = NULL;
234                sp->step = STP_RECV;
235                return (0);
236        default:
237                WRONG("Illegal action in vcl_deliver{}");
238        }
239
240        sp->director = NULL;
241        sp->restarts = 0;
242
243        RES_WriteObj(sp);
244
245        AZ(sp->wrk->wfd);
246        (void)HSH_Deref(sp->wrk, NULL, &sp->obj);
247        sp->wrk->resp = NULL;
248        sp->step = STP_DONE;
249        return (0);
250}
251
252/*--------------------------------------------------------------------
253 * This is the final state, figure out if we should close or recycle
254 * the client connection
255 *
256DOT     DONE [
257DOT             shape=hexagon
258DOT             label="Request completed"
259DOT     ]
260 */
261
262static int
263cnt_done(struct sess *sp)
264{
265        double dh, dp, da;
266        int i;
267
268        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
269        CHECK_OBJ_ORNULL(sp->vcl, VCL_CONF_MAGIC);
270
271        AZ(sp->obj);
272        AZ(sp->vbc);
273        sp->director = NULL;
274        sp->restarts = 0;
275
276        if (sp->vcl != NULL && sp->esi_level == 0) {
277                if (sp->wrk->vcl != NULL)
278                        VCL_Rel(&sp->wrk->vcl);
279                sp->wrk->vcl = sp->vcl;
280                sp->vcl = NULL;
281        }
282
283        SES_Charge(sp);
284
285        sp->t_end = TIM_real();
286        sp->wrk->lastused = sp->t_end;
287        if (sp->xid == 0) {
288                sp->t_req = sp->t_end;
289                sp->t_resp = sp->t_end;
290        } else if (sp->esi_level == 0) {
291                dp = sp->t_resp - sp->t_req;
292                da = sp->t_end - sp->t_resp;
293                dh = sp->t_req - sp->t_open;
294                /* XXX: Add StatReq == StatSess */
295                WSP(sp, SLT_Length, "%u", sp->acct_req.bodybytes);
296                WSL(sp->wrk, SLT_ReqEnd, sp->id, "%u %.9f %.9f %.9f %.9f %.9f",
297                    sp->xid, sp->t_req, sp->t_end, dh, dp, da);
298        }
299        sp->xid = 0;
300        sp->t_open = sp->t_end;
301        sp->t_resp = NAN;
302        WSL_Flush(sp->wrk, 0);
303
304        /* If we did an ESI include, don't mess up our state */
305        if (sp->esi_level > 0)
306                return (1);
307
308        memset(&sp->acct_req, 0, sizeof sp->acct_req);
309
310        sp->t_req = NAN;
311        sp->hash_always_miss = 0;
312        sp->hash_ignore_busy = 0;
313
314        if (sp->fd >= 0 && sp->doclose != NULL) {
315                /*
316                 * This is an orderly close of the connection; ditch nolinger
317                 * before we close, to get queued data transmitted.
318                 */
319                // XXX: not yet (void)TCP_linger(sp->fd, 0);
320                vca_close_session(sp, sp->doclose);
321        }
322
323        if (sp->fd < 0) {
324                sp->wrk->stats.sess_closed++;
325                sp->wrk = NULL;
326                SES_Delete(sp);
327                return (1);
328        }
329
330        if (sp->wrk->stats.client_req >= params->wthread_stats_rate)
331                WRK_SumStat(sp->wrk);
332        /* Reset the workspace to the session-watermark */
333        WS_Reset(sp->ws, sp->ws_ses);
334        WS_Reset(sp->wrk->ws, NULL);
335
336        i = HTC_Reinit(sp->htc);
337        if (i == 1) {
338                sp->wrk->stats.sess_pipeline++;
339                sp->step = STP_START;
340                return (0);
341        }
342        if (Tlen(sp->htc->rxbuf)) {
343                sp->wrk->stats.sess_readahead++;
344                sp->step = STP_WAIT;
345                return (0);
346        }
347        if (params->session_linger > 0) {
348                sp->wrk->stats.sess_linger++;
349                sp->step = STP_WAIT;
350                return (0);
351        }
352        sp->wrk->stats.sess_herd++;
353        sp->wrk = NULL;
354        vca_return_session(sp);
355        return (1);
356}
357
358/*--------------------------------------------------------------------
359 * Emit an error
360 *
361DOT subgraph xcluster_error {
362DOT     vcl_error [
363DOT             shape=record
364DOT             label="vcl_error()|resp."
365DOT     ]
366DOT     ERROR -> vcl_error
367DOT     vcl_error-> deliver [label=deliver]
368DOT }
369 */
370
371static int
372cnt_error(struct sess *sp)
373{
374        struct worker *w;
375        struct http *h;
376        char date[40];
377
378        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
379
380        w = sp->wrk;
381        if (sp->obj == NULL) {
382                HSH_Prealloc(sp);
383                sp->wrk->cacheable = 0;
384                /* XXX: 1024 is a pure guess */
385                sp->obj = STV_NewObject(sp, NULL, 1024, 0,
386                     params->http_headers);
387                sp->obj->xid = sp->xid;
388                sp->obj->entered = sp->t_req;
389        } else {
390                /* XXX: Null the headers ? */
391        }
392        CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
393        h = sp->obj->http;
394
395        if (sp->err_code < 100 || sp->err_code > 999)
396                sp->err_code = 501;
397
398        http_PutProtocol(w, sp->fd, h, "HTTP/1.1");
399        http_PutStatus(h, sp->err_code);
400        TIM_format(TIM_real(), date);
401        http_PrintfHeader(w, sp->fd, h, "Date: %s", date);
402        http_PrintfHeader(w, sp->fd, h, "Server: Varnish");
403        http_PrintfHeader(w, sp->fd, h, "Retry-After: %d", params->err_ttl);
404
405        if (sp->err_reason != NULL)
406                http_PutResponse(w, sp->fd, h, sp->err_reason);
407        else
408                http_PutResponse(w, sp->fd, h,
409                    http_StatusMessage(sp->err_code));
410        VCL_error_method(sp);
411
412        if (sp->handling == VCL_RET_RESTART &&
413            sp->restarts <  params->max_restarts) {
414                HSH_Drop(sp);
415                sp->director = NULL;
416                sp->restarts++;
417                sp->step = STP_RECV;
418                return (0);
419        } else if (sp->handling == VCL_RET_RESTART)
420                sp->handling = VCL_RET_DELIVER;
421
422
423        /* We always close when we take this path */
424        sp->doclose = "error";
425        sp->wantbody = 1;
426
427        assert(sp->handling == VCL_RET_DELIVER);
428        sp->err_code = 0;
429        sp->err_reason = NULL;
430        sp->wrk->bereq = NULL;
431        sp->step = STP_DELIVER;
432        return (0);
433}
434
435/*--------------------------------------------------------------------
436 * We have fetched the headers from the backend, ask the VCL code what
437 * to do next, then head off in that direction.
438 *
439DOT subgraph xcluster_fetch {
440DOT     fetch [
441DOT             shape=ellipse
442DOT             label="fetch from backend\n(find obj.ttl)"
443DOT     ]
444DOT     vcl_fetch [
445DOT             shape=record
446DOT             label="vcl_fetch()|req.\nbereq.\nberesp."
447DOT     ]
448DOT     fetch -> vcl_fetch [style=bold,color=blue,weight=2]
449DOT     fetch_pass [
450DOT             shape=ellipse
451DOT             label="obj.pass=true"
452DOT     ]
453DOT     vcl_fetch -> fetch_pass [label="pass",style=bold,color=red]
454DOT }
455DOT fetch_pass -> deliver [style=bold,color=red]
456DOT vcl_fetch -> deliver [label="deliver",style=bold,color=blue,weight=2]
457DOT vcl_fetch -> recv [label="restart"]
458DOT vcl_fetch -> rstfetch [label="restart",color=purple]
459DOT rstfetch [label="RESTART",shape=plaintext]
460DOT fetch -> errfetch
461DOT vcl_fetch -> errfetch [label="error"]
462DOT errfetch [label="ERROR",shape=plaintext]
463 */
464
465static int
466cnt_fetch(struct sess *sp)
467{
468        int i;
469        struct http *hp, *hp2;
470        char *b;
471        unsigned l, nhttp;
472        int varyl = 0;
473        struct vsb *vary = NULL;
474
475        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
476        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
477
478        AN(sp->director);
479        AZ(sp->vbc);
480
481        /* sp->wrk->http[0] is (still) bereq */
482        sp->wrk->beresp = sp->wrk->http[1];
483        http_Setup(sp->wrk->beresp, sp->wrk->ws);
484
485        i = FetchHdr(sp);
486        /*
487         * If we recycle a backend connection, there is a finite chance
488         * that the backend closed it before we get a request to it.
489         * Do a single retry in that case.
490         */
491        if (i == 1) {
492                VSC_main->backend_retry++;
493                i = FetchHdr(sp);
494        }
495
496        /*
497         * These two headers can be spread over multiple actual headers
498         * and we rely on their content outside of VCL, so collect them
499         * into one line here.
500         */
501        http_CollectHdr(sp->wrk->beresp, H_Cache_Control);
502        http_CollectHdr(sp->wrk->beresp, H_Vary);
503
504        /*
505         * Save a copy before it might get mangled in VCL.  When it comes to
506         * dealing with the body, we want to see the unadultered headers.
507         */
508        sp->wrk->beresp1 = sp->wrk->http[2];
509        *sp->wrk->beresp1 = *sp->wrk->beresp;
510
511        if (i) {
512                if (sp->objcore != NULL) {
513                        CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
514                        AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
515                        sp->objcore = NULL;
516                }
517                AZ(sp->obj);
518                sp->wrk->bereq = NULL;
519                sp->wrk->beresp = NULL;
520                sp->wrk->beresp1 = NULL;
521                sp->err_code = 503;
522                sp->step = STP_ERROR;
523                return (0);
524        }
525
526        sp->err_code = http_GetStatus(sp->wrk->beresp);
527
528        /*
529         * Initial cacheability determination per [RFC2616, 13.4]
530         * We do not support ranges yet, so 206 is out.
531         */
532        switch (sp->err_code) {
533        case 200: /* OK */
534        case 203: /* Non-Authoritative Information */
535        case 300: /* Multiple Choices */
536        case 301: /* Moved Permanently */
537        case 302: /* Moved Temporarily */
538        case 410: /* Gone */
539        case 404: /* Not Found */
540                sp->wrk->cacheable = 1;
541                break;
542        default:
543                sp->wrk->cacheable = 0;
544                break;
545        }
546
547        sp->wrk->entered = TIM_real();
548        sp->wrk->age = 0;
549        sp->wrk->ttl = RFC2616_Ttl(sp);
550
551        if (sp->objcore == NULL)
552                sp->wrk->cacheable = 0;
553
554        sp->wrk->do_esi = 0;
555        sp->wrk->grace = NAN;
556
557        sp->wrk->body_status = RFC2616_Body(sp);
558
559        AZ(sp->wrk->storage_hint);
560
561        VCL_fetch_method(sp);
562
563        if (sp->objcore == NULL) {
564                /* This is a pass from vcl_recv */
565                sp->wrk->cacheable = 0;
566        } else if (!sp->wrk->cacheable && sp->objcore != NULL) {
567                AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
568                sp->objcore = NULL;
569        }
570
571        /*
572         * At this point we are either committed to flesh out the busy
573         * object we have in the hash or we have let go of it, if we ever
574         * had one.
575         */
576
577        if (sp->wrk->cacheable) {
578                CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
579                vary = VRY_Create(sp, sp->wrk->beresp);
580                if (vary != NULL) {
581                        varyl = vsb_len(vary);
582                        assert(varyl > 0);
583                }
584        } else {
585                AZ(sp->objcore);
586        }
587
588        AZ(sp->wrk->vfp);
589        /* XXX: precedence, also: do_esi */
590        if (sp->wrk->do_esi) {
591                sp->wrk->vfp = &vfp_esi;
592        } else
593        if (sp->wrk->do_gunzip &&
594            http_HdrIs(sp->wrk->beresp, H_Content_Encoding, "gzip")) {
595                http_Unset(sp->wrk->beresp, H_Content_Encoding);
596                sp->wrk->vfp = &vfp_gunzip;
597        } else if (sp->wrk->do_gzip &&
598            !http_HdrIs(sp->wrk->beresp, H_Content_Encoding, "gzip")) {
599                http_PrintfHeader(sp->wrk, sp->fd, sp->wrk->beresp,
600                    "Content-Encoding: %s", "gzip");
601                sp->wrk->vfp = &vfp_gzip;
602        }
603
604        l = http_EstimateWS(sp->wrk->beresp,
605            sp->pass ? HTTPH_R_PASS : HTTPH_A_INS, &nhttp);
606
607        if (vary != NULL)
608                l += varyl;
609
610        /*
611         * Space for producing a Content-Length: header including padding
612         * A billion gigabytes is enough for anybody.
613         */
614        l += strlen("Content-Encoding: XxxXxxXxxXxxXxxXxx" + sizeof(void *));
615
616        /*
617         * XXX: VFP's may affect estimate
618         */
619
620        sp->obj = STV_NewObject(sp, sp->wrk->storage_hint, l,
621            sp->wrk->ttl, nhttp);
622        CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
623
624        sp->wrk->storage_hint = NULL;
625
626        if (vary != NULL) {
627                sp->obj->vary =
628                    (void *)WS_Alloc(sp->obj->http->ws, varyl);
629                AN(sp->obj->vary);
630                memcpy(sp->obj->vary, vsb_data(vary), varyl);
631                vsb_delete(vary);
632                vary = NULL;
633        }
634
635        sp->obj->xid = sp->xid;
636        sp->obj->response = sp->err_code;
637        sp->obj->cacheable = sp->wrk->cacheable;
638        sp->obj->grace = sp->wrk->grace;
639        if (sp->obj->ttl == 0. && sp->obj->grace == 0.)
640                sp->obj->cacheable = 0;
641        sp->obj->age = sp->wrk->age;
642        sp->obj->entered = sp->wrk->entered;
643        WS_Assert(sp->obj->ws_o);
644
645
646        /* Filter into object */
647        hp = sp->wrk->beresp;
648        hp2 = sp->obj->http;
649
650        hp2->logtag = HTTP_Obj;
651        http_CopyResp(hp2, hp);
652        http_FilterFields(sp->wrk, sp->fd, hp2, hp,
653            sp->pass ? HTTPH_R_PASS : HTTPH_A_INS);
654        http_CopyHome(sp->wrk, sp->fd, hp2);
655
656        if (http_GetHdr(hp, H_Last_Modified, &b))
657                sp->obj->last_modified = TIM_parse(b);
658        else
659                sp->obj->last_modified = sp->wrk->entered;
660
661        i = FetchBody(sp);
662        sp->wrk->vfp = NULL;
663        AZ(sp->wrk->wfd);
664        AZ(sp->vbc);
665        AN(sp->director);
666
667        if (i) {
668                HSH_Drop(sp);
669                AZ(sp->obj);
670                sp->wrk->bereq = NULL;
671                sp->wrk->beresp = NULL;
672                sp->wrk->beresp1 = NULL;
673                sp->err_code = 503;
674                sp->step = STP_ERROR;
675                return (0);
676        }
677
678        switch (sp->handling) {
679        case VCL_RET_RESTART:
680                HSH_Drop(sp);
681                sp->director = NULL;
682                sp->restarts++;
683                sp->wrk->bereq = NULL;
684                sp->wrk->beresp = NULL;
685                sp->wrk->beresp1 = NULL;
686                sp->step = STP_RECV;
687                return (0);
688        case VCL_RET_PASS:
689                if (sp->obj->objcore != NULL)
690                        sp->obj->objcore->flags |= OC_F_PASS;
691                if (sp->obj->ttl - sp->t_req < params->default_ttl)
692                        sp->obj->ttl = sp->t_req + params->default_ttl;
693                break;
694        case VCL_RET_DELIVER:
695                break;
696        case VCL_RET_ERROR:
697                HSH_Drop(sp);
698                sp->wrk->bereq = NULL;
699                sp->wrk->beresp = NULL;
700                sp->wrk->beresp1 = NULL;
701                sp->step = STP_ERROR;
702                return (0);
703        default:
704                WRONG("Illegal action in vcl_fetch{}");
705        }
706
707        sp->obj->cacheable = 1;
708        if (sp->wrk->cacheable) {
709                EXP_Insert(sp->obj);
710                AN(sp->obj->objcore);
711                AN(sp->obj->objcore->ban);
712                HSH_Unbusy(sp);
713        }
714        sp->acct_tmp.fetch++;
715        sp->wrk->bereq = NULL;
716        sp->wrk->beresp = NULL;
717        sp->wrk->beresp1 = NULL;
718        sp->step = STP_DELIVER;
719        return (0);
720}
721
722/*--------------------------------------------------------------------
723 * The very first request
724 */
725static int
726cnt_first(struct sess *sp)
727{
728
729        /*
730         * XXX: If we don't have acceptfilters we are somewhat subject
731         * XXX: to DoS'ing here.  One remedy would be to set a shorter
732         * XXX: SO_RCVTIMEO and once we have received something here
733         * XXX: increase it to the normal value.
734         */
735
736        assert(sp->xid == 0);
737        assert(sp->restarts == 0);
738        VCA_Prep(sp);
739
740        /* Record the session watermark */
741        sp->ws_ses = WS_Snapshot(sp->ws);
742
743        /* Receive a HTTP protocol request */
744        HTC_Init(sp->htc, sp->ws, sp->fd);
745        sp->wrk->lastused = sp->t_open;
746        sp->acct_tmp.sess++;
747
748        sp->step = STP_WAIT;
749        return (0);
750}
751
752/*--------------------------------------------------------------------
753 * HIT
754 * We had a cache hit.  Ask VCL, then march off as instructed.
755 *
756DOT subgraph xcluster_hit {
757DOT     hit [
758DOT             shape=record
759DOT             label="vcl_hit()|req.\nobj."
760DOT     ]
761DOT }
762DOT hit -> err_hit [label="error"]
763DOT err_hit [label="ERROR",shape=plaintext]
764DOT hit -> rst_hit [label="restart",color=purple]
765DOT rst_hit [label="RESTART",shape=plaintext]
766DOT hit -> pass [label=pass,style=bold,color=red]
767DOT hit -> deliver [label="deliver",style=bold,color=green,weight=4]
768 */
769
770static int
771cnt_hit(struct sess *sp)
772{
773
774        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
775        CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
776        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
777
778        assert(!(sp->obj->objcore->flags & OC_F_PASS));
779
780        VCL_hit_method(sp);
781
782        if (sp->handling == VCL_RET_DELIVER) {
783                /* Dispose of any body part of the request */
784                (void)FetchReqBody(sp);
785                sp->wrk->bereq = NULL;
786                sp->step = STP_DELIVER;
787                return (0);
788        }
789
790        /* Drop our object, we won't need it */
791        (void)HSH_Deref(sp->wrk, NULL, &sp->obj);
792        sp->objcore = NULL;
793
794        switch(sp->handling) {
795        case VCL_RET_PASS:
796                sp->step = STP_PASS;
797                return (0);
798        case VCL_RET_ERROR:
799                sp->step = STP_ERROR;
800                return (0);
801        case VCL_RET_RESTART:
802                sp->director = NULL;
803                sp->restarts++;
804                sp->step = STP_RECV;
805                return (0);
806        default:
807                WRONG("Illegal action in vcl_hit{}");
808        }
809}
810
811/*--------------------------------------------------------------------
812 * LOOKUP
813 * Hash things together and look object up in hash-table.
814 *
815 * LOOKUP consists of two substates so that we can reenter if we
816 * encounter a busy object.
817 *
818DOT subgraph xcluster_lookup {
819DOT     hash [
820DOT             shape=record
821DOT             label="vcl_hash()|req."
822DOT     ]
823DOT     lookup [
824DOT             shape=diamond
825DOT             label="obj in cache ?\ncreate if not"
826DOT     ]
827DOT     lookup2 [
828DOT             shape=diamond
829DOT             label="obj.pass ?"
830DOT     ]
831DOT     hash -> lookup [label="hash",style=bold,color=green,weight=4]
832DOT     lookup -> lookup2 [label="yes",style=bold,color=green,weight=4]
833DOT }
834DOT lookup2 -> hit [label="no", style=bold,color=green,weight=4]
835DOT lookup2 -> pass [label="yes",style=bold,color=red]
836DOT lookup -> miss [label="no",style=bold,color=blue,weight=2]
837 */
838
839static int
840cnt_lookup(struct sess *sp)
841{
842        struct objcore *oc;
843        struct object *o;
844        struct objhead *oh;
845
846        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
847        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
848
849
850        oc = HSH_Lookup(sp, &oh);
851
852        if (oc == NULL) {
853                /*
854                 * We lost the session to a busy object, disembark the
855                 * worker thread.   The hash code to restart the session,
856                 * still in STP_LOOKUP, later when the busy object isn't.
857                 * NB:  Do not access sp any more !
858                 */
859                return (1);
860        }
861
862        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
863        CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
864
865        /* If we inserted a new object it's a miss */
866        if (oc->flags & OC_F_BUSY) {
867                sp->wrk->stats.cache_miss++;
868
869                sp->objcore = oc;
870                sp->step = STP_MISS;
871                return (0);
872        }
873
874        o = oc_getobj(sp->wrk, oc);
875        CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
876        sp->obj = o;
877
878        if (oc->flags & OC_F_PASS) {
879                sp->wrk->stats.cache_hitpass++;
880                WSP(sp, SLT_HitPass, "%u", sp->obj->xid);
881                (void)HSH_Deref(sp->wrk, NULL, &sp->obj);
882                sp->objcore = NULL;
883                sp->step = STP_PASS;
884                return (0);
885        }
886
887        sp->wrk->stats.cache_hit++;
888        WSP(sp, SLT_Hit, "%u", sp->obj->xid);
889        sp->step = STP_HIT;
890        return (0);
891}
892
893/*--------------------------------------------------------------------
894 * We had a miss, ask VCL, proceed as instructed
895 *
896DOT subgraph xcluster_miss {
897DOT     miss [
898DOT             shape=ellipse
899DOT             label="filter req.->bereq."
900DOT     ]
901DOT     vcl_miss [
902DOT             shape=record
903DOT             label="vcl_miss()|req.\nbereq."
904DOT     ]
905DOT     miss -> vcl_miss [style=bold,color=blue,weight=2]
906DOT }
907DOT vcl_miss -> rst_miss [label="restart",color=purple]
908DOT rst_miss [label="RESTART",shape=plaintext]
909DOT vcl_miss -> err_miss [label="error"]
910DOT err_miss [label="ERROR",shape=plaintext]
911DOT vcl_miss -> fetch [label="fetch",style=bold,color=blue,weight=2]
912DOT vcl_miss -> pass [label="pass",style=bold,color=red]
913DOT
914 */
915
916static int
917cnt_miss(struct sess *sp)
918{
919
920        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
921        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
922
923        AZ(sp->obj);
924        AN(sp->objcore);
925        WS_Reset(sp->wrk->ws, NULL);
926        sp->wrk->bereq = sp->wrk->http[0];
927        http_Setup(sp->wrk->bereq, sp->wrk->ws);
928        http_FilterHeader(sp, HTTPH_R_FETCH);
929        http_ForceGet(sp->wrk->bereq);
930        if (params->http_gzip_support) {
931                /*
932                 * We always ask the backend for gzip, even if the
933                 * client doesn't grok it.  We will uncompress for
934                 * the minority of clients which don't.
935                 */
936                http_PrintfHeader(sp->wrk, sp->fd, sp->wrk->bereq,
937                    "Accept-Encoding: gzip");
938        }
939        sp->wrk->connect_timeout = 0;
940        sp->wrk->first_byte_timeout = 0;
941        sp->wrk->between_bytes_timeout = 0;
942        VCL_miss_method(sp);
943        switch(sp->handling) {
944        case VCL_RET_ERROR:
945                AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
946                sp->objcore = NULL;
947                sp->step = STP_ERROR;
948                return (0);
949        case VCL_RET_PASS:
950                AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
951                sp->objcore = NULL;
952                sp->step = STP_PASS;
953                return (0);
954        case VCL_RET_FETCH:
955                sp->step = STP_FETCH;
956                return (0);
957        case VCL_RET_RESTART:
958                AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
959                sp->objcore = NULL;
960                INCOMPL();
961        default:
962                WRONG("Illegal action in vcl_miss{}");
963        }
964}
965
966/*--------------------------------------------------------------------
967 * Start pass processing by getting headers from backend, then
968 * continue in passbody.
969 *
970DOT subgraph xcluster_pass {
971DOT     pass [
972DOT             shape=ellipse
973DOT             label="deref obj."
974DOT     ]
975DOT     pass2 [
976DOT             shape=ellipse
977DOT             label="filter req.->bereq."
978DOT     ]
979DOT     vcl_pass [
980DOT             shape=record
981DOT             label="vcl_pass()|req.\nbereq."
982DOT     ]
983DOT     pass_do [
984DOT             shape=ellipse
985DOT             label="create anon object\n"
986DOT     ]
987DOT     pass -> pass2 [style=bold, color=red]
988DOT     pass2 -> vcl_pass [style=bold, color=red]
989DOT     vcl_pass -> pass_do [label="pass"] [style=bold, color=red]
990DOT }
991DOT pass_do -> fetch [style=bold, color=red]
992DOT vcl_pass -> rst_pass [label="restart",color=purple]
993DOT rst_pass [label="RESTART",shape=plaintext]
994DOT vcl_pass -> err_pass [label="error"]
995DOT err_pass [label="ERROR",shape=plaintext]
996 */
997
998static int
999cnt_pass(struct sess *sp)
1000{
1001
1002        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1003        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
1004        AZ(sp->obj);
1005
1006        WS_Reset(sp->wrk->ws, NULL);
1007        sp->wrk->bereq = sp->wrk->http[0];
1008        http_Setup(sp->wrk->bereq, sp->wrk->ws);
1009        http_FilterHeader(sp, HTTPH_R_PASS);
1010
1011        sp->wrk->connect_timeout = 0;
1012        sp->wrk->first_byte_timeout = 0;
1013        sp->wrk->between_bytes_timeout = 0;
1014        VCL_pass_method(sp);
1015        if (sp->handling == VCL_RET_ERROR) {
1016                sp->step = STP_ERROR;
1017                return (0);
1018        }
1019        assert(sp->handling == VCL_RET_PASS);
1020        sp->acct_tmp.pass++;
1021        sp->sendbody = 1;
1022        sp->step = STP_FETCH;
1023        sp->pass = 1;
1024        return (0);
1025}
1026
1027/*--------------------------------------------------------------------
1028 * Ship the request header to the backend unchanged, then pipe
1029 * until one of the ends close the connection.
1030 *
1031DOT subgraph xcluster_pipe {
1032DOT     pipe [
1033DOT             shape=ellipse
1034DOT             label="Filter req.->bereq."
1035DOT     ]
1036DOT     vcl_pipe [
1037DOT             shape=record
1038DOT             label="vcl_pipe()|req.\nbereq\."
1039DOT     ]
1040DOT     pipe_do [
1041DOT             shape=ellipse
1042DOT             label="send bereq.\npipe until close"
1043DOT     ]
1044DOT     vcl_pipe -> pipe_do [label="pipe",style=bold,color=orange]
1045DOT     pipe -> vcl_pipe [style=bold,color=orange]
1046DOT }
1047DOT pipe_do -> DONE [style=bold,color=orange]
1048DOT vcl_pipe -> err_pipe [label="error"]
1049DOT err_pipe [label="ERROR",shape=plaintext]
1050 */
1051
1052static int
1053cnt_pipe(struct sess *sp)
1054{
1055
1056        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1057        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
1058
1059        sp->acct_tmp.pipe++;
1060        WS_Reset(sp->wrk->ws, NULL);
1061        sp->wrk->bereq = sp->wrk->http[0];
1062        http_Setup(sp->wrk->bereq, sp->wrk->ws);
1063        http_FilterHeader(sp, HTTPH_R_PIPE);
1064
1065        VCL_pipe_method(sp);
1066
1067        if (sp->handling == VCL_RET_ERROR)
1068                INCOMPL();
1069        assert(sp->handling == VCL_RET_PIPE);
1070
1071        PipeSession(sp);
1072        AZ(sp->wrk->wfd);
1073        sp->wrk->bereq = NULL;
1074        sp->step = STP_DONE;
1075        return (0);
1076}
1077
1078/*--------------------------------------------------------------------
1079 * RECV
1080 * We have a complete request, set everything up and start it.
1081 *
1082DOT subgraph xcluster_recv {
1083DOT     recv [
1084DOT             shape=record
1085DOT             label="vcl_recv()|req."
1086DOT     ]
1087DOT }
1088DOT RESTART -> recv
1089DOT recv -> pipe [label="pipe",style=bold,color=orange]
1090DOT recv -> pass2 [label="pass",style=bold,color=red]
1091DOT recv -> err_recv [label="error"]
1092DOT err_recv [label="ERROR",shape=plaintext]
1093DOT recv -> hash [label="lookup",style=bold,color=green,weight=4]
1094 */
1095
1096static int
1097cnt_recv(struct sess *sp)
1098{
1099        unsigned recv_handling;
1100
1101        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1102        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
1103        AZ(sp->obj);
1104
1105        /* By default we use the first backend */
1106        AZ(sp->director);
1107        sp->director = sp->vcl->director[0];
1108        AN(sp->director);
1109
1110        sp->disable_esi = 0;
1111        sp->pass = 0;
1112        sp->hash_always_miss = 0;
1113        sp->hash_ignore_busy = 0;
1114        sp->client_identity = NULL;
1115
1116        http_CollectHdr(sp->http, H_Cache_Control);
1117
1118        VCL_recv_method(sp);
1119        recv_handling = sp->handling;
1120
1121        if (sp->restarts >= params->max_restarts) {
1122                if (sp->err_code == 0)
1123                        sp->err_code = 503;
1124                sp->step = STP_ERROR;
1125                return (0);
1126        }
1127
1128        SHA256_Init(sp->wrk->sha256ctx);
1129        VCL_hash_method(sp);
1130        assert(sp->handling == VCL_RET_HASH);
1131        SHA256_Final(sp->digest, sp->wrk->sha256ctx);
1132
1133        if (!strcmp(sp->http->hd[HTTP_HDR_REQ].b, "HEAD"))
1134                sp->wantbody = 0;
1135        else
1136                sp->wantbody = 1;
1137
1138        sp->sendbody = 0;
1139        switch(recv_handling) {
1140        case VCL_RET_LOOKUP:
1141                /* XXX: discard req body, if any */
1142                sp->step = STP_LOOKUP;
1143                return (0);
1144        case VCL_RET_PIPE:
1145                if (sp->esi_level > 0) {
1146                        /* XXX: VSL something */
1147                        INCOMPL();
1148                        /* sp->step = STP_DONE; */
1149                        return (1);
1150                }
1151                sp->step = STP_PIPE;
1152                return (0);
1153        case VCL_RET_PASS:
1154                sp->step = STP_PASS;
1155                return (0);
1156        case VCL_RET_ERROR:
1157                /* XXX: discard req body, if any */
1158                sp->step = STP_ERROR;
1159                return (0);
1160        default:
1161                WRONG("Illegal action in vcl_recv{}");
1162        }
1163}
1164
1165/*--------------------------------------------------------------------
1166 * START
1167 * Handle a request, wherever it came from recv/restart.
1168 *
1169DOT start [shape=box,label="Dissect request"]
1170DOT start -> recv [style=bold,color=green,weight=4]
1171 */
1172
1173static int
1174cnt_start(struct sess *sp)
1175{
1176        int done;
1177        char *p;
1178        const char *r = "HTTP/1.1 100 Continue\r\n\r\n";
1179
1180        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1181        AZ(sp->restarts);
1182        AZ(sp->obj);
1183        AZ(sp->vcl);
1184
1185        /* Update stats of various sorts */
1186        sp->wrk->stats.client_req++;
1187        sp->t_req = TIM_real();
1188        sp->wrk->lastused = sp->t_req;
1189        sp->acct_tmp.req++;
1190
1191        /* Assign XID and log */
1192        sp->xid = ++xids;                               /* XXX not locked */
1193        WSP(sp, SLT_ReqStart, "%s %s %u", sp->addr, sp->port,  sp->xid);
1194
1195        /* Borrow VCL reference from worker thread */
1196        VCL_Refresh(&sp->wrk->vcl);
1197        sp->vcl = sp->wrk->vcl;
1198        sp->wrk->vcl = NULL;
1199
1200        http_Setup(sp->http, sp->ws);
1201        done = http_DissectRequest(sp);
1202
1203        /* If we could not even parse the request, just close */
1204        if (done < 0) {
1205                sp->step = STP_DONE;
1206                vca_close_session(sp, "junk");
1207                return (0);
1208        }
1209
1210        /* Catch request snapshot */
1211        sp->ws_req = WS_Snapshot(sp->ws);
1212
1213        /* Catch original request, before modification */
1214        HTTP_Copy(sp->http0, sp->http);
1215
1216        if (done != 0) {
1217                sp->err_code = done;
1218                sp->step = STP_ERROR;
1219                return (0);
1220        }
1221
1222        sp->doclose = http_DoConnection(sp->http);
1223
1224        /* XXX: Handle TRACE & OPTIONS of Max-Forwards = 0 */
1225
1226        /*
1227         * Handle Expect headers
1228         */
1229        if (http_GetHdr(sp->http, H_Expect, &p)) {
1230                if (strcasecmp(p, "100-continue")) {
1231                        sp->err_code = 417;
1232                        sp->step = STP_ERROR;
1233                        return (0);
1234                }
1235
1236                /* XXX: Don't bother with write failures for now */
1237                (void)write(sp->fd, r, strlen(r));
1238                /* XXX: When we do ESI includes, this is not removed
1239                 * XXX: because we use http0 as our basis.  Believed
1240                 * XXX: safe, but potentially confusing.
1241                 */
1242                http_Unset(sp->http, H_Expect);
1243        }
1244
1245        sp->step = STP_RECV;
1246        return (0);
1247}
1248
1249/*--------------------------------------------------------------------
1250 * Central state engine dispatcher.
1251 *
1252 * Kick the session around until it has had enough.
1253 *
1254 */
1255
1256static void
1257cnt_diag(struct sess *sp, const char *state)
1258{
1259        if (sp->wrk != NULL) {
1260                WSL(sp->wrk, SLT_Debug, sp->id,
1261                    "thr %p STP_%s sp %p obj %p vcl %p",
1262                    pthread_self(), state, sp, sp->obj, sp->vcl);
1263                WSL_Flush(sp->wrk, 0);
1264        } else {
1265                VSL(SLT_Debug, sp->id,
1266                    "thr %p STP_%s sp %p obj %p vcl %p",
1267                    pthread_self(), state, sp, sp->obj, sp->vcl);
1268        }
1269}
1270
1271void
1272CNT_Session(struct sess *sp)
1273{
1274        int done;
1275        struct worker *w;
1276
1277        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1278        w = sp->wrk;
1279        CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
1280
1281        /*
1282         * Possible entrance states
1283         */
1284        assert(
1285            sp->step == STP_FIRST ||
1286            sp->step == STP_START ||
1287            sp->step == STP_LOOKUP ||
1288            sp->step == STP_RECV);
1289
1290        /*
1291         * Whenever we come in from the acceptor we need to set blocking
1292         * mode, but there is no point in setting it when we come from
1293         * ESI or when a parked sessions returns.
1294         * It would be simpler to do this in the acceptor, but we'd rather
1295         * do the syscall in the worker thread.
1296         */
1297        if (sp->step == STP_FIRST || sp->step == STP_START)
1298                (void)TCP_blocking(sp->fd);
1299
1300        /*
1301         * NB: Once done is set, we can no longer touch sp!
1302         */
1303        for (done = 0; !done; ) {
1304                assert(sp->wrk == w);
1305                /*
1306                 * This is a good place to be paranoid about the various
1307                 * pointers still pointing to the things we expect.
1308                 */
1309                CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1310                CHECK_OBJ_ORNULL(sp->obj, OBJECT_MAGIC);
1311                CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
1312                CHECK_OBJ_ORNULL(w->nobjhead, OBJHEAD_MAGIC);
1313                WS_Assert(w->ws);
1314                AZ(sp->wrk->storage_hint);
1315                AZ(sp->wrk->storage);
1316
1317                switch (sp->step) {
1318#define STEP(l,u) \
1319                    case STP_##u: \
1320                        if (params->diag_bitmap & 0x01) \
1321                                cnt_diag(sp, #u); \
1322                        done = cnt_##l(sp); \
1323                        break;
1324#include "steps.h"
1325#undef STEP
1326                default:
1327                        WRONG("State engine misfire");
1328                }
1329                WS_Assert(w->ws);
1330                CHECK_OBJ_ORNULL(w->nobjhead, OBJHEAD_MAGIC);
1331        }
1332        WSL_Flush(w, 0);
1333        AZ(w->wfd);
1334}
1335
1336/*
1337DOT }
1338*/
1339
1340/*--------------------------------------------------------------------
1341 * Debugging aids
1342 */
1343
1344static void
1345cli_debug_xid(struct cli *cli, const char * const *av, void *priv)
1346{
1347        (void)priv;
1348        if (av[2] != NULL)
1349                xids = strtoul(av[2], NULL, 0);
1350        cli_out(cli, "XID is %u", xids);
1351}
1352
1353/*
1354 * Default to seed=1, this is the only seed value POSIXl guarantees will
1355 * result in a reproducible random number sequence.
1356 */
1357static void
1358cli_debug_srandom(struct cli *cli, const char * const *av, void *priv)
1359{
1360        (void)priv;
1361        unsigned seed = 1;
1362
1363        if (av[2] != NULL)
1364                seed = strtoul(av[2], NULL, 0);
1365        srandom(seed);
1366        srand48(random());
1367        cli_out(cli, "Random(3) seeded with %lu", seed);
1368}
1369
1370static struct cli_proto debug_cmds[] = {
1371        { "debug.xid", "debug.xid",
1372                "\tExamine or set XID\n", 0, 1, "d", cli_debug_xid },
1373        { "debug.srandom", "debug.srandom",
1374                "\tSeed the random(3) function\n", 0, 1, "d", cli_debug_srandom },
1375        { NULL }
1376};
1377
1378/*--------------------------------------------------------------------
1379 *
1380 */
1381
1382void
1383CNT_Init(void)
1384{
1385
1386        srandomdev();
1387        srand48(random());
1388        xids = random();
1389        CLI_AddFuncs(debug_cmds);
1390}
1391
1392
Note: See TracBrowser for help on using the repository browser.