varnish-cache/bin/varnishadm/varnishadm.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2015 Varnish Software AS
4
 * All rights reserved.
5
 *
6
 * Author: Cecilie Fritzvold <cecilihf@linpro.no>
7
 *
8
 * SPDX-License-Identifier: BSD-2-Clause
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions
12
 * are met:
13
 * 1. Redistributions of source code must retain the above copyright
14
 *    notice, this list of conditions and the following disclaimer.
15
 * 2. Redistributions in binary form must reproduce the above copyright
16
 *    notice, this list of conditions and the following disclaimer in the
17
 *    documentation and/or other materials provided with the distribution.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
23
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
 * SUCH DAMAGE.
30
 */
31
32
#include "config.h"
33
34
#include <sys/types.h>
35
#include <sys/socket.h>
36
37
#include <stdio.h>
38
39
#if defined(HAVE_EDIT_READLINE_READLINE_H)
40
#  include <edit/readline/readline.h>
41
#elif defined(HAVE_LIBEDIT)
42
#  include <editline/readline.h>
43
#elif defined (HAVE_READLINE_READLINE_H)
44
#  include <readline/readline.h>
45
#  ifdef HAVE_READLINE_HISTORY_H
46
#    include <readline/history.h>
47
#  else
48
#    error missing history.h - this should have got caught in configure
49
#  endif
50
#else
51
#  error missing readline.h - this should have got caught in configure
52
#endif
53
54
#include <math.h>
55
#include <fcntl.h>
56
#include <poll.h>
57
#include <stdint.h>
58
#include <stdlib.h>
59
#include <string.h>
60
#include <unistd.h>
61
62
#include "vdef.h"
63
64
#include "vqueue.h"
65
66
#include "vapi/vsig.h"
67
#include "vapi/vsm.h"
68
#include "vas.h"
69
#include "vcli.h"
70
#include "vjsn.h"
71
#include "vtcp.h"
72
73
#define RL_EXIT(status) \
74
        do { \
75
                rl_callback_handler_remove(); \
76
                exit(status); \
77
        } while (0)
78
79
enum pass_mode_e {
80
        pass_script,
81
        pass_interactive,
82
};
83
84
static double timeout = 5;
85
static int p_arg = 0;
86
static int line_sock;
87
88
static void
89 8720
cli_write(int sock, const char *s)
90
{
91
        int i, l;
92
93 8720
        i = strlen(s);
94 8720
        l = write (sock, s, i);
95 8720
        if (i == l)
96 8720
                return;
97 0
        perror("Write error CLI socket");
98 0
        RL_EXIT(1);
99 8720
}
100
101
/*
102
 * This function establishes a connection to the specified ip and port and
103
 * sends a command to varnishd. If varnishd returns an OK status, the result
104
 * is printed and 0 returned. Else, an error message is printed and 1 is
105
 * returned
106
 */
