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 <signal.h>
58
#include <stdint.h>
59
#include <stdlib.h>
60
#include <string.h>
61
#include <unistd.h>
62
63
#include "vdef.h"
64
65
#include "vapi/vsig.h"
66
#include "vapi/vsm.h"
67
#include "vas.h"
68
#include "vcli.h"
69
#include "vtcp.h"
70
71
#define RL_EXIT(status) \
72
        do { \
73
                rl_callback_handler_remove(); \
74
                exit(status); \
75
        } while (0)
76
77
78
static const double timeout = 5;        // XXX should be settable by arg ?
79
80
static void
81 178
cli_write(int sock, const char *s)
82
{
83
        int i, l;
84
85 178
        i = strlen(s);
86 178
        l = write (sock, s, i);
87 178
        if (i == l)
88 178
                return;
89 0
        perror("Write error CLI socket");
90 0
        RL_EXIT(1);
91 178
}
92
93
/*
94
 * This function establishes a connection to the specified ip and port and
95
 * sends a command to varnishd. If varnishd returns an OK status, the result
96
 * is printed and 0 returned. Else, an error message is printed and 1 is
97
 * returned
98
 */
99
static int
100 29
cli_sock(const char *T_arg, const char *S_arg)
101
{
102
        int fd;
103
        int sock;
104
        unsigned status;
105 29
        char *answer = NULL;
106
        char buf[CLI_AUTH_RESPONSE_LEN + 1];
107
        const char *err;
108
109 29
        sock = VTCP_open(T_arg, NULL, timeout, &err);
110 29
        if (sock < 0) {
111 1
                fprintf(stderr, "Connection failed (%s): %s\n", T_arg, err);
112 1
                return (-1);
113
        }
114
115 28
        (void)VCLI_ReadResult(sock, &status, &answer, timeout);
116 28
        if (status == CLIS_AUTH) {
117 27
                if (S_arg == NULL) {
118 1
                        fprintf(stderr, "Authentication required\n");
119 1
                        closefd(&sock);
120 1
                        return (-1);
121
                }
122 26
                fd = open(S_arg, O_RDONLY);
123 26
                if (fd < 0) {
124 2
                        fprintf(stderr, "Cannot open \"%s\": %s\n",
125 1
                            S_arg, strerror(errno));
126 1
                        closefd(&sock);
127 1
                        return (-1);
128
                }
129 25
                VCLI_AuthResponse(fd, answer, buf);
130 25
                closefd(&fd);
131 25
                free(answer);
132
133 25
                cli_write(sock, "auth ");
134 25
                cli_write(sock, buf);
135 25
                cli_write(sock, "\n");
136 25
                (void)VCLI_ReadResult(sock, &status, &answer, timeout);
137 25
        }
138 26
        if (status != CLIS_OK && status != CLIS_TRUNCATED) {
139 2
                fprintf(stderr, "Rejected %u\n%s\n", status, answer);
140 2
                closefd(&sock);
141 2
                free(answer);
142 2
                return (-1);
143
        }
144 24
        free(answer);
145
146 24
        cli_write(sock, "ping\n");
147 24
        (void)VCLI_ReadResult(sock, &status, &answer, timeout);
148 24
        if (status != CLIS_OK || strstr(answer, "PONG") == NULL) {
149 1
                fprintf(stderr, "No pong received from server\n");
150 1
                closefd(&sock);
151 1
                free(answer);
152 1
                return (-1);
153
        }
154 23
        free(answer);
155
156 23
        return (sock);
157 29
}
158
159
static void v_noreturn_
160 21
do_args(int sock, int argc, char * const *argv)
161
{
162
        int i;
163
        unsigned status;
164 21
        char *answer = NULL;
165
166 54
        for (i = 0; i < argc; i++) {
167
                /* XXX: We should really CLI-quote these */
168 33
                if (i > 0)
169 12
                        cli_write(sock, " ");
170 33
                cli_write(sock, argv[i]);
171 33
        }
172 21
        cli_write(sock, "\n");
173
174 21
        (void)VCLI_ReadResult(sock, &status, &answer, 2000);
175
176
        /* XXX: AZ() ? */
177 21
        (void)close(sock);
178
179 21
        printf("%s\n", answer);
180 21
        if (status == CLIS_OK)
181 14
                exit(0);
182 7
        if (status == CLIS_TRUNCATED) {
183 1
                printf("[response was truncated]\n");
184 1
                exit(0);
185
        }
186 6
        fprintf(stderr, "Command failed with error code %u\n", status);
187 6
        exit(1);
188 0
}
189
190
/* Callback for readline, doesn't take a private pointer, so we need
191
 * to have a global variable.
192
 */
