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