107
static int
108 1400
cli_sock(const char *T_arg, const char *S_arg)
109
{
110
        int fd;
111
        int sock;
112
        unsigned status;
113 1400
        char *answer = NULL;
114
        char buf[CLI_AUTH_RESPONSE_LEN + 1];
115
        const char *err;
116
117 1400
        sock = VTCP_open(T_arg, NULL, timeout, &err);
118 1400
        if (sock < 0) {
119 40
                fprintf(stderr, "Connection failed (%s): %s\n", T_arg, err);
120 40
                return (-1);
121
        }
122
123 1360
        (void)VCLI_ReadResult(sock, &status, &answer, timeout);
124 1360
        if (status == CLIS_AUTH) {
125 1320
                if (S_arg == NULL) {
126 40
                        fprintf(stderr, "Authentication required\n");
127 40
                        closefd(&sock);
128 40
                        return (-1);
129
                }
130 1280
                fd = open(S_arg, O_RDONLY);
131 1280
                if (fd < 0) {
132 80
                        fprintf(stderr, "Cannot open \"%s\": %s\n",
133 40
                            S_arg, strerror(errno));
134 40
                        closefd(&sock);
135 40
                        return (-1);
136
                }
137 1240
                VCLI_AuthResponse(fd, answer, buf);
138 1240
                closefd(&fd);
139 1240
                free(answer);
140
141 1240
                cli_write(sock, "auth ");
142 1240
                cli_write(sock, buf);
143 1240
                cli_write(sock, "\n");
144 1240
                (void)VCLI_ReadResult(sock, &status, &answer, timeout);
145 1240
        }
146 1280
        if (status != CLIS_OK && status != CLIS_TRUNCATED) {
147 80
                fprintf(stderr, "Rejected %u\n%s\n", status, answer);
148 80
                closefd(&sock);
149 80
                free(answer);
150 80
                return (-1);
151
        }
152 1200
        free(answer);
153
154 1200
        cli_write(sock, "ping\n");
155 1200
        (void)VCLI_ReadResult(sock, &status, &answer, timeout);
156 1200
        if (status != CLIS_OK || strstr(answer, "PONG") == NULL) {
157 40
                fprintf(stderr, "No pong received from server\n");
158 40
                closefd(&sock);
159 40
                free(answer);
160 40
                return (-1);
161
        }
162 1160
        free(answer);
163
164 1160
        return (sock);
165 1400
}
166
167
static unsigned
168 1440
pass_answer(int fd, enum pass_mode_e mode)
169
{
170
        unsigned u, status;
171 1440
        char *answer = NULL;
172
173 1440
        u = VCLI_ReadResult(fd, &status, &answer, timeout);
174 1440
        if (u) {
175 40
                if (status == CLIS_COMMS)
176 40
                        RL_EXIT(0);
177 0
                if (answer)
178 0
                        fprintf(stderr, "%s\n", answer);
179 0
                RL_EXIT(1);
180 0
        }
181
182 1400
        if (p_arg && answer != NULL) {
183 120
                printf("%-3u %-8zu\n%s", status, strlen(answer), answer);
184 1400
        } else if (p_arg) {
185 0
                printf("%-3u %-8u\n", status, 0U);
186 0
        } else {
187 1280
                if (mode == pass_interactive)
188 200
                        printf("%u\n", status);
189 1280
                if (answer != NULL)
190 1280
                        printf("%s\n", answer);
191 1280
                if (status == CLIS_TRUNCATED)
192 40
                        printf("[response was truncated]\n");
193
        }
194 1400
        free(answer);
195 1400
        (void)fflush(stdout);
196 1400
        return (status);
197
}
198
199
static void v_noreturn_
200 1080
do_args(int sock, int argc, char * const *argv)
201
{
202
        int i;
203
        unsigned status;
204
205 2640
        for (i = 0; i < argc; i++) {
206
                /* XXX: We should really CLI-quote these */
207 1560
                if (i > 0)
208 480
                        cli_write(sock, " ");
209 1560
                cli_write(sock, argv[i]);
210 1560
        }
211 1080
        cli_write(sock, "\n");
212 1080
        status = pass_answer(sock, pass_script);
213 1080
        closefd(&sock);
214 1080
        if (status == CLIS_OK || status == CLIS_TRUNCATED)
215 840
                exit(0);
216 240
        if (!p_arg)
217 240
                fprintf(stderr, "Command failed with error code %u\n", status);
218 240
        exit(1);
219
}
220
221
/* Callback for readline, doesn't take a private pointer, so we need
222
 * to have a global variable.
223
 */
224
static void v_matchproto_()
225 160
send_line(char *l)
226
{
227 160
        if (l) {
228 160
                cli_write(line_sock, l);
229 160
                cli_write(line_sock, "\n");
230 160
                if (*l)
231 160
                        add_history(l);
232 160
                rl_callback_handler_install("varnish> ", send_line);
233 160
        } else {
234 0
                RL_EXIT(0);
235
        }
236 160
}
237
238
static char *
239 560
command_generator (const char *text, int state)
240
{
241
        static struct vjsn *jsn_cmds;
242
        static const struct vjsn_val *jv;
243
        struct vjsn_val *jv2;
244
        unsigned u;
245 560
        char *answer = NULL;
246
        const char *err;
247
248 560
        if (!state) {
249 200
                cli_write(line_sock, "help -j\n");
250 200
                u = VCLI_ReadResult(line_sock, NULL, &answer, timeout);
251 200
                if (u) {
252 0
                        free(answer);
253 0
                        return (NULL);
254
                }
255 200
                jsn_cmds = vjsn_parse(answer, &err);
256 200
                free(answer);
257 200
                if (err != NULL)
258 0
                        return (NULL);
259 200
                AN(jsn_cmds);
260 200
                AN(jsn_cmds->value);
261 200
                assert (vjsn_is_array(jsn_cmds->value));
262 200
                jv = VTAILQ_FIRST(&jsn_cmds->value->children);
263 200
                assert (vjsn_is_number(jv));
264 200
                jv = VTAILQ_NEXT(jv, list);
265 200
                assert (vjsn_is_array(jv));
266 200
                jv = VTAILQ_NEXT(jv, list);
267 200
                assert (vjsn_is_number(jv));
268 200
                jv = VTAILQ_NEXT(jv, list);
269 200
        }
270 7800
        while (jv != NULL) {
271 7600
                assert (vjsn_is_object(jv));
272 7600
                jv2 = VTAILQ_FIRST(&jv->children);
273 7600
                AN(jv2);
274 7600
                jv = VTAILQ_NEXT(jv, list);
275 7600
                assert (vjsn_is_string(jv2));
276 7600
                assert (!strcmp(jv2->name, "request"));
277 7600
                if (!strncmp(text, jv2->value, strlen(text)))
278 360
                        return (strdup(jv2->value));
279
        }
280 200
        vjsn_delete(&jsn_cmds);
281 200
        return (NULL);
282 560
}
283
284
static char **
285 200
varnishadm_completion (const char *text, int start, int end)
286
{
287
        char **matches;
288 200
        (void)end;
289 200
        matches = (char **)NULL;
290 200
        if (start == 0)
291 200
                matches = rl_completion_matches(text, command_generator);
292 200
        return (matches);
293
}
294
295
296
/*
297
 * No arguments given, simply pass bytes on stdin/stdout and CLI socket
298
 * Send a "banner" to varnish, to provoke a welcome message.
299
 */
