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