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