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