300
static void v_noreturn_
301 40
interactive(int sock)
302
{
303
        struct pollfd fds[2];
304
        int i;
305 40
        line_sock = sock;
306 40
        rl_already_prompted = 1;
307 40
        rl_callback_handler_install("varnish> ", send_line);
308 40
        rl_attempted_completion_function = varnishadm_completion;
309
310 40
        fds[0].fd = sock;
311 40
        fds[0].events = POLLIN;
312 40
        fds[1].fd = 0;
313 40
        fds[1].events = POLLIN;
314
315 40
        cli_write(sock, "banner\n");
316 1560
        while (1) {
317 1560
                i = poll(fds, 2, -1);
318 1560
                if (i == -1 && errno == EINTR) {
319 0
                        continue;
320
                }
321 1560
                assert(i > 0);
322 1560
                if (fds[0].revents & POLLIN) {
323
                        /* Get rid of the prompt, kinda hackish */
324 240
                        printf("\r           \r");
325 240
                        (void)pass_answer(fds[0].fd, pass_interactive);
326 240
                        rl_forced_update_display();
327 240
                }
328 1520
                if (fds[1].revents & POLLIN) {
329 1320
                        rl_callback_read_char();
330 1320
                }
331
        }
332
}
333
334
/*
335
 * No arguments given, simply pass bytes on stdin/stdout and CLI socket
336
 */
