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 7200
cli_write(int sock, const char *s)
89
{
90
        int i, l;
91
92 7200
        i = strlen(s);
93 7200
        l = write (sock, s, i);
94 7200
        if (i == l)
95 7200
                return;
96 0
        perror("Write error CLI socket");
97 0
        RL_EXIT(1);
98 7200
}
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 1150
cli_sock(const char *T_arg, const char *S_arg)
108
{
109
        int fd;
110
        int sock;
111
        unsigned status;
112 1150
        char *answer = NULL;
113
        char buf[CLI_AUTH_RESPONSE_LEN + 1];
114
        const char *err;
115
116 1150
        sock = VTCP_open(T_arg, NULL, timeout, &err);
117 1150
        if (sock < 0) {
118 25
                fprintf(stderr, "Connection failed (%s): %s\n", T_arg, err);
119 25
                return (-1);
120
        }
121
122 1125
        (void)VCLI_ReadResult(sock, &status, &answer, timeout);
123 1125
        if (status == CLIS_AUTH) {
124 1100
                if (S_arg == NULL) {
125 25
                        fprintf(stderr, "Authentication required\n");
126 25
                        free(answer);
127 25
                        closefd(&sock);
128 25
                        return (-1);
129
                }
130 1075
                fd = open(S_arg, O_RDONLY);
131 1075
                if (fd < 0) {
132 50
                        fprintf(stderr, "Cannot open \"%s\": %s\n",
133 25
                            S_arg, strerror(errno));
134 25
                        closefd(&sock);
135 25
                        free(answer);
136 25
                        return (-1);
137
                }
138 1050
                VCLI_AuthResponse(fd, answer, buf);
139 1050
                closefd(&fd);
140 1050
                free(answer);
141
142 1050
                cli_write(sock, "auth ");
143 1050
                cli_write(sock, buf);
144 1050
                cli_write(sock, "\n");
145 1050
                (void)VCLI_ReadResult(sock, &status, &answer, timeout);
146 1050
        }
147 1075
        if (status != CLIS_OK && status != CLIS_TRUNCATED) {
148 50
                fprintf(stderr, "Rejected %u\n%s\n", status, answer);
149 50
                closefd(&sock);
150 50
                free(answer);
151 50
                return (-1);
152
        }
153 1025
        free(answer);
154
155 1025
        cli_write(sock, "ping\n");
156 1025
        (void)VCLI_ReadResult(sock, &status, &answer, timeout);
157 1025
        if (status != CLIS_OK || strstr(answer, "PONG") == NULL) {
158 25
                fprintf(stderr, "No pong received from server\n");
159 25
                closefd(&sock);
160 25
                free(answer);
161 25
                return (-1);
162
        }
163 1000
        free(answer);
164
165 1000
        return (sock);
166 1150
}
167
168
static unsigned
169 1150
pass_answer(int fd, enum pass_mode_e mode)
170
{
171
        unsigned u, status;
172 1150
        char *answer = NULL;
173
174 1150
        u = VCLI_ReadResult(fd, &status, &answer, timeout);
175 1150
        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 1150
        if (p_arg && answer != NULL) {
186 175
                printf("%-3u %-8zu\n%s", status, strlen(answer), answer);
187 1150
        } else if (p_arg) {
188 0
                printf("%-3u %-8u\n", status, 0U);
189 0
        } else {
190 975
                if (mode == pass_interactive)
191 125
                        printf("%u\n", status);
192 975
                if (answer != NULL)
193 975
                        printf("%s\n", answer);
194 975
                if (status == CLIS_TRUNCATED)
195 25
                        printf("[response was truncated]\n");
196
        }
197 1150
        free(answer);
198 1150
        (void)fflush(stdout);
199 1150
        return (status);
200
}
201
202
static void v_noreturn_
203 850
do_args(int sock, int argc, char * const *argv)
204
{
205
        int i;
206
        unsigned status;
207
208 2100
        for (i = 0; i < argc; i++) {
209
                /* XXX: We should really CLI-quote these */
210 1250
                if (i > 0)
211 400
                        cli_write(sock, " ");
212 1250
                cli_write(sock, argv[i]);
213 1250
        }
214 850
        cli_write(sock, "\n");
215 850
        status = pass_answer(sock, pass_script);
216 850
        closefd(&sock);
217 850
        if (status == CLIS_OK || status == CLIS_TRUNCATED)
218 675
                exit(0);
219 175
        if (!p_arg)
220 175
                fprintf(stderr, "Command failed with error code %u\n", status);
221 175
        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 100
send_line(char *l)
229
{
230 100
        if (l) {
231 100
                cli_write(line_sock, l);
232 100
                cli_write(line_sock, "\n");
233 100
                if (*l)
234 100
                        add_history(l);
235 100
                rl_callback_handler_install("varnish> ", send_line);
236 100
        } else {
237 0
                RL_EXIT(0);
238
        }
239 100
}
240
241
static char *
242 350
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 350
        char *answer = NULL;
249
        const char *err;
250
251 350
        if (!state) {
252 125
                cli_write(line_sock, "help -j\n");
253 125
                u = VCLI_ReadResult(line_sock, NULL, &answer, timeout);
254 125
                if (u) {
255 0
                        free(answer);
256 0
                        return (NULL);
257
                }
258 125
                jsn_cmds = vjsn_parse(answer, &err);
259 125
                free(answer);
260 125
                if (err != NULL)
261 0
                        return (NULL);
262 125
                AN(jsn_cmds);
263 125
                AN(jsn_cmds->value);
264 125
                assert (vjsn_is_array(jsn_cmds->value));
265 125
                jv = VTAILQ_FIRST(&jsn_cmds->value->children);
266 125
                assert (vjsn_is_number(jv));
267 125
                jv = VTAILQ_NEXT(jv, list);
268 125
                assert (vjsn_is_array(jv));
269 125
                jv = VTAILQ_NEXT(jv, list);
270 125
                assert (vjsn_is_number(jv));
271 125
                jv = VTAILQ_NEXT(jv, list);
272 125
        }
273 4875
        while (jv != NULL) {
274 4750
                assert (vjsn_is_object(jv));
275 4750
                jv2 = VTAILQ_FIRST(&jv->children);
276 4750
                AN(jv2);
277 4750
                jv = VTAILQ_NEXT(jv, list);
278 4750
                assert (vjsn_is_string(jv2));
279 4750
                assert (!strcmp(jv2->name, "request"));
280 4750
                if (!strncmp(text, jv2->value, strlen(text)))
281 225
                        return (strdup(jv2->value));
282
        }
283 125
        vjsn_delete(&jsn_cmds);
284 125
        return (NULL);
285 350
}
286
287
static char **
288 125
varnishadm_completion (const char *text, int start, int end)
289
{
290
        char **matches;
291 125
        (void)end;
292 125
        matches = (char **)NULL;
293 125
        if (start == 0)
294 125
                matches = rl_completion_matches(text, command_generator);
295 125
        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 25
interactive(int sock)
305
{
306
        struct pollfd fds[2];
307
        int i;
308
        unsigned status;
309 25
        line_sock = sock;
310 25
        rl_already_prompted = 1;
311 25
        rl_callback_handler_install("varnish> ", send_line);
312 25
        rl_attempted_completion_function = varnishadm_completion;
313
314 25
        fds[0].fd = sock;
315 25
        fds[0].events = POLLIN;
316 25
        fds[1].fd = 0;
317 25
        fds[1].events = POLLIN;
318
319 25
        cli_write(sock, "banner\n");
320 950
        while (1) {
321 950
                i = poll(fds, 2, -1);
322 950
                if (i == -1 && errno == EINTR) {
323 0
                        continue;
324
                }
325 950
                assert(i > 0);
326 950
                if (fds[0].revents & POLLIN) {
327
                        /* Get rid of the prompt, kinda hackish */
328 125
                        printf("\r           \r");
329 125
                        status = pass_answer(fds[0].fd, pass_interactive);
330 125
                        rl_forced_update_display();
331 125
                        if (status == CLIS_CLOSE)
332 25
                                RL_EXIT(0);
333 100
                }
334 925
                if (fds[1].revents & POLLIN) {
335 825
                        rl_callback_read_char();
336 825
                }
337
        }
338
}
339
340
/*
341
 * No arguments given, simply pass bytes on stdin/stdout and CLI socket
342
 */
343
static void v_noreturn_
344 125
pass(int sock)
345
{
346
        struct pollfd fds[2];
347
        char buf[1024];
348
        int i;
349
        ssize_t n;
350 125
        int busy = 0;
351
352 125
        fds[0].fd = sock;
353 125
        fds[0].events = POLLIN;
354 125
        fds[1].fd = 0;
355 125
        fds[1].events = POLLIN;
356 475
        while (1) {
357 475
                i = poll(fds, 2, -1);
358 475
                if (i == -1 && errno == EINTR) {
359 0
                        continue;
360
                }
361 475
                assert(i > 0);
362 475
                if (fds[0].revents & POLLIN) {
363 175
                        (void)pass_answer(fds[0].fd, pass_script);
364 175
                        busy = 0;
365 175
                        if (fds[1].fd < 0)
366 100
                                RL_EXIT(0);
367 75
                }
368 375
                if (fds[1].revents & POLLIN || fds[1].revents & POLLHUP) {
369 300
                        n = read(fds[1].fd, buf, sizeof buf - 1);
370 300
                        if (n == 0) {
371 125
                                if (!busy)
372 25
                                        RL_EXIT(0);
373 100
                                fds[1].fd = -1;
374 275
                        } else if (n < 0) {
375 0
                                RL_EXIT(0);
376 0
                        } else {
377 175
                                busy = 1;
378 175
                                buf[n] = '\0';
379 175
                                cli_write(sock, buf);
380
                        }
381 275
                }
382
        }
383
}
384
385
386
static void v_noreturn_
387 75
usage(int status)
388
{
389 75
        fprintf(stderr,
390
            "Usage: varnishadm [-h] [-n ident] [-p] [-S secretfile] "
391
            "[-T [address]:port] [-t timeout] [command [...]]\n");
392 75
        fprintf(stderr, "\t-n is mutually exclusive with -S and -T\n");
393 75
        exit(status);
394
}
395
396
static int
397 1025
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 1025
        vsm = VSM_New();
406 1025
        AN(vsm);
407 2025
        if (VSM_Arg(vsm, 'n', n_arg) < 0 ||
408 1025
            VSM_Arg(vsm, 't', t_arg) < 0 ||
409 1000
            VSM_Attach(vsm, STDERR_FILENO) < 0) {
410 50
                fprintf(stderr, "%s\n", VSM_Error(vsm));
411 50
                VSM_Destroy(&vsm);
412 50
                return (-1);
413
        }
414
415 975
        T_start = T_arg = VSM_Dup(vsm, "Arg", "-T");
416 975
        S_arg = VSM_Dup(vsm, "Arg", "-S");
417 975
        VSM_Destroy(&vsm);
418
419 975
        if (T_arg == NULL) {
420 0
                fprintf(stderr, "No -T in shared memory\n");
421 0
                return (-1);
422
        }
423
424 975
        sock = -1;
425 975
        while (*T_arg) {
426 975
                p = strchr(T_arg, '\n');
427 975
                AN(p);
428 975
                *p = '\0';
429 975
                sock = cli_sock(T_arg, S_arg);
430 975
                if (sock >= 0)
431 975
                        break;
432 0
                T_arg = p + 1;
433
        }
434 975
        free(T_start);
435 975
        free(S_arg);
436 975
        return (sock);
437 1025
}
438
439
static int
440 75
t_arg_timeout(const char *t_arg)
441
{
442 75
        char *p = NULL;
443
444 75
        AN(t_arg);
445 75
        timeout = strtod(t_arg, &p);
446 150
        if ((p != NULL && *p != '\0') ||
447 75
            !isfinite(timeout) || timeout < 0) {
448 0
                fprintf(stderr, "-t: Invalid argument: %s", t_arg);
449 0
                return (-1);
450
        }
451 75
        return (1);
452 75
}
453
454
#define OPTARG "hn:pS:T:t:"
455
456
int
457 1275
main(int argc, char * const *argv)
458
{
459 1275
        const char *T_arg = NULL;
460 1275
        const char *S_arg = NULL;
461 1275
        const char *n_arg = NULL;
462 1275
        const char *t_arg = NULL;
463
        int opt, sock;
464
465 1275
        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 2850
        while ((opt = getopt(argc, argv, "+" OPTARG)) != -1) {
478 1650
                switch (opt) {
479
                case 'h':
480
                        /* Usage help */
481 25
                        usage(0);
482
                case 'n':
483 1000
                        n_arg = optarg;
484 1000
                        break;
485
                case 'p':
486 125
                        p_arg = 1;
487 125
                        break;
488
                case 'S':
489 150
                        S_arg = optarg;
490 150
                        break;
491
                case 'T':
492 175
                        T_arg = optarg;
493 175
                        break;
494
                case 't':
495 125
                        t_arg = optarg;
496 125
                        break;
497
                default:
498 50
                        usage(1);
499
                }
500
        }
501
502 1200
        argc -= optind;
503 1200
        argv += optind;
504
505 1200
        if (T_arg != NULL) {
506 175
                if (n_arg != NULL)
507 0
                        usage(1);
508 175
                sock = cli_sock(T_arg, S_arg);
509 175
        } else {
510 1025
                if (S_arg != NULL)
511 0
                        usage(1);
512 1025
                sock = n_arg_sock(n_arg, t_arg);
513
        }
514 1200
        if (sock < 0)
515 200
                exit(2);
516
517 1000
        if (t_arg != NULL && t_arg_timeout(t_arg) < 0)
518 0
                exit(2);
519
520 1000
        if (argc > 0) {
521 850
                VSIG_Arm_int();
522 850
                VSIG_Arm_term();
523 850
                do_args(sock, argc, argv);
524
                NEEDLESS(exit(0));
525
        }
526
527 150
        if (isatty(0) && !p_arg)
528 25
                interactive(sock);
529
        else
530 125
                pass(sock);
531
        NEEDLESS(exit(0));
532
}