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