193
static int _line_sock;
194
static void v_matchproto_()
195 4
send_line(char *l)
196
{
197 4
        if (l) {
198 4
                cli_write(_line_sock, l);
199 4
                cli_write(_line_sock, "\n");
200 4
                if (*l)
201 4
                        add_history(l);
202 4
                rl_callback_handler_install("varnish> ", send_line);
203 4
        } else {
204 0
                RL_EXIT(0);
205
        }
206 4
}
207
208
static char *commands[256];
209
static char *
210 12
command_generator (const char *text, int state)
211
{
212
        static int list_index, len;
213
        const char *name;
214
215
        /* If this is a new word to complete, initialize now.  This
216
           includes saving the length of TEXT for efficiency, and
217
           initializing the index variable to 0. */
218 12
        if (!state) {
219 5
                list_index = 0;
220 5
                len = strlen(text);
221 5
        }
222
223 140
        while ((name = commands[list_index]) != NULL) {
224 135
                list_index++;
225 135
                if (strncmp (name, text, len) == 0)
226 7
                        return (strdup(name));
227
        }
228
        /* If no names matched, then return NULL. */
229 5
        return (NULL);
230 12
}
231
232
static char **
233 5
varnishadm_completion (const char *text, int start, int end)
234
{
235
        char **matches;
236 5
        (void)end;
237 5
        matches = (char **)NULL;
238 5
        if (start == 0)
239 5
                matches = rl_completion_matches(text, command_generator);
240 5
        return (matches);
241
}
242
243
static void
244 10
pass_answer(int fd)
245
{
246
        unsigned u, status;
247 10
        char *answer = NULL;
248
249 10
        u = VCLI_ReadResult(fd, &status, &answer, timeout);
250 10
        if (u) {
251 2
                if (status == CLIS_COMMS)
252 2
                        RL_EXIT(0);
253 0
                if (answer)
254 0
                        fprintf(stderr, "%s\n", answer);
255 0
                RL_EXIT(1);
256 0
        }
257
258 8
        printf("%u\n", status);
259 8
        if (answer) {
260 8
                printf("%s\n", answer);
261 8
                free(answer);
262 8
        }
263 8
        if (status == CLIS_TRUNCATED)
264 0
                printf("[response was truncated]\n");
265 8
        (void)fflush(stdout);
266 8
}
267
268
/*
269
 * No arguments given, simply pass bytes on stdin/stdout and CLI socket
270
 * Send a "banner" to varnish, to provoke a welcome message.
271
 */
272
static void v_noreturn_
273 1
interactive(int sock)
274
{
275
        struct pollfd fds[2];
276
        int i;
277 1
        char *answer = NULL;
278
        unsigned u, status;
279 1
        _line_sock = sock;
280 1
        rl_already_prompted = 1;
281 1
        rl_callback_handler_install("varnish> ", send_line);
282 1
        rl_attempted_completion_function = varnishadm_completion;
283
284 1
        fds[0].fd = sock;
285 1
        fds[0].events = POLLIN;
286 1
        fds[1].fd = 0;
287 1
        fds[1].events = POLLIN;
288
289
        /* Grab the commands, for completion */
290 1
        cli_write(sock, "help\n");
291 1
        u = VCLI_ReadResult(fds[0].fd, &status, &answer, timeout);
292 1
        if (!u) {
293
                char *t, c[128];
294 1
                if (status == CLIS_COMMS) {
295 0
                        RL_EXIT(0);
296 0
                }
297 1
                t = answer;
298
299 1
                i = 0;
300 28
                while (*t) {
301 27
                        if (sscanf(t, "%127s", c) == 1) {
302 27
                                commands[i++] = strdup(c);
303 693
                                while (*t != '\n' && *t != '\0')
304 666
                                        t++;
305 27
                                if (*t == '\n')
306 27
                                        t++;
307 27
                        } else {
308
                                /* what? */
309 0
                                fprintf(stderr, "Unknown command '%s' parsing "
310
                                        "help output. Tab completion may be "
311 0
                                        "broken\n", t);
312 0
                                break;
313
                        }
314
                }
315 1
        }
316 1
        free(answer);
317 1
        cli_write(sock, "banner\n");
318 39
        while (1) {
319 39
                i = poll(fds, 2, -1);
320 39
                if (i == -1 && errno == EINTR) {
321 0
                        continue;
322
                }
323 39
                assert(i > 0);
324 39
                if (fds[0].revents & POLLIN) {
325
                        /* Get rid of the prompt, kinda hackish */
326 6
                        printf("\r           \r");
327 6
                        pass_answer(fds[0].fd);
328 6
                        rl_forced_update_display();
329 6
                }
330 38
                if (fds[1].revents & POLLIN) {
331 33
                        rl_callback_read_char();
332 33
                }
333
        }
334 0
}
335
336
/*
337
 * No arguments given, simply pass bytes on stdin/stdout and CLI socket
338
 */
