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 91
cli_write(int sock, const char *s)
79
{
80
        int i, l;
81
82 91
        i = strlen(s);
83 91
        l = write (sock, s, i);
84 91
        if (i == l)
85 182
                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 12
cli_sock(const char *T_arg, const char *S_arg)
98
{
99
        int fd;
100
        int sock;
101
        unsigned status;
102 12
        char *answer = NULL;
103
        char buf[CLI_AUTH_RESPONSE_LEN + 1];
104
        const char *err;
105
106 12
        sock = VTCP_open(T_arg, NULL, timeout, &err);
107 12
        if (sock < 0) {
108 0
                fprintf(stderr, "Connection failed (%s): %s\n", T_arg, err);
109 0
                return (-1);
110
        }
111
112 12
        (void)VCLI_ReadResult(sock, &status, &answer, timeout);
113 12
        if (status == CLIS_AUTH) {
114 12
                if (S_arg == NULL) {
115 0
                        fprintf(stderr, "Authentication required\n");
116 0
                        closefd(&sock);
117 0
                        return(-1);
118
                }
119 12
                fd = open(S_arg, O_RDONLY);
120 12
                if (fd < 0) {
121 0
                        fprintf(stderr, "Cannot open \"%s\": %s\n",
122 0
                            S_arg, strerror(errno));
123 0
                        closefd(&sock);
124 0
                        return (-1);
125
                }
126 12
                VCLI_AuthResponse(fd, answer, buf);
127 12
                closefd(&fd);
128 12
                free(answer);
129
130 12
                cli_write(sock, "auth ");
131 12
                cli_write(sock, buf);
132 12
                cli_write(sock, "\n");
133 12
                (void)VCLI_ReadResult(sock, &status, &answer, timeout);
134
        }
135 12
        if (status != CLIS_OK && status != CLIS_TRUNCATED) {
136 0
                fprintf(stderr, "Rejected %u\n%s\n", status, answer);
137 0
                closefd(&sock);
138 0
                free(answer);
139 0
                return (-1);
140
        }
141 12
        free(answer);
142
143 12
        cli_write(sock, "ping\n");
144 12
        (void)VCLI_ReadResult(sock, &status, &answer, timeout);
145 12
        if (status != CLIS_OK || strstr(answer, "PONG") == NULL) {
146 0
                fprintf(stderr, "No pong received from server\n");
147 0
                closefd(&sock);
148 0
                free(answer);
149 0
                return (-1);
150
        }
151 12
        free(answer);
152
153 12
        return (sock);
154
}
155
156
static void
157 10
do_args(int sock, int argc, char * const *argv)
158
{
159
        int i;
160
        unsigned status;
161 10
        char *answer = NULL;
162
163 25
        for (i=0; i<argc; i++) {
164
                /* XXX: We should really CLI-quote these */
165 15
                if (i > 0)
166 5
                        cli_write(sock, " ");
167 15
                cli_write(sock, argv[i]);
168
        }
169 10
        cli_write(sock, "\n");
170
171 10
        (void)VCLI_ReadResult(sock, &status, &answer, 2000);
172
173
        /* XXX: AZ() ? */
174 10
        (void)close(sock);
175
176 10
        printf("%s\n", answer);
177 10
        if (status == CLIS_OK || status == CLIS_TRUNCATED)
178 7
                exit(0);
179 3
        fprintf(stderr, "Command failed with error code %u\n", status);
180 3
        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 4
send_line(char *l)
189
{
190 4
        if (l) {
191 4
                cli_write(_line_sock, l);
192 4
                cli_write(_line_sock, "\n");
193 4
                if (*l)
194 4
                        add_history(l);
195 4
                rl_callback_handler_install("varnish> ", send_line);
196
        } else {
197 0
                RL_EXIT(0);
198
        }
199 4
}
200
201
static char *commands[256];
202
static char *
203 12
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 12
        if (!state) {
212 5
                list_index = 0;
213 5
                len = strlen(text);
214
        }
215
216 97
        while ((name = commands[list_index]) != NULL) {
217 80
                list_index++;
218 80
                if (strncmp (name, text, len) == 0)
219 7
                        return (strdup(name));
220
        }
221
        /* If no names matched, then return NULL. */
222 5
        return (NULL);
223
}
224
225
static char **
226 5
varnishadm_completion (const char *text, int start, int end)
227
{
228
        char **matches;
229
        (void)end;
230 5
        matches = (char **)NULL;
231 5
        if (start == 0)
232 5
                matches = rl_completion_matches(text, command_generator);
233 5
        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 1
interactive(int sock)
242
{
243
        struct pollfd fds[2];
244
        char buf[1024];
245
        int i;
246 1
        char *answer = NULL;
247
        unsigned u, status;
248 1
        _line_sock = sock;
249 1
        rl_already_prompted = 1;
250 1
        rl_callback_handler_install("varnish> ", send_line);
251 1
        rl_attempted_completion_function = varnishadm_completion;
252
253 1
        fds[0].fd = sock;
254 1
        fds[0].events = POLLIN;
255 1
        fds[1].fd = 0;
256 1
        fds[1].events = POLLIN;
257
258
        /* Grab the commands, for completion */
259 1
        cli_write(sock, "help\n");
260 1
        u = VCLI_ReadResult(fds[0].fd, &status, &answer, timeout);
261 1
        if (!u) {
262
                char *t, c[128];
263 1
                if (status == CLIS_COMMS) {
264 0
                        RL_EXIT(0);
265
                }
266 1
                t = answer;
267
268 1
                i = 0;
269 18
                while (*t) {
270 16
                        if (sscanf(t, "%127s", c) == 1) {
271 16
                                commands[i++] = strdup(c);
272 457
                                while (*t != '\n' && *t != '\0')
273 425
                                        t++;
274 16
                                if (*t == '\n')
275 16
                                        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 1
        free(answer);
286 1
        cli_write(sock, "banner\n");
287
        while (1) {
288 73
                i = poll(fds, 2, -1);
289 37
                if (i == -1 && errno == EINTR) {
290 0
                        continue;
291
                }
292 37
                assert(i > 0);
293 37
                if (fds[0].revents & POLLIN) {
294
                        /* Get rid of the prompt, kinda hackish */
295 6
                        u = write(1, "\r           \r", 13);
296 6
                        u = VCLI_ReadResult(fds[0].fd, &status, &answer,
297
                            timeout);
298 6
                        if (u) {
299 1
                                if (status == CLIS_COMMS)
300 1
                                        RL_EXIT(0);
301 0
                                if (answer)
302 0
                                        fprintf(stderr, "%s\n", answer);
303 0
                                RL_EXIT(1);
304
                        }
305
306 5
                        bprintf(buf, "%u\n", status);
307 5
                        u = write(1, buf, strlen(buf));
308 5
                        if (answer) {
309 5
                                u = write(1, answer, strlen(answer));
310 5
                                u = write(1, "\n", 1);
311 5
                                free(answer);
312 5
                                answer = NULL;
313
                        }
314 5
                        rl_forced_update_display();
315
                }
316 36
                if (fds[1].revents & POLLIN) {
317 31
                        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 1
pass(int sock)
327
{
328
        struct pollfd fds[2];
329
        char buf[1024];
330
        int i;
331 1
        char *answer = NULL;
332
        unsigned u, status;
333
        ssize_t n;
334
335 1
        fds[0].fd = sock;
336 1
        fds[0].events = POLLIN;
337 1
        fds[1].fd = 0;
338 1
        fds[1].events = POLLIN;
339
        while (1) {
340 15
                i = poll(fds, 2, -1);
341 8
                if (i == -1 && errno == EINTR) {
342 0
                        continue;
343
                }
344 8
                assert(i > 0);
345 8
                if (fds[0].revents & POLLIN) {
346 4
                        u = VCLI_ReadResult(fds[0].fd, &status, &answer,
347
                            timeout);
348 4
                        if (u) {
349 1
                                if (status == CLIS_COMMS)
350 1
                                        RL_EXIT(0);
351 0
                                if (answer)
352 0
                                        fprintf(stderr, "%s\n", answer);
353 0
                                RL_EXIT(1);
354
                        }
355
356 3
                        bprintf(buf, "%u\n", status);
357 3
                        u = write(1, buf, strlen(buf));
358 3
                        if (answer) {
359 3
                                u = write(1, answer, strlen(answer));
360 3
                                u = write(1, "\n", 1);
361 3
                                free(answer);
362 3
                                answer = NULL;
363
                        }
364
                }
365 7
                if (fds[1].revents & POLLIN || fds[1].revents & POLLHUP) {
366 4
                        n = read(fds[1].fd, buf, sizeof buf - 1);
367 4
                        if (n == 0) {
368 1
                                AZ(shutdown(sock, SHUT_WR));
369 1
                                fds[1].fd = -1;
370 3
                        } else if (n < 0) {
371 0
                                RL_EXIT(0);
372
                        } else {
373 3
                                buf[n] = '\0';
374 3
                                cli_write(sock, buf);
375
                        }
376
                }
377
        }
378
}
379
380
381
static void v_noreturn_
382 0
usage(int status)
383
{
384 0
        fprintf(stderr,
385
            "Usage: varnishadm [-h] [-n ident] [-t timeout] [-S secretfile] "
386
            "[-T [address]:port] [command [...]]\n");
387 0
        fprintf(stderr, "\t-n is mutually exclusive with -S and -T\n");
388 0
        exit(status);
389
}
390
391
static int
392 12
n_arg_sock(const char *n_arg, const char *t_arg)
393
{
394 12
        char *T_arg = NULL, *T_start = NULL;
395 12
        char *S_arg = NULL;
396
        struct vsm *vsm;
397
        char *p;
398
        int sock;
399
400 12
        vsm = VSM_New();
401 12
        AN(vsm);
402 24
        if (VSM_Arg(vsm, 'n', n_arg) < 0 ||
403 24
            VSM_Arg(vsm, 't', t_arg) < 0 ||
404 12
            VSM_Attach(vsm, STDERR_FILENO) < 0) {
405 0
                fprintf(stderr, "%s\n", VSM_Error(vsm));
406 0
                VSM_Destroy(&vsm);
407 0
                return (-1);
408
        }
409
410 12
        T_start = T_arg = VSM_Dup(vsm, "Arg", "-T");
411 12
        S_arg = VSM_Dup(vsm, "Arg", "-S");
412 12
        VSM_Destroy(&vsm);
413
414 12
        if (T_arg == NULL) {
415 0
                fprintf(stderr, "No -T in shared memory\n");
416 0
                return (-1);
417
        }
418
419 12
        sock = -1;
420 24
        while (*T_arg) {
421 12
                p = strchr(T_arg, '\n');
422 12
                AN(p);
423 12
                *p = '\0';
424 12
                sock = cli_sock(T_arg, S_arg);
425 12
                if (sock >= 0)
426 12
                        break;
427 0
                T_arg = p + 1;
428
        }
429 12
        free(T_start);
430 12
        free(S_arg);
431 12
        return (sock);
432
}
433
434
int
435 12
main(int argc, char * const *argv)
436
{
437 12
        const char *T_arg = NULL;
438 12
        const char *S_arg = NULL;
439 12
        const char *n_arg = NULL;
440 12
        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 36
        while ((opt = getopt(argc, argv, "+hn:S:T:t:")) != -1) {
452 12
                switch (opt) {
453
                case 'h':
454
                        /* Usage help */
455 0
                        usage(0);
456
                case 'n':
457 12
                        n_arg = optarg;
458 12
                        break;
459
                case 'S':
460 0
                        S_arg = optarg;
461 0
                        break;
462
                case 'T':
463 0
                        T_arg = optarg;
464 0
                        break;
465
                case 't':
466 0
                        t_arg = optarg;
467 0
                        break;
468
                default:
469 0
                        usage(1);
470
                }
471
        }
472
473 12
        argc -= optind;
474 12
        argv += optind;
475
476 12
        if (T_arg != NULL) {
477 0
                if (n_arg != NULL)
478 0
                        usage(1);
479 0
                sock = cli_sock(T_arg, S_arg);
480
        } else {
481 12
                if (S_arg != NULL)
482 0
                        usage(1);
483 12
                sock = n_arg_sock(n_arg, t_arg);
484
        }
485 12
        if (sock < 0)
486 0
                exit(2);
487
488 12
        if (argc > 0)
489 10
                do_args(sock, argc, argv);
490 2
        else if (isatty(0))
491 1
                interactive(sock);
492
        else
493 1
                pass(sock);
494 0
        exit(0);
495
}