s4:lib/tevent: rename structs
[ira/wip.git] / source4 / torture / raw / offline.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Andrew Tridgell 2008
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
20 /*
21   test offline files
22  */
23
24 #include "includes.h"
25 #include "torture/torture.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "system/time.h"
28 #include "system/filesys.h"
29 #include "libcli/libcli.h"
30 #include "torture/util.h"
31 #include "lib/events/events.h"
32 #include "lib/cmdline/popt_common.h"
33 #include "libcli/composite/composite.h"
34 #include "libcli/smb_composite/smb_composite.h"
35 #include "libcli/resolve/resolve.h"
36
37 #define BASEDIR "\\testoffline"
38
39 static int nconnections;
40 static int numstates;
41 static int num_connected;
42 static int test_failed;
43 extern int torture_numops;
44 extern int torture_entries;
45 static bool test_finished;
46
47 enum offline_op {OP_LOADFILE, OP_SAVEFILE, OP_SETOFFLINE, OP_GETOFFLINE, OP_ENDOFLIST};
48
49 static double latencies[OP_ENDOFLIST];
50 static double worst_latencies[OP_ENDOFLIST];
51
52 #define FILE_SIZE 8192
53
54
55 struct offline_state {
56         struct torture_context *tctx;
57         struct tevent_context *ev;
58         struct smbcli_tree *tree;
59         TALLOC_CTX *mem_ctx;
60         int client;
61         int fnum;
62         uint32_t count;
63         uint32_t lastcount;
64         uint32_t fnumber;
65         uint32_t offline_count;
66         uint32_t online_count;
67         char *fname;
68         struct smb_composite_loadfile *loadfile;
69         struct smb_composite_savefile *savefile;
70         struct smbcli_request *req;
71         enum offline_op op;
72         struct timeval tv_start;
73 };
74
75 static void test_offline(struct offline_state *state);
76
77
78 static char *filename(TALLOC_CTX *ctx, int i)
79 {
80         char *s = talloc_asprintf(ctx, BASEDIR "\\file%u.dat", i);
81         return s;
82 }
83
84
85 /*
86   called when a loadfile completes
87  */
88 static void loadfile_callback(struct composite_context *ctx) 
89 {
90         struct offline_state *state = ctx->async.private_data;
91         NTSTATUS status;
92         int i;
93
94         status = smb_composite_loadfile_recv(ctx, state->mem_ctx);
95         if (!NT_STATUS_IS_OK(status)) {
96                 printf("Failed to read file '%s' - %s\n", 
97                        state->loadfile->in.fname, nt_errstr(status));
98                 test_failed++;
99         }
100
101         /* check the data is correct */
102         if (state->loadfile->out.size != FILE_SIZE) {
103                 printf("Wrong file size %u - expected %u\n", 
104                        state->loadfile->out.size, FILE_SIZE);
105                 test_failed++;
106                 return;
107         }
108
109         for (i=0;i<FILE_SIZE;i++) {
110                 if (state->loadfile->out.data[i] != 1+(state->fnumber % 255)) {
111                         printf("Bad data in file %u (got %u expected %u)\n", 
112                                state->fnumber, 
113                                state->loadfile->out.data[i],
114                                1+(state->fnumber % 255));
115                         test_failed++;
116                         return;
117                 }
118         }
119         
120         talloc_steal(state->loadfile, state->loadfile->out.data);
121
122         state->count++;
123         talloc_free(state->loadfile);
124         state->loadfile = NULL;
125
126         if (!test_finished) {
127                 test_offline(state);
128         }
129 }
130
131
132 /*
133   called when a savefile completes
134  */
135 static void savefile_callback(struct composite_context *ctx) 
136 {
137         struct offline_state *state = ctx->async.private_data;
138         NTSTATUS status;
139
140         status = smb_composite_savefile_recv(ctx);
141         if (!NT_STATUS_IS_OK(status)) {
142                 printf("Failed to save file '%s' - %s\n", 
143                        state->savefile->in.fname, nt_errstr(status));
144                 test_failed++;
145         }
146
147         state->count++;
148         talloc_free(state->savefile);
149         state->savefile = NULL;
150
151         if (!test_finished) {
152                 test_offline(state);
153         }
154 }
155
156
157 /*
158   called when a setoffline completes
159  */
160 static void setoffline_callback(struct smbcli_request *req) 
161 {
162         struct offline_state *state = req->async.private;
163         NTSTATUS status;
164
165         status = smbcli_request_simple_recv(req);
166         if (!NT_STATUS_IS_OK(status)) {
167                 printf("Failed to set offline file '%s' - %s\n", 
168                        state->fname, nt_errstr(status));
169                 test_failed++;
170         }
171
172         state->req = NULL;
173         state->count++;
174
175         if (!test_finished) {
176                 test_offline(state);
177         }
178 }
179
180
181 /*
182   called when a getoffline completes
183  */
184 static void getoffline_callback(struct smbcli_request *req) 
185 {
186         struct offline_state *state = req->async.private;
187         NTSTATUS status;
188         union smb_fileinfo io;
189
190         io.getattr.level = RAW_FILEINFO_GETATTR;
191         
192         status = smb_raw_pathinfo_recv(req, state->mem_ctx, &io);
193         if (!NT_STATUS_IS_OK(status)) {
194                 printf("Failed to get offline file '%s' - %s\n", 
195                        state->fname, nt_errstr(status));
196                 test_failed++;
197         }
198
199         if (io.getattr.out.attrib & FILE_ATTRIBUTE_OFFLINE) {
200                 state->offline_count++;
201         } else {
202                 state->online_count++;
203         }
204
205         state->req = NULL;
206         state->count++;
207
208         if (!test_finished) {
209                 test_offline(state);
210         }
211 }
212
213
214 /*
215   send the next offline file fetch request
216 */
217 static void test_offline(struct offline_state *state)
218 {
219         struct composite_context *ctx;
220         double lat;
221
222         lat = timeval_elapsed(&state->tv_start);
223         if (latencies[state->op] < lat) {
224                 latencies[state->op] = lat;
225         }
226
227         state->op = (enum offline_op) (random() % OP_ENDOFLIST);
228         
229         state->fnumber = random() % torture_numops;
230         talloc_free(state->fname);
231         state->fname = filename(state->mem_ctx, state->fnumber);
232
233         state->tv_start = timeval_current();
234
235         switch (state->op) {
236         case OP_LOADFILE:
237                 state->loadfile = talloc_zero(state->mem_ctx, struct smb_composite_loadfile);
238                 state->loadfile->in.fname = state->fname;
239         
240                 ctx = smb_composite_loadfile_send(state->tree, state->loadfile);
241                 if (ctx == NULL) {
242                         printf("Failed to setup loadfile for %s\n", state->fname);
243                         test_failed = true;
244                 }
245
246                 talloc_steal(state->loadfile, ctx);
247
248                 ctx->async.fn = loadfile_callback;
249                 ctx->async.private_data = state;
250                 break;
251
252         case OP_SAVEFILE:
253                 state->savefile = talloc_zero(state->mem_ctx, struct smb_composite_savefile);
254
255                 state->savefile->in.fname = state->fname;
256                 state->savefile->in.data  = talloc_size(state->savefile, FILE_SIZE);
257                 state->savefile->in.size  = FILE_SIZE;
258                 memset(state->savefile->in.data, 1+(state->fnumber%255), FILE_SIZE);
259         
260                 ctx = smb_composite_savefile_send(state->tree, state->savefile);
261                 if (ctx == NULL) {
262                         printf("Failed to setup savefile for %s\n", state->fname);
263                         test_failed = true;
264                 }
265
266                 talloc_steal(state->savefile, ctx);
267
268                 ctx->async.fn = savefile_callback;
269                 ctx->async.private_data = state;
270                 break;
271
272         case OP_SETOFFLINE: {
273                 union smb_setfileinfo io;
274                 ZERO_STRUCT(io);
275                 io.setattr.level = RAW_SFILEINFO_SETATTR;
276                 io.setattr.in.attrib = FILE_ATTRIBUTE_OFFLINE;
277                 io.setattr.in.file.path = state->fname;
278                 /* make the file 1 hour old, to get past mininum age restrictions 
279                    for HSM systems */
280                 io.setattr.in.write_time = time(NULL) - 60*60;
281
282                 state->req = smb_raw_setpathinfo_send(state->tree, &io);
283                 if (state->req == NULL) {
284                         printf("Failed to setup setoffline for %s\n", state->fname);
285                         test_failed = true;
286                 }
287                 
288                 state->req->async.fn = setoffline_callback;
289                 state->req->async.private = state;
290                 break;
291         }
292
293         case OP_GETOFFLINE: {
294                 union smb_fileinfo io;
295                 ZERO_STRUCT(io);
296                 io.getattr.level = RAW_FILEINFO_GETATTR;
297                 io.getattr.in.file.path = state->fname;
298
299                 state->req = smb_raw_pathinfo_send(state->tree, &io);
300                 if (state->req == NULL) {
301                         printf("Failed to setup getoffline for %s\n", state->fname);
302                         test_failed = true;
303                 }
304                 
305                 state->req->async.fn = getoffline_callback;
306                 state->req->async.private = state;
307                 break;
308         }
309
310         default:
311                 printf("bad operation??\n");
312                 break;
313         }
314 }
315
316
317
318
319 static void echo_completion(struct smbcli_request *req)
320 {
321         struct offline_state *state = (struct offline_state *)req->async.private;
322         NTSTATUS status = smbcli_request_simple_recv(req);
323         if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
324             NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT)) {
325                 talloc_free(state->tree);
326                 state->tree = NULL;
327                 num_connected--;        
328                 DEBUG(0,("lost connection\n"));
329                 test_failed++;
330         }
331 }
332
333 static void report_rate(struct tevent_context *ev, struct tevent_timer *te, 
334                         struct timeval t, void *private_data)
335 {
336         struct offline_state *state = talloc_get_type(private_data, 
337                                                         struct offline_state);
338         int i;
339         uint32_t total=0, total_offline=0, total_online=0;
340         for (i=0;i<numstates;i++) {
341                 total += state[i].count - state[i].lastcount;
342                 if (timeval_elapsed(&state[i].tv_start) > latencies[state[i].op]) {
343                         latencies[state[i].op] = timeval_elapsed(&state[i].tv_start);
344                 }
345                 state[i].lastcount = state[i].count;            
346                 total_online += state[i].online_count;
347                 total_offline += state[i].offline_count;
348         }
349         printf("ops/s=%4u  offline=%5u online=%4u  set_lat=%.1f/%.1f get_lat=%.1f/%.1f save_lat=%.1f/%.1f load_lat=%.1f/%.1f\n",
350                total, total_offline, total_online,
351                latencies[OP_SETOFFLINE],
352                worst_latencies[OP_SETOFFLINE],
353                latencies[OP_GETOFFLINE],
354                worst_latencies[OP_GETOFFLINE],
355                latencies[OP_SAVEFILE],
356                worst_latencies[OP_SAVEFILE],
357                latencies[OP_LOADFILE],
358                worst_latencies[OP_LOADFILE]);
359         fflush(stdout);
360         event_add_timed(ev, state, timeval_current_ofs(1, 0), report_rate, state);
361
362         for (i=0;i<OP_ENDOFLIST;i++) {
363                 if (latencies[i] > worst_latencies[i]) {
364                         worst_latencies[i] = latencies[i];
365                 }
366                 latencies[i] = 0;
367         }
368
369         /* send an echo on each interface to ensure it stays alive - this helps
370            with IP takeover */
371         for (i=0;i<numstates;i++) {
372                 struct smb_echo p;
373                 struct smbcli_request *req;
374
375                 if (!state[i].tree) {
376                         continue;
377                 }
378
379                 p.in.repeat_count = 1;
380                 p.in.size = 0;
381                 p.in.data = NULL;
382                 req = smb_raw_echo_send(state[i].tree->session->transport, &p);
383                 req->async.private = &state[i];
384                 req->async.fn      = echo_completion;
385         }
386 }
387
388 /* 
389    test offline file handling
390 */
391 bool torture_test_offline(struct torture_context *torture)
392 {
393         bool ret = true;
394         TALLOC_CTX *mem_ctx = talloc_new(torture);
395         int i;
396         int timelimit = torture_setting_int(torture, "timelimit", 10);
397         struct timeval tv;
398         struct offline_state *state;
399         struct smbcli_state *cli;
400         bool progress;
401         progress = torture_setting_bool(torture, "progress", true);
402
403         nconnections = torture_setting_int(torture, "nprocs", 4);
404         numstates = nconnections * torture_entries;
405
406         state = talloc_zero_array(mem_ctx, struct offline_state, numstates);
407
408         printf("Opening %d connections with %d simultaneous operations and %u files\n", nconnections, numstates, torture_numops);
409         for (i=0;i<nconnections;i++) {
410                 state[i].tctx = torture;
411                 state[i].mem_ctx = talloc_new(state);
412                 state[i].ev = torture->ev;
413                 if (!torture_open_connection_ev(&cli, i, torture, torture->ev)) {
414                         return false;
415                 }
416                 state[i].tree = cli->tree;
417                 state[i].client = i;
418                 /* allow more time for offline files */
419                 state[i].tree->session->transport->options.request_timeout = 200;
420         }
421
422         /* the others are repeats on the earlier connections */
423         for (i=nconnections;i<numstates;i++) {
424                 state[i].tctx = torture;
425                 state[i].mem_ctx = talloc_new(state);
426                 state[i].ev = torture->ev;
427                 state[i].tree = state[i % nconnections].tree;
428                 state[i].client = i;
429         }
430
431         num_connected = i;
432
433         if (!torture_setup_dir(cli, BASEDIR)) {
434                 goto failed;
435         }
436
437         /* pre-create files */
438         printf("Pre-creating %u files ....\n", torture_numops);
439         for (i=0;i<torture_numops;i++) {
440                 int fnum;
441                 char *fname = filename(mem_ctx, i);
442                 char buf[FILE_SIZE];
443                 NTSTATUS status;
444
445                 memset(buf, 1+(i % 255), sizeof(buf));
446
447                 fnum = smbcli_open(state[0].tree, fname, O_RDWR|O_CREAT, DENY_NONE);
448                 if (fnum == -1) {
449                         printf("Failed to open %s on connection %d\n", fname, i);
450                         goto failed;
451                 }
452
453                 if (smbcli_write(state[0].tree, fnum, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
454                         printf("Failed to write file of size %u\n", FILE_SIZE);
455                         goto failed;
456                 }
457
458                 status = smbcli_close(state[0].tree, fnum);
459                 if (!NT_STATUS_IS_OK(status)) {
460                         printf("Close failed - %s\n", nt_errstr(status));
461                         goto failed;
462                 }
463
464                 talloc_free(fname);
465         }
466
467         /* start the async ops */
468         for (i=0;i<numstates;i++) {
469                 state[i].tv_start = timeval_current();
470                 test_offline(&state[i]);
471         }
472
473         tv = timeval_current(); 
474
475         if (progress) {
476                 event_add_timed(torture->ev, state, timeval_current_ofs(1, 0), report_rate, state);
477         }
478
479         printf("Running for %d seconds\n", timelimit);
480         while (timeval_elapsed(&tv) < timelimit) {
481                 event_loop_once(torture->ev);
482
483                 if (test_failed) {
484                         DEBUG(0,("test failed\n"));
485                         goto failed;
486                 }
487         }
488
489         printf("\nWaiting for completion\n");
490         test_finished = true;
491         for (i=0;i<numstates;i++) {
492                 while (state[i].loadfile || 
493                        state[i].savefile ||
494                        state[i].req) {
495                         event_loop_once(torture->ev);
496                 }
497         }       
498
499         printf("worst latencies: set_lat=%.1f get_lat=%.1f save_lat=%.1f load_lat=%.1f\n",
500                worst_latencies[OP_SETOFFLINE],
501                worst_latencies[OP_GETOFFLINE],
502                worst_latencies[OP_SAVEFILE],
503                worst_latencies[OP_LOADFILE]);
504
505         smbcli_deltree(state[0].tree, BASEDIR);
506         talloc_free(mem_ctx);
507         printf("\n");
508         return ret;
509
510 failed:
511         talloc_free(mem_ctx);
512         return false;
513 }