source: lib/libvarnish/cli_serve.c @ 866b41

Revision 866b41, 12.1 KB checked in by Charlie Root <root@…>, 2 years ago (diff)

Fix a printf format issue with time_t

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2006 Verdens Gang AS
3 * Copyright (c) 2006-2011 Varnish Software 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 * Stuff for handling the CLI protocol
30 */
31
32#include "config.h"
33
34#include <ctype.h>
35#include <errno.h>
36#include <poll.h>
37#include <stdint.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <time.h>
42#include <unistd.h>
43
44#include "miniobj.h"
45#include "vas.h"
46
47#include "vav.h"
48#include "vcli.h"
49#include "vcli_common.h"
50#include "vcli_priv.h"
51#include "vcli_serve.h"
52#include "vlu.h"
53#include "vqueue.h"
54#include "vsb.h"
55
56struct VCLS_func {
57        unsigned                        magic;
58#define VCLS_FUNC_MAGIC                 0x7d280c9b
59        VTAILQ_ENTRY(VCLS_func)         list;
60        unsigned                        auth;
61        struct cli_proto                *clp;
62};
63
64struct VCLS_fd {
65        unsigned                        magic;
66#define VCLS_FD_MAGIC                   0x010dbd1e
67        VTAILQ_ENTRY(VCLS_fd)           list;
68        int                             fdi, fdo;
69        struct VCLS                     *cls;
70        struct cli                      *cli, clis;
71        cls_cb_f                        *closefunc;
72        void                            *priv;
73        struct vsb                      *last_arg;
74        int                             last_idx;
75        char                            **argv;
76};
77
78struct VCLS {
79        unsigned                        magic;
80#define VCLS_MAGIC                      0x60f044a3
81        VTAILQ_HEAD(,VCLS_fd)           fds;
82        unsigned                        nfd;
83        VTAILQ_HEAD(,VCLS_func)         funcs;
84        cls_cbc_f                       *before, *after;
85        volatile unsigned               *maxlen;
86        volatile unsigned               *limit;
87};
88
89/*--------------------------------------------------------------------*/
90
91void
92VCLS_func_close(struct cli *cli, const char *const *av, void *priv)
93{
94
95        (void)av;
96        (void)priv;
97        VCLI_Out(cli, "Closing CLI connection");
98        VCLI_SetResult(cli, CLIS_CLOSE);
99}
100
101/*--------------------------------------------------------------------*/
102
103void
104VCLS_func_ping(struct cli *cli, const char * const *av, void *priv)
105{
106        time_t t;
107
108        (void)priv;
109        (void)av;
110        t = time(NULL);
111        VCLI_Out(cli, "PONG %jd 1.0", (intmax_t)t);
112}
113
114/*--------------------------------------------------------------------*/
115
116void
117VCLS_func_help(struct cli *cli, const char * const *av, void *priv)
118{
119        struct cli_proto *cp;
120        struct VCLS_func *cfn;
121        unsigned all, debug, u, d, h, i, wc;
122        struct VCLS *cs;
123
124        (void)priv;
125        cs = cli->cls;
126        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
127
128        if (av[2] == NULL) {
129                all = debug = 0;
130        } else if (!strcmp(av[2], "-a")) {
131                all = 1;
132                debug = 0;
133        } else if (!strcmp(av[2], "-d")) {
134                all = 0;
135                debug = 1;
136        } else {
137                VTAILQ_FOREACH(cfn, &cs->funcs, list) {
138                        if (cfn->auth > cli->auth)
139                                continue;
140                        for (cp = cfn->clp; cp->request != NULL; cp++) {
141                                if (!strcmp(cp->request, av[2])) {
142                                        VCLI_Out(cli, "%s\n%s\n",
143                                            cp->syntax, cp->help);
144                                        return;
145                                }
146                                for (u = 0; u < sizeof cp->flags; u++) {
147                                        if (cp->flags[u] == '*') {
148                                                cp->func(cli,av,priv);
149                                                return;
150                                        }
151                                }
152                        }
153                }
154                VCLI_Out(cli, "Unknown request.\nType 'help' for more info.\n");
155                VCLI_SetResult(cli, CLIS_UNKNOWN);
156                return;
157        }
158        VTAILQ_FOREACH(cfn, &cs->funcs, list) {
159                if (cfn->auth > cli->auth)
160                        continue;
161                for (cp = cfn->clp; cp->request != NULL; cp++) {
162                        d = 0;
163                        h = 0;
164                        i = 0;
165                        wc = 0;
166                        for (u = 0; u < sizeof cp->flags; u++) {
167                                if (cp->flags[u] == '\0')
168                                        continue;
169                                if (cp->flags[u] == 'd')
170                                        d = 1;
171                                if (cp->flags[u] == 'h')
172                                        h = 1;
173                                if (cp->flags[u] == 'i')
174                                        i = 1;
175                                if (cp->flags[u] == '*')
176                                        wc = 1;
177                        }
178                        if (i)
179                                continue;
180                        if (wc) {
181                                cp->func(cli, av, priv);
182                                continue;
183                        }
184                        if (debug != d)
185                                continue;
186                        if (h && !all)
187                                continue;
188                        if (cp->syntax != NULL)
189                                VCLI_Out(cli, "%s\n", cp->syntax);
190                }
191        }
192}
193
194/*--------------------------------------------------------------------
195 * Look for a CLI command to execute
196 */
197
198static int
199cls_dispatch(struct cli *cli, struct cli_proto *clp, char * const * av,
200    unsigned ac)
201{
202        struct cli_proto *cp;
203
204        AN(av);
205        for (cp = clp; cp->request != NULL; cp++) {
206                if (!strcmp(av[1], cp->request))
207                        break;
208                if (!strcmp("*", cp->request))
209                        break;
210        }
211        if (cp->request == NULL)
212                return (0);
213
214        if (cp->func == NULL) {
215                VCLI_Out(cli, "Unimplemented\n");
216                VCLI_SetResult(cli, CLIS_UNIMPL);
217                return(1);
218        }
219
220        if (ac - 1 < cp->minarg) {
221                VCLI_Out(cli, "Too few parameters\n");
222                VCLI_SetResult(cli, CLIS_TOOFEW);
223                return(1);
224        }
225
226        if (ac - 1> cp->maxarg) {
227                VCLI_Out(cli, "Too many parameters\n");
228                VCLI_SetResult(cli, CLIS_TOOMANY);
229                return(1);
230        }
231
232        cli->result = CLIS_OK;
233        VSB_clear(cli->sb);
234        cp->func(cli, (const char * const *)av, cp->priv);
235        return (1);
236}
237
238/*--------------------------------------------------------------------
239 * We have collected a full cli line, parse it and execute, if possible.
240 */
241
242static int
243cls_vlu2(void *priv, char * const *av)
244{
245        struct VCLS_fd *cfd;
246        struct VCLS *cs;
247        struct VCLS_func *cfn;
248        struct cli *cli;
249        unsigned na;
250        ssize_t len;
251        char *s;
252        unsigned lim;
253        const char *trunc = "!\n[response was truncated]\n";
254
255        CAST_OBJ_NOTNULL(cfd, priv, VCLS_FD_MAGIC);
256        cs = cfd->cls;
257        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
258
259        cli = cfd->cli;
260        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
261        AN(cli->cmd);
262
263        cli->cls = cs;
264
265        cli->result = CLIS_UNKNOWN;
266        VSB_clear(cli->sb);
267        VCLI_Out(cli, "Unknown request.\nType 'help' for more info.\n");
268
269        if (cs->before != NULL)
270                cs->before(cli);
271
272        do {
273                if (av[0] != NULL) {
274                        VCLI_Out(cli, "Syntax Error: %s\n", av[0]);
275                        VCLI_SetResult(cli, CLIS_SYNTAX);
276                        break;
277                }
278
279                if (isupper(av[1][0])) {
280                        VCLI_Out(cli, "all commands are in lower-case.\n");
281                        VCLI_SetResult(cli, CLIS_UNKNOWN);
282                        break;
283                }
284
285                if (!islower(av[1][0]))
286                        break;
287
288                for (na = 0; av[na + 1] != NULL; na++)
289                        continue;
290
291                VTAILQ_FOREACH(cfn, &cs->funcs, list) {
292                        if (cfn->auth > cli->auth)
293                                continue;
294                        if (cls_dispatch(cli, cfn->clp, av, na))
295                                break;
296                }
297        } while (0);
298
299        AZ(VSB_finish(cli->sb));
300
301        if (cs->after != NULL)
302                cs->after(cli);
303
304        cli->cls = NULL;
305
306        s = VSB_data(cli->sb);
307        len = VSB_len(cli->sb);
308        lim = *cs->limit;
309        if (len > lim) {
310                if (cli->result == CLIS_OK)
311                        cli->result = CLIS_TRUNCATED;
312                strcpy(s + (lim - strlen(trunc)), trunc);
313                assert(strlen(s) <= lim);
314        }
315        if (VCLI_WriteResult(cfd->fdo, cli->result, s) ||
316            cli->result == CLIS_CLOSE)
317                return (1);
318
319        return (0);
320}
321
322static int
323cls_vlu(void *priv, const char *p)
324{
325        struct VCLS_fd *cfd;
326        struct cli *cli;
327        int i;
328        char **av;
329
330        CAST_OBJ_NOTNULL(cfd, priv, VCLS_FD_MAGIC);
331
332        cli = cfd->cli;
333        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
334
335        if (cfd->argv == NULL) {
336                /*
337                 * Lines with only whitespace are simply ignored, in order
338                 * to not complicate CLI-client side scripts and TELNET users
339                 */
340                for (; isspace(*p); p++)
341                        continue;
342                if (*p == '\0')
343                        return (0);
344                REPLACE(cli->cmd, p);
345
346                av = VAV_Parse(p, NULL, 0);
347                AN(av);
348                if (av[0] != NULL) {
349                        i = cls_vlu2(priv, av);
350                        VAV_Free(av);
351                        free(cli->cmd);
352                        cli->cmd = NULL;
353                        return (i);
354                }
355                for (i = 1; av[i] != NULL; i++)
356                        continue;
357                if (i < 3 || cli->auth == 0 || strcmp(av[i - 2], "<<")) {
358                        i = cls_vlu2(priv, av);
359                        VAV_Free(av);
360                        free(cli->cmd);
361                        cli->cmd = NULL;
362                        return (i);
363                }
364                cfd->argv = av;
365                cfd->last_idx = i - 2;
366                cfd->last_arg = VSB_new_auto();
367                AN(cfd->last_arg);
368                return (0);
369        } else {
370                AN(cfd->argv[cfd->last_idx]);
371                assert(!strcmp(cfd->argv[cfd->last_idx], "<<"));
372                AN(cfd->argv[cfd->last_idx + 1]);
373                if (strcmp(p, cfd->argv[cfd->last_idx + 1])) {
374                        VSB_cat(cfd->last_arg, p);
375                        VSB_cat(cfd->last_arg, "\n");
376                        return (0);
377                }
378                AZ(VSB_finish(cfd->last_arg));
379                free(cfd->argv[cfd->last_idx]);
380                cfd->argv[cfd->last_idx] = NULL;
381                free(cfd->argv[cfd->last_idx + 1]);
382                cfd->argv[cfd->last_idx + 1] = NULL;
383                cfd->argv[cfd->last_idx] = VSB_data(cfd->last_arg);
384                i = cls_vlu2(priv, cfd->argv);
385                cfd->argv[cfd->last_idx] = NULL;
386                VAV_Free(cfd->argv);
387                cfd->argv = NULL;
388                free(cli->cmd);
389                cli->cmd = NULL;
390                VSB_delete(cfd->last_arg);
391                cfd->last_arg = NULL;
392                cfd->last_idx = 0;
393                return (i);
394        }
395}
396
397struct VCLS *
398VCLS_New(cls_cbc_f *before, cls_cbc_f *after, volatile unsigned *maxlen,
399    volatile unsigned *limit)
400{
401        struct VCLS *cs;
402
403        ALLOC_OBJ(cs, VCLS_MAGIC);
404        AN(cs);
405        VTAILQ_INIT(&cs->fds);
406        VTAILQ_INIT(&cs->funcs);
407        cs->before = before;
408        cs->after = after;
409        cs->maxlen = maxlen;
410        cs->limit = limit;
411        return (cs);
412}
413
414struct cli *
415VCLS_AddFd(struct VCLS *cs, int fdi, int fdo, cls_cb_f *closefunc, void *priv)
416{
417        struct VCLS_fd *cfd;
418
419        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
420        assert(fdi >= 0);
421        assert(fdo >= 0);
422        ALLOC_OBJ(cfd, VCLS_FD_MAGIC);
423        AN(cfd);
424        cfd->cls = cs;
425        cfd->fdi = fdi;
426        cfd->fdo = fdo;
427        cfd->cli = &cfd->clis;
428        cfd->cli->magic = CLI_MAGIC;
429        cfd->cli->vlu = VLU_New(cfd, cls_vlu, *cs->maxlen);
430        cfd->cli->sb = VSB_new_auto();
431        cfd->cli->limit = cs->limit;
432        cfd->closefunc = closefunc;
433        cfd->priv = priv;
434        AN(cfd->cli->sb);
435        VTAILQ_INSERT_TAIL(&cs->fds, cfd, list);
436        cs->nfd++;
437        return (cfd->cli);
438}
439
440static void
441cls_close_fd(struct VCLS *cs, struct VCLS_fd *cfd)
442{
443
444        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
445        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
446
447        VTAILQ_REMOVE(&cs->fds, cfd, list);
448        cs->nfd--;
449        VLU_Destroy(cfd->cli->vlu);
450        VSB_delete(cfd->cli->sb);
451        if (cfd->closefunc == NULL) {
452                (void)close(cfd->fdi);
453                if (cfd->fdo != cfd->fdi)
454                        (void)close(cfd->fdo);
455        } else {
456                cfd->closefunc(cfd->priv);
457        }
458        if (cfd->cli->ident != NULL)
459                free(cfd->cli->ident);
460        FREE_OBJ(cfd);
461}
462
463
464int
465VCLS_AddFunc(struct VCLS *cs, unsigned auth, struct cli_proto *clp)
466{
467        struct VCLS_func *cfn;
468
469        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
470        ALLOC_OBJ(cfn, VCLS_FUNC_MAGIC);
471        AN(cfn);
472        cfn->clp = clp;
473        cfn->auth = auth;
474        VTAILQ_INSERT_TAIL(&cs->funcs, cfn, list);
475        return (0);
476}
477
478int
479VCLS_PollFd(struct VCLS *cs, int fd, int timeout)
480{
481        struct VCLS_fd *cfd;
482        struct pollfd pfd[1];
483        int i, j, k;
484
485        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
486        if (cs->nfd == 0) {
487                errno = 0;
488                return (-1);
489        }
490        assert(cs->nfd > 0);
491
492        i = 0;
493        VTAILQ_FOREACH(cfd, &cs->fds, list) {
494                if (cfd->fdi != fd)
495                        continue;
496                pfd[i].fd = cfd->fdi;
497                pfd[i].events = POLLIN;
498                pfd[i].revents = 0;
499                i++;
500                break;
501        }
502        assert(i == 1);
503        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
504
505        j = poll(pfd, 1, timeout);
506        if (j <= 0)
507                return (j);
508        if (pfd[0].revents & POLLHUP)
509                k = 1;
510        else
511                k = VLU_Fd(cfd->fdi, cfd->cli->vlu);
512        if (k)
513                cls_close_fd(cs, cfd);
514        return (k);
515}
516
517int
518VCLS_Poll(struct VCLS *cs, int timeout)
519{
520        struct VCLS_fd *cfd, *cfd2;
521        int i, j, k;
522
523        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
524        if (cs->nfd == 0) {
525                errno = 0;
526                return (-1);
527        }
528        assert(cs->nfd > 0);
529        {
530                struct pollfd pfd[cs->nfd];
531
532                i = 0;
533                VTAILQ_FOREACH(cfd, &cs->fds, list) {
534                        pfd[i].fd = cfd->fdi;
535                        pfd[i].events = POLLIN;
536                        pfd[i].revents = 0;
537                        i++;
538                }
539                assert(i == cs->nfd);
540
541                j = poll(pfd, cs->nfd, timeout);
542                if (j <= 0)
543                        return (j);
544                i = 0;
545                VTAILQ_FOREACH_SAFE(cfd, &cs->fds, list, cfd2) {
546                        assert(pfd[i].fd == cfd->fdi);
547                        if (pfd[i].revents & POLLHUP)
548                                k = 1;
549                        else
550                                k = VLU_Fd(cfd->fdi, cfd->cli->vlu);
551                        if (k)
552                                cls_close_fd(cs, cfd);
553                        i++;
554                }
555                assert(i == j);
556        }
557        return (j);
558}
559
560void
561VCLS_Destroy(struct VCLS **csp)
562{
563        struct VCLS *cs;
564        struct VCLS_fd *cfd, *cfd2;
565        struct VCLS_func *cfn;
566
567        cs = *csp;
568        *csp = NULL;
569        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
570        VTAILQ_FOREACH_SAFE(cfd, &cs->fds, list, cfd2)
571                cls_close_fd(cs, cfd);
572
573        while (!VTAILQ_EMPTY(&cs->funcs)) {
574                cfn = VTAILQ_FIRST(&cs->funcs);
575                VTAILQ_REMOVE(&cs->funcs, cfn, list);
576                FREE_OBJ(cfn);
577        }
578        FREE_OBJ(cs);
579}
580
Note: See TracBrowser for help on using the repository browser.