source: bin/varnishd/mgt_cli.c @ 8aa1d8

Revision 8aa1d8, 14.8 KB checked in by Poul-Henning Kamp <phk@…>, 3 years ago (diff)

Eliminate nested <*.h> includes from include/*

Sort #includes according to rules which are for me to know and you
to guess.

  • 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 * The management process' CLI handling
30 */
31
32#include "config.h"
33
34#include <sys/socket.h>
35
36#include <fcntl.h>
37#include <poll.h>
38#include <stdarg.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <syslog.h>
43#include <unistd.h>
44
45#include "mgt.h"
46
47#include "vcli.h"
48#include "cli_common.h"
49#include "cli_priv.h"
50#include "cli_serve.h"
51#include "heritage.h"
52#include "mgt_cli.h"
53#include "vev.h"
54#include "vlu.h"
55#include "vss.h"
56#include "vtcp.h"
57
58#ifndef HAVE_SRANDOMDEV
59#include "compat/srandomdev.h"
60#endif
61
62static int              cli_i = -1, cli_o = -1;
63static struct VCLS      *cls;
64static const char       *secret_file;
65
66#define MCF_NOAUTH      0       /* NB: zero disables here-documents */
67#define MCF_AUTH        16
68
69/*--------------------------------------------------------------------*/
70
71static void
72mcf_banner(struct cli *cli, const char *const *av, void *priv)
73{
74
75        (void)av;
76        (void)priv;
77        VCLI_Out(cli, "-----------------------------\n");
78        VCLI_Out(cli, "Varnish Cache CLI 1.0\n");
79        VCLI_Out(cli, "-----------------------------\n");
80        VCLI_Out(cli, "%s\n", VSB_data(vident) + 1);
81        VCLI_Out(cli, "\n");
82        VCLI_Out(cli, "Type 'help' for command list.\n");
83        VCLI_Out(cli, "Type 'quit' to close CLI session.\n");
84        if (child_pid < 0)
85                VCLI_Out(cli, "Type 'start' to launch worker process.\n");
86        VCLI_SetResult(cli, CLIS_OK);
87}
88
89/*--------------------------------------------------------------------*/
90
91/* XXX: what order should this list be in ? */
92static struct cli_proto cli_proto[] = {
93        { CLI_BANNER,           "", mcf_banner, NULL },
94        { CLI_SERVER_STATUS,    "", mcf_server_status, NULL },
95        { CLI_SERVER_START,     "", mcf_server_startstop, NULL },
96        { CLI_SERVER_STOP,      "", mcf_server_startstop, cli_proto },
97        { CLI_VCL_LOAD,         "", mcf_config_load, NULL },
98        { CLI_VCL_INLINE,       "", mcf_config_inline, NULL },
99        { CLI_VCL_USE,          "", mcf_config_use, NULL },
100        { CLI_VCL_DISCARD,      "", mcf_config_discard, NULL },
101        { CLI_VCL_LIST,         "", mcf_config_list, NULL },
102        { CLI_VCL_SHOW,         "", mcf_config_show, NULL },
103        { CLI_PARAM_SHOW,       "", mcf_param_show, NULL },
104        { CLI_PARAM_SET,        "", mcf_param_set, NULL },
105        { CLI_PANIC_SHOW,       "", mcf_panic_show, NULL },
106        { CLI_PANIC_CLEAR,      "", mcf_panic_clear, NULL },
107        { NULL }
108};
109
110/*--------------------------------------------------------------------*/
111
112static void
113mcf_panic(struct cli *cli, const char * const *av, void *priv)
114{
115
116        (void)cli;
117        (void)av;
118        (void)priv;
119        assert(!strcmp("", "You asked for it"));
120}
121
122static struct cli_proto cli_debug[] = {
123        { "debug.panic.master", "debug.panic.master",
124                "\tPanic the master process.\n",
125                0, 0, "d", mcf_panic, NULL},
126        { NULL }
127};
128
129/*--------------------------------------------------------------------*/
130
131static void
132mcf_askchild(struct cli *cli, const char * const *av, void *priv)
133{
134        int i;
135        char *q;
136        unsigned u;
137        struct vsb *vsb;
138
139        (void)priv;
140        /*
141         * Command not recognized in master, try cacher if it is
142         * running.
143         */
144        if (cli_o <= 0) {
145                if (!strcmp(av[1], "help")) {
146                        VCLI_Out(cli, "No help from child, (not running).\n");
147                        return;
148                }
149                VCLI_SetResult(cli, CLIS_UNKNOWN);
150                VCLI_Out(cli,
151                    "Unknown request in manager process "
152                    "(child not running).\n"
153                    "Type 'help' for more info.");
154                return;
155        }
156        vsb = VSB_new_auto();
157        for (i = 1; av[i] != NULL; i++) {
158                VSB_quote(vsb, av[i], strlen(av[i]), 0);
159                VSB_putc(vsb, ' ');
160        }
161        VSB_putc(vsb, '\n');
162        AZ(VSB_finish(vsb));
163        i = write(cli_o, VSB_data(vsb), VSB_len(vsb));
164        if (i != VSB_len(vsb)) {
165                VSB_delete(vsb);
166                VCLI_SetResult(cli, CLIS_COMMS);
167                VCLI_Out(cli, "CLI communication error");
168                MGT_Child_Cli_Fail();
169                return;
170        }
171        VSB_delete(vsb);
172        (void)VCLI_ReadResult(cli_i, &u, &q, params->cli_timeout);
173        VCLI_SetResult(cli, u);
174        VCLI_Out(cli, "%s", q);
175        free(q);
176}
177
178static struct cli_proto cli_askchild[] = {
179        { "*", "<wild-card-entry>", "\t<fall through to cacher>\n",
180                0, 9999, "h*", mcf_askchild, NULL},
181        { NULL }
182};
183
184/*--------------------------------------------------------------------
185 * Ask the child something over CLI, return zero only if everything is
186 * happy happy.
187 */
188
189int
190mgt_cli_askchild(unsigned *status, char **resp, const char *fmt, ...) {
191        int i, j;
192        va_list ap;
193        unsigned u;
194        char buf[params->cli_buffer], *p;
195
196        if (resp != NULL)
197                *resp = NULL;
198        if (status != NULL)
199                *status = 0;
200        if (cli_i < 0|| cli_o < 0) {
201                if (status != NULL)
202                        *status = CLIS_CANT;
203                return (CLIS_CANT);
204        }
205        va_start(ap, fmt);
206        vbprintf(buf, fmt, ap);
207        va_end(ap);
208        p = strchr(buf, '\0');
209        assert(p != NULL && p > buf && p[-1] == '\n');
210        i = p - buf;
211        j = write(cli_o, buf, i);
212        if (j != i) {
213                if (status != NULL)
214                        *status = CLIS_COMMS;
215                if (resp != NULL)
216                        *resp = strdup("CLI communication error");
217                MGT_Child_Cli_Fail();
218                return (CLIS_COMMS);
219        }
220
221        (void)VCLI_ReadResult(cli_i, &u, resp, params->cli_timeout);
222        if (status != NULL)
223                *status = u;
224        if (u == CLIS_COMMS)
225                MGT_Child_Cli_Fail();
226        return (u == CLIS_OK ? 0 : u);
227}
228
229/*--------------------------------------------------------------------*/
230
231void
232mgt_cli_start_child(int fdi, int fdo)
233{
234
235        cli_i = fdi;
236        cli_o = fdo;
237}
238
239/*--------------------------------------------------------------------*/
240
241void
242mgt_cli_stop_child(void)
243{
244
245        cli_i = -1;
246        cli_o = -1;
247        /* XXX: kick any users */
248}
249
250/*--------------------------------------------------------------------
251 * Generate a random challenge
252 */
253
254static void
255mgt_cli_challenge(struct cli *cli)
256{
257        int i;
258
259        for (i = 0; i + 2L < sizeof cli->challenge; i++)
260                cli->challenge[i] = (random() % 26) + 'a';
261        cli->challenge[i++] = '\n';
262        cli->challenge[i] = '\0';
263        VCLI_Out(cli, "%s", cli->challenge);
264        VCLI_Out(cli, "\nAuthentication required.\n");
265        VCLI_SetResult(cli, CLIS_AUTH);
266}
267
268/*--------------------------------------------------------------------
269 * Validate the authentication
270 */
271
272static void
273mcf_auth(struct cli *cli, const char *const *av, void *priv)
274{
275        int fd;
276        char buf[CLI_AUTH_RESPONSE_LEN + 1];
277
278        AN(av[2]);
279        (void)priv;
280        if (secret_file == NULL) {
281                VCLI_Out(cli, "Secret file not configured\n");
282                VCLI_SetResult(cli, CLIS_CANT);
283                return;
284        }
285        fd = open(secret_file, O_RDONLY);
286        if (fd < 0) {
287                VCLI_Out(cli, "Cannot open secret file (%s)\n",
288                    strerror(errno));
289                VCLI_SetResult(cli, CLIS_CANT);
290                return;
291        }
292        mgt_got_fd(fd);
293        VCLI_AuthResponse(fd, cli->challenge, buf);
294        AZ(close(fd));
295        if (strcasecmp(buf, av[2])) {
296                mgt_cli_challenge(cli);
297                return;
298        }
299        cli->auth = MCF_AUTH;
300        memset(cli->challenge, 0, sizeof cli->challenge);
301        VCLI_SetResult(cli, CLIS_OK);
302        mcf_banner(cli, av, priv);
303}
304
305static struct cli_proto cli_auth[] = {
306        { CLI_HELP,             "", VCLS_func_help, NULL },
307        { CLI_PING,             "", VCLS_func_ping },
308        { CLI_AUTH,             "", mcf_auth, NULL },
309        { CLI_QUIT,             "", VCLS_func_close, NULL},
310        { NULL }
311};
312
313/*--------------------------------------------------------------------*/
314static void
315mgt_cli_cb_before(const struct cli *cli)
316{
317
318        if (params->syslog_cli_traffic)
319                syslog(LOG_NOTICE, "CLI %s Rd %s", cli->ident, cli->cmd);
320}
321
322static void
323mgt_cli_cb_after(const struct cli *cli)
324{
325
326        if (params->syslog_cli_traffic)
327                syslog(LOG_NOTICE, "CLI %s Wr %03u %s",
328                    cli->ident, cli->result, VSB_data(cli->sb));
329}
330
331/*--------------------------------------------------------------------*/
332
333static void
334mgt_cli_init_cls(void)
335{
336
337        cls = VCLS_New(mgt_cli_cb_before, mgt_cli_cb_after, params->cli_buffer);
338        AN(cls);
339        AZ(VCLS_AddFunc(cls, MCF_NOAUTH, cli_auth));
340        AZ(VCLS_AddFunc(cls, MCF_AUTH, cli_proto));
341        AZ(VCLS_AddFunc(cls, MCF_AUTH, cli_debug));
342        AZ(VCLS_AddFunc(cls, MCF_AUTH, cli_stv));
343        AZ(VCLS_AddFunc(cls, MCF_AUTH, cli_askchild));
344}
345
346/*--------------------------------------------------------------------
347 * Get rid of all CLI sessions
348 */
349
350void
351mgt_cli_close_all(void)
352{
353
354        VCLS_Destroy(&cls);
355}
356
357/*--------------------------------------------------------------------
358 * Callback whenever something happens to the input fd of the session.
359 */
360
361static int
362mgt_cli_callback2(const struct vev *e, int what)
363{
364        int i;
365
366        (void)e;
367        (void)what;
368        i = VCLS_PollFd(cls, e->fd, 0);
369        return (i);
370}
371
372/*--------------------------------------------------------------------*/
373
374void
375mgt_cli_setup(int fdi, int fdo, int verbose, const char *ident, mgt_cli_close_f *closefunc, void *priv)
376{
377        struct cli *cli;
378        struct vev *ev;
379
380        (void)ident;
381        (void)verbose;
382        if (cls == NULL)
383                mgt_cli_init_cls();
384
385        cli = VCLS_AddFd(cls, fdi, fdo, closefunc, priv);
386
387        cli->ident = strdup(ident);
388
389        /* Deal with TELNET options */
390        if (fdi != 0)
391                VLU_SetTelnet(cli->vlu, fdo);
392
393        if (fdi != 0 && secret_file != NULL) {
394                cli->auth = MCF_NOAUTH;
395                mgt_cli_challenge(cli);
396        } else {
397                cli->auth = MCF_AUTH;
398                mcf_banner(cli, NULL, NULL);
399        }
400        AZ(VSB_finish(cli->sb));
401        (void)VCLI_WriteResult(fdo, cli->result, VSB_data(cli->sb));
402
403
404        ev = vev_new();
405        AN(ev);
406        ev->name = cli->ident;
407        ev->fd = fdi;
408        ev->fd_flags = EV_RD;
409        ev->callback = mgt_cli_callback2;
410        ev->priv = cli;
411        AZ(vev_add(mgt_evb, ev));
412}
413
414/*--------------------------------------------------------------------*/
415
416static struct vsb *
417sock_id(const char *pfx, int fd)
418{
419        struct vsb *vsb;
420
421        char abuf1[VTCP_ADDRBUFSIZE], abuf2[VTCP_ADDRBUFSIZE];
422        char pbuf1[VTCP_PORTBUFSIZE], pbuf2[VTCP_PORTBUFSIZE];
423
424        vsb = VSB_new_auto();
425        AN(vsb);
426        VTCP_myname(fd, abuf1, sizeof abuf1, pbuf1, sizeof pbuf1);
427        VTCP_hisname(fd, abuf2, sizeof abuf2, pbuf2, sizeof pbuf2);
428        VSB_printf(vsb, "%s %s %s %s %s", pfx, abuf2, pbuf2, abuf1, pbuf1);
429        AZ(VSB_finish(vsb));
430        return (vsb);
431}
432
433/*--------------------------------------------------------------------*/
434
435struct telnet {
436        unsigned                magic;
437#define TELNET_MAGIC            0x53ec3ac0
438        int                     fd;
439        struct vev              *ev;
440};
441
442static void
443telnet_close(void *priv)
444{
445        struct telnet *tn;
446
447        CAST_OBJ_NOTNULL(tn, priv, TELNET_MAGIC);
448        (void)close(tn->fd);
449        FREE_OBJ(tn);
450}
451
452static struct telnet *
453telnet_new(int fd)
454{
455        struct telnet *tn;
456
457        ALLOC_OBJ(tn, TELNET_MAGIC);
458        AN(tn);
459        tn->fd = fd;
460        return (tn);
461}
462
463static int
464telnet_accept(const struct vev *ev, int what)
465{
466        struct vsb *vsb;
467        struct sockaddr_storage addr;
468        socklen_t addrlen;
469        struct telnet *tn;
470        int i;
471
472        (void)what;
473        addrlen = sizeof addr;
474        i = accept(ev->fd, (void *)&addr, &addrlen);
475        if (i < 0 && errno == EBADF)
476                return (1);
477        if (i < 0)
478                return (0);
479
480        mgt_got_fd(i);
481        tn = telnet_new(i);
482        vsb = sock_id("telnet", i);
483        mgt_cli_setup(i, i, 0, VSB_data(vsb), telnet_close, tn);
484        VSB_delete(vsb);
485        return (0);
486}
487
488void
489mgt_cli_secret(const char *S_arg)
490{
491        int i, fd;
492        char buf[BUFSIZ];
493        char *p;
494
495        /* Save in shmem */
496        i = strlen(S_arg);
497        p = VSM_Alloc(i + 1, "Arg", "-S", "");
498        AN(p);
499        strcpy(p, S_arg);
500
501        srandomdev();
502        fd = open(S_arg, O_RDONLY);
503        if (fd < 0) {
504                fprintf(stderr, "Can not open secret-file \"%s\"\n", S_arg);
505                exit (2);
506        }
507        mgt_got_fd(fd);
508        i = read(fd, buf, sizeof buf);
509        if (i == 0) {
510                fprintf(stderr, "Empty secret-file \"%s\"\n", S_arg);
511                exit (2);
512        }
513        if (i < 0) {
514                fprintf(stderr, "Can not read secret-file \"%s\"\n", S_arg);
515                exit (2);
516        }
517        AZ(close(fd));
518        secret_file = S_arg;
519}
520
521void
522mgt_cli_telnet(const char *T_arg)
523{
524        struct vss_addr **ta;
525        int i, n, sock, good;
526        struct telnet *tn;
527        char *p;
528        struct vsb *vsb;
529        char abuf[VTCP_ADDRBUFSIZE];
530        char pbuf[VTCP_PORTBUFSIZE];
531
532        n = VSS_resolve(T_arg, NULL, &ta);
533        if (n == 0) {
534                REPORT(LOG_ERR, "-T %s Could not be resolved\n", T_arg);
535                exit(2);
536        }
537        good = 0;
538        vsb = VSB_new_auto();
539        XXXAN(vsb);
540        for (i = 0; i < n; ++i) {
541                sock = VSS_listen(ta[i], 10);
542                if (sock < 0)
543                        continue;
544                VTCP_myname(sock, abuf, sizeof abuf, pbuf, sizeof pbuf);
545                VSB_printf(vsb, "%s %s\n", abuf, pbuf);
546                good++;
547                tn = telnet_new(sock);
548                tn->ev = vev_new();
549                XXXAN(tn->ev);
550                tn->ev->fd = sock;
551                tn->ev->fd_flags = POLLIN;
552                tn->ev->callback = telnet_accept;
553                AZ(vev_add(mgt_evb, tn->ev));
554                free(ta[i]);
555                ta[i] = NULL;
556        }
557        free(ta);
558        if (good == 0) {
559                REPORT(LOG_ERR, "-T %s could not be listened on.", T_arg);
560                exit(2);
561        }
562        AZ(VSB_finish(vsb));
563        /* Save in shmem */
564        p = VSM_Alloc(VSB_len(vsb) + 1, "Arg", "-T", "");
565        AN(p);
566        strcpy(p, VSB_data(vsb));
567        VSB_delete(vsb);
568}
569
570/* Reverse CLI ("Master") connections --------------------------------*/
571
572static int M_fd = -1;
573static struct vev *M_poker, *M_conn;
574static struct vss_addr **M_ta;
575static int M_nta, M_nxt;
576static double M_poll = 0.1;
577
578static void
579Marg_closer(void *priv)
580{
581
582        (void)priv;
583        (void)close(M_fd);
584        M_fd = -1;
585}
586
587static int
588Marg_poker(const struct vev *e, int what)
589{
590        struct vsb *vsb;
591        int s, k;
592        socklen_t l;
593
594        (void)what;     /* XXX: ??? */
595
596        if (e == M_conn) {
597                /* Our connect(2) returned, check result */
598                l = sizeof k;
599                AZ(getsockopt(M_fd, SOL_SOCKET, SO_ERROR, &k, &l));
600                if (k) {
601                        errno = k;
602                        syslog(LOG_INFO, "Could not connect to CLI-master: %m");
603                        (void)close(M_fd);
604                        M_fd = -1;
605                        /* Try next address */
606                        if (++M_nxt >= M_nta) {
607                                M_nxt = 0;
608                                if (M_poll < 10)
609                                        M_poll *= 2;
610                        }
611                        return (1);
612                }
613                vsb = sock_id("master", M_fd);
614                mgt_cli_setup(M_fd, M_fd, 0, VSB_data(vsb), Marg_closer, NULL);
615                VSB_delete(vsb);
616                M_poll = 1;
617                return (1);
618        }
619
620        assert(e == M_poker);
621
622        M_poker->timeout = M_poll;      /* XXX nasty ? */
623        if (M_fd >= 0)
624                return (0);
625
626        /* Try to connect asynchronously */
627        s = VSS_connect(M_ta[M_nxt], 1);
628        if (s < 0)
629                return (0);
630
631        mgt_got_fd(s);
632
633        M_conn = vev_new();
634        AN(M_conn);
635        M_conn->callback = Marg_poker;
636        M_conn->name = "-M connector";
637        M_conn->fd_flags = EV_WR;
638        M_conn->fd = s;
639        M_fd = s;
640        AZ(vev_add(mgt_evb, M_conn));
641        return (0);
642}
643
644void
645mgt_cli_master(const char *M_arg)
646{
647        (void)M_arg;
648
649        M_nta = VSS_resolve(M_arg, NULL, &M_ta);
650        if (M_nta <= 0) {
651                fprintf(stderr, "Could resolve -M argument to address\n");
652                exit (1);
653        }
654        M_nxt = 0;
655        AZ(M_poker);
656        M_poker = vev_new();
657        AN(M_poker);
658        M_poker->timeout = M_poll;
659        M_poker->callback = Marg_poker;
660        M_poker->name = "-M poker";
661        AZ(vev_add(mgt_evb, M_poker));
662}
Note: See TracBrowser for help on using the repository browser.