337
static void v_noreturn_
338 40
pass(int sock)
339
{
340
        struct pollfd fds[2];
341
        char buf[1024];
342
        int i;
343
        ssize_t n;
344 40
        int busy = 0;
345
346 40
        fds[0].fd = sock;
347 40
        fds[0].events = POLLIN;
348 40
        fds[1].fd = 0;
349 40
        fds[1].events = POLLIN;
350 280
        while (1) {
351 280
                i = poll(fds, 2, -1);
352 280
                if (i == -1 && errno == EINTR) {
353 0
                        continue;
354
                }
355 280
                assert(i > 0);
356 280
                if (fds[0].revents & POLLIN) {
357 120
                        (void)pass_answer(fds[0].fd, pass_script);
358 120
                        busy = 0;
359 120
                        if (fds[1].fd < 0)
360 0
                                RL_EXIT(0);
361 120
                }
362 280
                if (fds[1].revents & POLLIN || fds[1].revents & POLLHUP) {
363 160
                        n = read(fds[1].fd, buf, sizeof buf - 1);
364 160
                        if (n == 0) {
365 40
                                if (!busy)
366 40
                                        RL_EXIT(0);
367 0
                                fds[1].fd = -1;
368 120
                        } else if (n < 0) {
369 0
                                RL_EXIT(0);
370 0
                        } else {
371 120
                                busy = 1;
372 120
                                buf[n] = '\0';
373 120
                                cli_write(sock, buf);
374
                        }
375 120
                }
376
        }
377
}
378
379
380
static void v_noreturn_
381 120
usage(int status)
382
{
383 120
        fprintf(stderr,
384
            "Usage: varnishadm [-h] [-n ident] [-p] [-S secretfile] "
385
            "[-T [address]:port] [-t timeout] [command [...]]\n");
386 120
        fprintf(stderr, "\t-n is mutually exclusive with -S and -T\n");
387 120
        exit(status);
388
}
389
390
static int
391 1200
n_arg_sock(const char *n_arg, const char *t_arg)
392
{
393
        char *T_arg, *T_start;
394
        char *S_arg;
395
        struct vsm *vsm;
396
        char *p;
397
        int sock;
398
399 1200
        vsm = VSM_New();
400 1200
        AN(vsm);
401 2360
        if (VSM_Arg(vsm, 'n', n_arg) < 0 ||
402 1200
            VSM_Arg(vsm, 't', t_arg) < 0 ||
403 1160
            VSM_Attach(vsm, STDERR_FILENO) < 0) {
404 80
                fprintf(stderr, "%s\n", VSM_Error(vsm));
405 80
                VSM_Destroy(&vsm);
406 80
                return (-1);
407
        }
408
409 1120
        T_start = T_arg = VSM_Dup(vsm, "Arg", "-T");
410 1120
        S_arg = VSM_Dup(vsm, "Arg", "-S");
411 1120
        VSM_Destroy(&vsm);
412
413 1120
        if (T_arg == NULL) {
414 0
                fprintf(stderr, "No -T in shared memory\n");
415 0
                return (-1);
416
        }
417
418 1120
        sock = -1;
419 1120
        while (*T_arg) {
420 1120
                p = strchr(T_arg, '\n');
421 1120
                AN(p);
422 1120
                *p = '\0';
423 1120
                sock = cli_sock(T_arg, S_arg);
424 1120
                if (sock >= 0)
425 1120
                        break;
426 0
                T_arg = p + 1;
427
        }
428 1120
        free(T_start);
429 1120
        free(S_arg);
430 1120
        return (sock);
431 1200
}
432
433
static int
434 40
t_arg_timeout(const char *t_arg)
435
{
436 40
        char *p = NULL;
437
438 40
        AN(t_arg);
439 40
        timeout = strtod(t_arg, &p);
440 80
        if ((p != NULL && *p != '\0') ||
441 40
            !isfinite(timeout) || timeout < 0) {
442 0
                fprintf(stderr, "-t: Invalid argument: %s", t_arg);
443 0
                return (-1);
444
        }
445 40
        return (1);
446 40
}
447
448
#define OPTARG "hn:pS:T:t:"
449
450
int
451 1600
main(int argc, char * const *argv)
452
{
453 1600
        const char *T_arg = NULL;
454 1600
        const char *S_arg = NULL;
455 1600
        const char *n_arg = NULL;
456 1600
        const char *t_arg = NULL;
457
        int opt, sock;
458
459 1600
        if (argc == 2 && !strcmp(argv[1], "--optstring")) {
460 0
                printf(OPTARG "\n");
461 0
                exit(0);
462
        }
463
        /*
464
         * By default linux::getopt(3) mangles the argv order, such that
465
         *      varnishadm -n bla param.set foo -bar
466
         * gets interpreted as
467
         *      varnishadm -n bla -bar param.set foo
468
         * The '+' stops that from happening
469
         * See #1496
470
         */
471 3440
        while ((opt = getopt(argc, argv, "+" OPTARG)) != -1) {
472 1960
                switch (opt) {
473
                case 'h':
474
                        /* Usage help */
475 40
                        usage(0);
476
                case 'n':
477 1160
                        n_arg = optarg;
478 1160
                        break;
479
                case 'p':
480 40
                        p_arg = 1;
481 40
                        break;
482
                case 'S':
483 240
                        S_arg = optarg;
484 240
                        break;
485
                case 'T':
486 280
                        T_arg = optarg;
487 280
                        break;
488
                case 't':
489 120
                        t_arg = optarg;
490 120
                        break;
491
                default:
492 80
                        usage(1);
493
                }
494
        }
495
496 1480
        argc -= optind;
497 1480
        argv += optind;
498
499 1480
        if (T_arg != NULL) {
500 280
                if (n_arg != NULL)
501 0
                        usage(1);
502 280
                sock = cli_sock(T_arg, S_arg);
503 280
        } else {
504 1200
                if (S_arg != NULL)
505 0
                        usage(1);
506 1200
                sock = n_arg_sock(n_arg, t_arg);
507
        }
508 1480
        if (sock < 0)
509 320
                exit(2);
510
511 1160
        if (t_arg != NULL && t_arg_timeout(t_arg) < 0)
512 0
                exit(2);
513
514 1160
        if (argc > 0) {
515 1080
                VSIG_Arm_int();
516 1080
                VSIG_Arm_term();
517 1080
                do_args(sock, argc, argv);
518
                NEEDLESS(exit(0));
519
        }
520
521 80
        if (isatty(0) && !p_arg)
522 40
                interactive(sock);
523
        else
524 40
                pass(sock);
525
        NEEDLESS(exit(0));
526
}