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