TMP: add a ctdb snapshot of current ctdb master (git://git.samba.org/ctdb.git) to...
[obnox/samba/samba-obnox.git] / ctdb / libctdb / test / ctdb-test.c
1 /*
2    test driver for libctdb
3
4    Copyright (C) Rusty Russell 2010
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19 #include <unistd.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <poll.h>
23 #include <talloc.h>
24 #include <tdb.h>
25
26 /* We replace the following functions, for finer control. */
27 #define poll(fds, nfds, timeout) ctdb_test_poll((fds), (nfds), (timeout), __location__)
28 #define malloc(size) ctdb_test_malloc((size), __location__)
29 #define free(ptr) ctdb_test_free((ptr), __location__)
30 #define realloc(ptr, size) ctdb_test_realloc((ptr), (size), __location__)
31 #define read(fd, buf, count) ctdb_test_read((fd), (buf), (count), __location__)
32 #define write(fd, buf, count) ctdb_test_write((fd), (buf), (count), __location__)
33 #define socket(domain, type, protocol) ctdb_test_socket((domain), (type), (protocol), __location__)
34 #define connect(sockfd, addr, addrlen) ctdb_test_connect((sockfd), (addr), (addrlen), __location__)
35
36 #define tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, log_ctx, hash_fn) ctdb_test_tdb_open_ex((name), (hash_size), (tdb_flags), (open_flags), (mode), (log_ctx), (hash_fn), __location__)
37 #define tdb_fetch(tdb, key) ctdb_test_tdb_fetch((tdb), (key))
38
39 /* Implement these if they're ever used. */
40 #define calloc ctdb_test_calloc
41 #define select ctdb_test_select
42 #define epoll_wait ctdb_test_epoll_wait
43 #define epoll_ctl ctdb_test_epoll_ctl
44 #define tdb_open ctdb_test_tdb_open
45
46 static int ctdb_test_poll(struct pollfd *fds, nfds_t nfds, int timeout, const char *location);
47 static void *ctdb_test_malloc(size_t size, const char *location);
48 static void ctdb_test_free(void *ptr, const char *location);
49 static void *ctdb_test_realloc(void *ptr, size_t size, const char *location);
50 static ssize_t ctdb_test_read(int fd, void *buf, size_t count, const char *location);
51 static ssize_t ctdb_test_write(int fd, const void *buf, size_t count, const char *location);
52 static int ctdb_test_socket(int domain, int type, int protocol, const char *location);
53 static int ctdb_test_connect(int sockfd, const struct sockaddr *addr,
54                              socklen_t addrlen, const char *location);
55 static struct tdb_context *ctdb_test_tdb_open_ex(const char *name,
56                                                  int hash_size, int tdb_flags,
57                                                  int open_flags, mode_t mode,
58                                                  const struct tdb_logging_context *log_ctx,
59                                                  tdb_hash_func hash_fn, const char *location);
60 static TDB_DATA ctdb_test_tdb_fetch(struct tdb_context *tdb, TDB_DATA key);
61
62 #include "../sync.c"
63 #include "../control.c"
64 #include "../ctdb.c"
65 #include "../io_elem.c"
66 #include "../local_tdb.c"
67 #include "../logging.c"
68 #include "../messages.c"
69
70 #undef poll
71 #undef malloc
72 #undef realloc
73 #undef read
74 #undef write
75 #undef socket
76 #undef connect
77 #undef tdb_open_ex
78 #undef calloc
79 #undef select
80 #undef epoll_wait
81 #undef epoll_ctl
82 #undef tdb_open
83 #undef tdb_fetch
84
85 #include "ctdb-test.h"
86 #include "utils.h"
87 #include "tui.h"
88 #include "log.h"
89 #include "failtest.h"
90 #include "expect.h"
91 #include <err.h>
92
93 /* Talloc contexts */
94 void *allocations;
95 void *working;
96
97 static void run_inits(void)
98 {
99         /* Linker magic creates these to delineate section. */
100         extern initcall_t __start_init_call[], __stop_init_call[];
101         initcall_t *p;
102
103         for (p = __start_init_call; p < __stop_init_call; p++)
104                 (*p)();
105 }
106
107 static void print_license(void)
108 {
109         printf("ctdb-test, Copyright (C) 2010 Jeremy Kerr, Rusty Russell\n"
110                "ctdb-test comes with ABSOLUTELY NO WARRANTY; see COPYING.\n"
111                "This is free software, and you are welcome to redistribute\n"
112                "it under certain conditions; see COPYING for details.\n");
113 }
114
115 /*** XML Argument:
116     <section id="a:echo">
117      <title><option>--echo</option>, <option>-x</option></title>
118      <subtitle>Echo commands as they are executed</subtitle>
119      <para>ctdb-test will echo each command before it is executed. Useful when
120       commands are read from a file</para>
121     </section>
122 */
123 static void cmdline_echo(struct option *opt)
124 {
125         tui_echo_commands = 1;
126 }
127 cmdline_opt("echo", 0, 'x', cmdline_echo);
128
129 /*** XML Argument:
130     <section id="a:quiet">
131      <title><option>--quiet</option>, <option>-q</option></title>
132      <subtitle>Run quietly</subtitle>
133      <para>Causes ctdb-test to reduce its output to the minimum possible - no prompt
134       is displayed, and most warning messages are suppressed
135      </para>
136     </section>
137 */
138 static void cmdline_quiet(struct option *opt)
139 {
140         tui_quiet = 1;
141 }
142 cmdline_opt("quiet", 0, 'q', cmdline_quiet);
143
144 /*** XML Argument:
145     <section id="a:exit">
146      <title><option>--exit</option>, <option>-e</option></title>
147      <subtitle>Exit on error</subtitle>
148      <para>If <option>--exit</option> is specified, ctdb-test will exit (with a
149      non-zero error code) on the first script error it encounters (eg an
150      expect command does not match). This is the default when invoked as a
151      non-interactive script.</para>
152     </section>
153 */
154 static void cmdline_abort_on_fail(struct option *opt)
155 {
156         tui_abort_on_fail = 1;
157 }
158 cmdline_opt("exit", 0, 'e', cmdline_abort_on_fail);
159
160 /*** XML Argument:
161     <section id="a:help">
162      <title><option>--help</option></title>
163      <subtitle>Print usage information</subtitle>
164      <para>Causes ctdb-test to print its command line arguments and then exit</para>
165     </section>
166 */
167 static void cmdline_help(struct option *opt)
168 {
169         print_license();
170         print_usage();
171         exit(EXIT_SUCCESS);
172 }
173 cmdline_opt("help", 0, 'h', cmdline_help);
174
175 extern struct cmdline_option __start_cmdline[], __stop_cmdline[];
176
177 static struct cmdline_option *get_cmdline_option(int opt)
178 {
179         struct cmdline_option *copt;
180
181         /* if opt is < '0', we have been passed a long option, which is
182          * indexed directly */
183         if (opt < '0')
184                 return __start_cmdline + opt;
185
186         /* otherwise search for the short option in the .val member */
187         for (copt = __start_cmdline; copt < __stop_cmdline; copt++)
188                 if (copt->opt.val == opt)
189                         return copt;
190
191         return NULL;
192 }
193
194 static struct option *get_cmdline_options(void)
195 {
196         struct cmdline_option *copts;
197         struct option *opts;
198         unsigned int x, n_opts;
199
200         n_opts = ((void *)__stop_cmdline - (void *)__start_cmdline) /
201                 sizeof(struct cmdline_option);
202
203         opts = talloc_zero_array(NULL, struct option, n_opts + 1);
204         copts = __start_cmdline;
205
206         for (x = 0; x < n_opts; x++) {
207                 unsigned int y;
208
209                 if (copts[x].opt.has_arg > 2)
210                         errx(1, "Bad argument `%s'", copts[x].opt.name);
211
212                 for (y = 0; y < x; y++)
213                         if ((copts[x].opt.val && copts[x].opt.val
214                                                 == opts[y].val)
215                                         || streq(copts[x].opt.name,
216                                                 opts[y].name))
217                                 errx(1, "Conflicting arguments %s = %s\n",
218                                      copts[x].opt.name, opts[y].name);
219
220                 opts[x] = copts[x].opt;
221                 opts[x].val = x;
222         }
223
224         return opts;
225 }
226
227 static char *get_cmdline_optstr(void)
228 {
229         struct cmdline_option *copts;
230         unsigned int x, n_opts;
231         char *optstr, tmpstr[3], *colonstr = "::";
232
233         n_opts = ((void *)__stop_cmdline - (void *)__start_cmdline) /
234                 sizeof(struct cmdline_option);
235
236         optstr = talloc_size(NULL, 3 * n_opts * sizeof(*optstr) + 1);
237         *optstr = '\0';
238
239         copts = __start_cmdline;
240
241         for (x = 0; x < n_opts; x++) {
242                 if (!copts[x].opt.val)
243                         continue;
244                 snprintf(tmpstr, 4, "%c%s", copts[x].opt.val,
245                         colonstr + 2 - copts[x].opt.has_arg);
246                 strcat(optstr, tmpstr);
247         }
248         return optstr;
249 }
250
251 static int ctdb_test_poll(struct pollfd *fds, nfds_t nfds, int timeout,
252                           const char *location)
253 {
254         if (should_i_fail("poll", location)) {
255                 errno = EINVAL;
256                 return -1;
257         }
258         return poll(fds, nfds, timeout);
259 }
260
261 static void *ctdb_test_malloc(size_t size, const char *location)
262 {
263         if (should_i_fail("malloc", location)) {
264                 errno = ENOMEM;
265                 return NULL;
266         }
267         return talloc_named_const(allocations, size, location);
268 }
269
270 static void ctdb_test_free(void *ptr, const char *location)
271 {
272         talloc_free(ptr);
273 }
274
275 static void *ctdb_test_realloc(void *ptr, size_t size, const char *location)
276 {
277         if (should_i_fail("realloc", location)) {
278                 errno = ENOMEM;
279                 return NULL;
280         }
281         ptr = _talloc_realloc(allocations, ptr, size, location);
282         if (ptr)
283                 talloc_set_name(ptr, "%s (reallocated to %u at %s)",
284                                 talloc_get_name(ptr), size, location);
285         return ptr;
286 }
287
288 static ssize_t ctdb_test_read(int fd, void *buf, size_t count,
289                               const char *location)
290 {
291         if (should_i_fail("read", location)) {
292                 errno = EBADF;
293                 return -1;
294         }
295         /* FIXME: We only let parent read and write.
296          * We should have child do short read, at least until whole packet is
297          * read.  Then we terminate child. */
298         if (!am_parent()) {
299                 log_line(LOG_DEBUG, "Child reading fd");
300                 return 0;
301         }
302         return read(fd, buf, count);
303 }
304
305 static ssize_t ctdb_test_write(int fd, const void *buf, size_t count,
306                                const char *location)
307 {
308         if (should_i_fail("write", location)) {
309                 errno = EBADF;
310                 return -1;
311         }
312         /* FIXME: We only let parent read and write.
313          * We should have child do short write, at least until whole packet is
314          * written, then terminate child.  Check that all children and parent
315          * write the same data. */
316         if (!am_parent()) {
317                 log_line(LOG_DEBUG, "Child writing fd");
318                 return 0;
319         }
320         return write(fd, buf, count);
321 }
322
323 static int ctdb_test_socket(int domain, int type, int protocol,
324                             const char *location)
325 {
326         if (should_i_fail("socket", location)) {
327                 errno = EINVAL;
328                 return -1;
329         }
330         return socket(domain, type, protocol);
331 }
332
333 static int ctdb_test_connect(int sockfd, const struct sockaddr *addr,
334                              socklen_t addrlen, const char *location)
335 {
336         if (should_i_fail("connect", location)) {
337                 errno = EINVAL;
338                 return -1;
339         }
340         return connect(sockfd, addr, addrlen);
341 }
342
343 static struct tdb_context *ctdb_test_tdb_open_ex(const char *name,
344                                                  int hash_size, int tdb_flags,
345                                                  int open_flags, mode_t mode,
346                                                  const struct tdb_logging_context *log_ctx,
347                                                  tdb_hash_func hash_fn,
348                                                  const char *location)
349 {
350         if (should_i_fail("tdb_open_ex", location)) {
351                 errno = ENOENT;
352                 return NULL;
353         }
354         return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode,
355                            log_ctx, hash_fn);
356 }
357
358 /* We don't need to fail this, but as library expects to be able to free()
359    dptr, we need to make sure it's talloced (see ctdb_test_free) */
360 static TDB_DATA ctdb_test_tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
361 {
362         TDB_DATA ret = tdb_fetch(tdb, key);
363         if (ret.dptr) {
364                 ret.dptr = talloc_memdup(allocations, ret.dptr, ret.dsize);
365                 if (!ret.dptr) {
366                         err(1, "Could not memdup %zu bytes", ret.dsize);
367                 }
368         }
369         return ret;
370 }
371
372 void check_allocations(void)
373 {
374         talloc_free(working);
375
376         if (talloc_total_blocks(allocations) != 1) {
377                 log_line(LOG_ALWAYS, "Resource leak:");
378                 talloc_report_full(allocations, stdout);
379                 exit(1);
380         }
381 }
382
383 /* This version adds one byte (for nul term) */
384 void *grab_fd(int fd, size_t *size)
385 {
386         size_t max = 16384;
387         int ret;
388         void *buffer = talloc_array(NULL, char, max+1);
389
390         *size = 0;
391         while ((ret = read(fd, buffer + *size, max - *size)) > 0) {
392                 *size += ret;
393                 if (*size == max)
394                         buffer = talloc_realloc(NULL, buffer, char, max *= 2 + 1);
395         }
396         if (ret < 0) {
397                 talloc_free(buffer);
398                 buffer = NULL;
399         }
400         return buffer;
401 }
402
403 int main(int argc, char *argv[])
404 {
405         int input_fd, c;
406         const char *optstr;
407         struct option *options;
408
409         allocations = talloc_named_const(NULL, 1, "ctdb-test");
410         working = talloc_named_const(NULL, 1, "ctdb-test-working");
411
412         options = get_cmdline_options();
413         optstr = get_cmdline_optstr();
414
415         while ((c = getopt_long(argc, argv, optstr, options, NULL)) != EOF) {
416                 struct cmdline_option *copt = get_cmdline_option(c);
417                 if (!copt)
418                         errx(1, "Unknown argument");
419
420                 copt->parse(&copt->opt);
421         }
422
423         if (optind == argc) {
424                 log_line(LOG_DEBUG, "Disabling failtest due to stdin.");
425                 failtest = false;
426                 input_fd = STDIN_FILENO;
427         } else if (optind + 1 != argc)
428                 errx(1, "Need a single argument: input filename");
429         else {
430                 input_fd = open(argv[optind], O_RDONLY);
431                 if (input_fd < 0)
432                         err(1, "Opening %s", argv[optind]);
433                 tui_abort_on_fail = true;
434         }
435
436         run_inits();
437         if (!tui_quiet)
438                 print_license();
439
440         log_line(LOG_VERBOSE, "initialisation done");
441
442         tui_run(input_fd);
443
444         /* Everyone loves a good error haiku! */
445         if (expects_remaining())
446                 errx(1, "Expectations still / "
447                      "unfulfilled remaining. / "
448                      "Testing blossoms fail.");
449         check_allocations();
450         check_databases();
451         dump_failinfo();
452
453         return EXIT_SUCCESS;
454 }