339
static void v_noreturn_
340 1
pass(int sock)
341
{
342
        struct pollfd fds[2];
343
        char buf[1024];
344
        int i;
345
        ssize_t n;
346
347 1
        fds[0].fd = sock;
348 1
        fds[0].events = POLLIN;
349 1
        fds[1].fd = 0;
350 1
        fds[1].events = POLLIN;
351 8
        while (1) {
352 8
                i = poll(fds, 2, -1);
353 8
                if (i == -1 && errno == EINTR) {
354 0
                        continue;
355
                }
356 8
                assert(i > 0);
357 8
                if (fds[0].revents & POLLIN)
358 4
                        pass_answer(fds[0].fd);
359 7
                if (fds[1].revents & POLLIN || fds[1].revents & POLLHUP) {
360 4
                        n = read(fds[1].fd, buf, sizeof buf - 1);
361 4
                        if (n == 0) {
362 1
                                AZ(shutdown(sock, SHUT_WR));
363 1
                                fds[1].fd = -1;
364 4
                        } else if (n < 0) {
365 0
                                RL_EXIT(0);
366 0
                        } else {
367 3
                                buf[n] = '\0';
368 3
                                cli_write(sock, buf);
369
                        }
370 4
                }
371
        }
372 0
}
373
374
375
static void v_noreturn_
376 1
usage(int status)
377
{
378 1
        fprintf(stderr,
379
            "Usage: varnishadm [-h] [-n ident] [-t timeout] [-S secretfile] "
380
            "[-T [address]:port] [command [...]]\n");
381 1
        fprintf(stderr, "\t-n is mutually exclusive with -S and -T\n");
382 1
        exit(status);
383 0
}
384
385
static int
386 23
n_arg_sock(const char *n_arg, const char *t_arg)
387
{
388
        char *T_arg, *T_start;
389
        char *S_arg;
390
        struct vsm *vsm;
391
        char *p;
392
        int sock;
393
394 23
        vsm = VSM_New();
395 23
        AN(vsm);
396 46
        if (VSM_Arg(vsm, 'n', n_arg) < 0 ||
397 23
            VSM_Arg(vsm, 't', t_arg) < 0 ||
398 23
            VSM_Attach(vsm, STDERR_FILENO) < 0) {
399 1
                fprintf(stderr, "%s\n", VSM_Error(vsm));
400 1
                VSM_Destroy(&vsm);
401 1
                return (-1);
402
        }
403
404 22
        T_start = T_arg = VSM_Dup(vsm, "Arg", "-T");
405 22
        S_arg = VSM_Dup(vsm, "Arg", "-S");
406 22
        VSM_Destroy(&vsm);
407
408 22
        if (T_arg == NULL) {
409 0
                fprintf(stderr, "No -T in shared memory\n");
410 0
                return (-1);
411
        }
412
413 22
        sock = -1;
414 22
        while (*T_arg) {
415 22
                p = strchr(T_arg, '\n');
416 22
                AN(p);
417 22
                *p = '\0';
418 22
                sock = cli_sock(T_arg, S_arg);
419 22
                if (sock >= 0)
420 22
                        break;
421 0
                T_arg = p + 1;
422
        }
423 22
        free(T_start);
424 22
        free(S_arg);
425 22
        return (sock);
426 23
}
427
428
int
429 31
main(int argc, char * const *argv)
430
{
431 31
        const char *T_arg = NULL;
432 31
        const char *S_arg = NULL;
433 31
        const char *n_arg = NULL;
434 31
        const char *t_arg = NULL;
435
        int opt, sock;
436
437
        /*
438
         * By default linux::getopt(3) mangles the argv order, such that
439
         *      varnishadm -n bla param.set foo -bar
440
         * gets interpreted as
441
         *      varnishadm -n bla -bar param.set foo
442
         * The '+' stops that from happening
443
         * See #1496
444
         */
445 68
        while ((opt = getopt(argc, argv, "+hn:S:T:t:")) != -1) {
446 38
                switch (opt) {
447
                case 'h':
448
                        /* Usage help */
449 0
                        usage(0);
450
                case 'n':
451 23
                        n_arg = optarg;
452 23
                        break;
453
                case 'S':
454 6
                        S_arg = optarg;
455 6
                        break;
456
                case 'T':
457 7
                        T_arg = optarg;
458 7
                        break;
459
                case 't':
460 1
                        t_arg = optarg;
461 1
                        break;
462
                default:
463 1
                        usage(1);
464
                }
465
        }
466
467 30
        argc -= optind;
468 30
        argv += optind;
469
470 30
        if (T_arg != NULL) {
471 7
                if (n_arg != NULL)
472 0
                        usage(1);
473 7
                sock = cli_sock(T_arg, S_arg);
474 7
        } else {
475 23
                if (S_arg != NULL)
476 0
                        usage(1);
477 23
                sock = n_arg_sock(n_arg, t_arg);
478
        }
479 30
        if (sock < 0)
480 7
                exit(2);
481
482 23
        if (argc > 0) {
483 21
                VSIG_Arm_int();
484 21
                VSIG_Arm_term();
485 21
                do_args(sock, argc, argv);
486
                NEEDLESS(exit(0));
487
        }
488
489 2
        if (isatty(0))
490 1
                interactive(sock);
491
        else
492 1
                pass(sock);
493
        NEEDLESS(exit(0));
494 0
}