r21276: Actually fire off multiple parallel read/write requests.
[sfrench/samba-autobuild/.git] / source4 / torture / basic / misc.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB torture tester
4    Copyright (C) Andrew Tridgell 1997-2003
5    Copyright (C) Jelmer Vernooij 2006
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 #include "libcli/raw/libcliraw.h"
24 #include "system/time.h"
25 #include "system/wait.h"
26 #include "system/filesys.h"
27 #include "libcli/raw/ioctl.h"
28 #include "libcli/libcli.h"
29 #include "lib/events/events.h"
30 #include "libcli/resolve/resolve.h"
31 #include "auth/credentials/credentials.h"
32 #include "librpc/gen_ndr/ndr_nbt.h"
33 #include "torture/torture.h"
34 #include "torture/util.h"
35 #include "libcli/smb_composite/smb_composite.h"
36 #include "libcli/composite/composite.h"
37
38 extern struct cli_credentials *cmdline_credentials;
39         
40 static BOOL wait_lock(struct smbcli_state *c, int fnum, uint32_t offset, uint32_t len)
41 {
42         while (NT_STATUS_IS_ERR(smbcli_lock(c->tree, fnum, offset, len, -1, WRITE_LOCK))) {
43                 if (!check_error(__location__, c, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return False;
44         }
45         return True;
46 }
47
48
49 static BOOL rw_torture(struct torture_context *tctx, struct smbcli_state *c)
50 {
51         const char *lockfname = "\\torture.lck";
52         char *fname;
53         int fnum;
54         int fnum2;
55         pid_t pid2, pid = getpid();
56         int i, j;
57         uint8_t buf[1024];
58         BOOL correct = True;
59
60         fnum2 = smbcli_open(c->tree, lockfname, O_RDWR | O_CREAT | O_EXCL, 
61                          DENY_NONE);
62         if (fnum2 == -1)
63                 fnum2 = smbcli_open(c->tree, lockfname, O_RDWR, DENY_NONE);
64         if (fnum2 == -1) {
65                 torture_comment(tctx, "open of %s failed (%s)\n", lockfname, smbcli_errstr(c->tree));
66                 return False;
67         }
68
69
70         for (i=0;i<torture_numops;i++) {
71                 uint_t n = (uint_t)random()%10;
72                 if (i % 10 == 0) {
73                         torture_comment(tctx, "%d\r", i); fflush(stdout);
74                 }
75                 asprintf(&fname, "\\torture.%u", n);
76
77                 if (!wait_lock(c, fnum2, n*sizeof(int), sizeof(int))) {
78                         return False;
79                 }
80
81                 fnum = smbcli_open(c->tree, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_ALL);
82                 if (fnum == -1) {
83                         torture_comment(tctx, "open failed (%s)\n", smbcli_errstr(c->tree));
84                         correct = False;
85                         break;
86                 }
87
88                 if (smbcli_write(c->tree, fnum, 0, &pid, 0, sizeof(pid)) != sizeof(pid)) {
89                         torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c->tree));
90                         correct = False;
91                 }
92
93                 for (j=0;j<50;j++) {
94                         if (smbcli_write(c->tree, fnum, 0, buf, 
95                                       sizeof(pid)+(j*sizeof(buf)), 
96                                       sizeof(buf)) != sizeof(buf)) {
97                                 torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c->tree));
98                                 correct = False;
99                         }
100                 }
101
102                 pid2 = 0;
103
104                 if (smbcli_read(c->tree, fnum, &pid2, 0, sizeof(pid)) != sizeof(pid)) {
105                         torture_comment(tctx, "read failed (%s)\n", smbcli_errstr(c->tree));
106                         correct = False;
107                 }
108
109                 if (pid2 != pid) {
110                         torture_comment(tctx, "data corruption!\n");
111                         correct = False;
112                 }
113
114                 if (NT_STATUS_IS_ERR(smbcli_close(c->tree, fnum))) {
115                         torture_comment(tctx, "close failed (%s)\n", smbcli_errstr(c->tree));
116                         correct = False;
117                 }
118
119                 if (NT_STATUS_IS_ERR(smbcli_unlink(c->tree, fname))) {
120                         torture_comment(tctx, "unlink failed (%s)\n", smbcli_errstr(c->tree));
121                         correct = False;
122                 }
123
124                 if (NT_STATUS_IS_ERR(smbcli_unlock(c->tree, fnum2, n*sizeof(int), sizeof(int)))) {
125                         torture_comment(tctx, "unlock failed (%s)\n", smbcli_errstr(c->tree));
126                         correct = False;
127                 }
128                 free(fname);
129         }
130
131         smbcli_close(c->tree, fnum2);
132         smbcli_unlink(c->tree, lockfname);
133
134         torture_comment(tctx, "%d\n", i);
135
136         return correct;
137 }
138
139 BOOL run_torture(struct torture_context *tctx, struct smbcli_state *cli, int dummy)
140 {
141         return rw_torture(tctx, cli);
142 }
143
144
145 /*
146   see how many RPC pipes we can open at once
147 */
148 BOOL run_pipe_number(struct torture_context *tctx, 
149                                          struct smbcli_state *cli1)
150 {
151         const char *pipe_name = "\\WKSSVC";
152         int fnum;
153         int num_pipes = 0;
154
155         while(1) {
156                 fnum = smbcli_nt_create_full(cli1->tree, pipe_name, 0, SEC_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
157                                    NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, NTCREATEX_DISP_OPEN_IF, 0, 0);
158
159                 if (fnum == -1) {
160                         torture_comment(tctx, "Open of pipe %s failed with error (%s)\n", pipe_name, smbcli_errstr(cli1->tree));
161                         break;
162                 }
163                 num_pipes++;
164                 torture_comment(tctx, "%d\r", num_pipes);
165                 fflush(stdout);
166         }
167
168         torture_comment(tctx, "pipe_number test - we can open %d %s pipes.\n", num_pipes, pipe_name );
169         return True;
170 }
171
172
173
174
175 /*
176   open N connections to the server and just hold them open
177   used for testing performance when there are N idle users
178   already connected
179  */
180 BOOL torture_holdcon(struct torture_context *tctx)
181 {
182         int i;
183         struct smbcli_state **cli;
184         int num_dead = 0;
185
186         torture_comment(tctx, "Opening %d connections\n", torture_numops);
187         
188         cli = malloc_array_p(struct smbcli_state *, torture_numops);
189
190         for (i=0;i<torture_numops;i++) {
191                 if (!torture_open_connection(&cli[i], i)) {
192                         return False;
193                 }
194                 torture_comment(tctx, "opened %d connections\r", i);
195                 fflush(stdout);
196         }
197
198         torture_comment(tctx, "\nStarting pings\n");
199
200         while (1) {
201                 for (i=0;i<torture_numops;i++) {
202                         NTSTATUS status;
203                         if (cli[i]) {
204                                 status = smbcli_chkpath(cli[i]->tree, "\\");
205                                 if (!NT_STATUS_IS_OK(status)) {
206                                         torture_comment(tctx, "Connection %d is dead\n", i);
207                                         cli[i] = NULL;
208                                         num_dead++;
209                                 }
210                                 usleep(100);
211                         }
212                 }
213
214                 if (num_dead == torture_numops) {
215                         torture_comment(tctx, "All connections dead - finishing\n");
216                         break;
217                 }
218
219                 torture_comment(tctx, ".");
220                 fflush(stdout);
221         }
222
223         return True;
224 }
225
226 /*
227 test how many open files this server supports on the one socket
228 */
229 BOOL run_maxfidtest(struct torture_context *tctx, struct smbcli_state *cli, int dummy)
230 {
231 #define MAXFID_TEMPLATE "\\maxfid\\fid%d\\maxfid.%d.%d"
232         char *fname;
233         int fnums[0x11000], i;
234         int retries=4, maxfid;
235         BOOL correct = True;
236
237         if (retries <= 0) {
238                 torture_comment(tctx, "failed to connect\n");
239                 return False;
240         }
241
242         if (smbcli_deltree(cli->tree, "\\maxfid") == -1) {
243                 torture_comment(tctx, "Failed to deltree \\maxfid - %s\n",
244                        smbcli_errstr(cli->tree));
245                 return False;
246         }
247         if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, "\\maxfid"))) {
248                 torture_comment(tctx, "Failed to mkdir \\maxfid, error=%s\n", 
249                        smbcli_errstr(cli->tree));
250                 return False;
251         }
252
253         torture_comment(tctx, "Testing maximum number of open files\n");
254
255         for (i=0; i<0x11000; i++) {
256                 if (i % 1000 == 0) {
257                         asprintf(&fname, "\\maxfid\\fid%d", i/1000);
258                         if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, fname))) {
259                                 torture_comment(tctx, "Failed to mkdir %s, error=%s\n", 
260                                        fname, smbcli_errstr(cli->tree));
261                                 return False;
262                         }
263                         free(fname);
264                 }
265                 asprintf(&fname, MAXFID_TEMPLATE, i/1000, i,(int)getpid());
266                 if ((fnums[i] = smbcli_open(cli->tree, fname, 
267                                         O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) ==
268                     -1) {
269                         torture_comment(tctx, "open of %s failed (%s)\n", 
270                                fname, smbcli_errstr(cli->tree));
271                         torture_comment(tctx, "maximum fnum is %d\n", i);
272                         break;
273                 }
274                 free(fname);
275                 torture_comment(tctx, "%6d\r", i);
276         }
277         torture_comment(tctx, "%6d\n", i);
278         i--;
279
280         maxfid = i;
281
282         torture_comment(tctx, "cleaning up\n");
283         for (i=0;i<maxfid/2;i++) {
284                 asprintf(&fname, MAXFID_TEMPLATE, i/1000, i,(int)getpid());
285                 if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnums[i]))) {
286                         torture_comment(tctx, "Close of fnum %d failed - %s\n", fnums[i], smbcli_errstr(cli->tree));
287                 }
288                 if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, fname))) {
289                         torture_comment(tctx, "unlink of %s failed (%s)\n", 
290                                fname, smbcli_errstr(cli->tree));
291                         correct = False;
292                 }
293                 free(fname);
294
295                 asprintf(&fname, MAXFID_TEMPLATE, (maxfid-i)/1000, maxfid-i,(int)getpid());
296                 if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnums[maxfid-i]))) {
297                         torture_comment(tctx, "Close of fnum %d failed - %s\n", fnums[maxfid-i], smbcli_errstr(cli->tree));
298                 }
299                 if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, fname))) {
300                         torture_comment(tctx, "unlink of %s failed (%s)\n", 
301                                fname, smbcli_errstr(cli->tree));
302                         correct = False;
303                 }
304                 free(fname);
305
306                 torture_comment(tctx, "%6d %6d\r", i, maxfid-i);
307         }
308         torture_comment(tctx, "%6d\n", 0);
309
310         if (smbcli_deltree(cli->tree, "\\maxfid") == -1) {
311                 torture_comment(tctx, "Failed to deltree \\maxfid - %s\n",
312                        smbcli_errstr(cli->tree));
313                 return False;
314         }
315
316         torture_comment(tctx, "maxfid test finished\n");
317         if (!torture_close_connection(cli)) {
318                 correct = False;
319         }
320         return correct;
321 #undef MAXFID_TEMPLATE
322 }
323
324
325
326 /*
327   sees what IOCTLs are supported
328  */
329 BOOL torture_ioctl_test(struct torture_context *tctx, 
330                                                 struct smbcli_state *cli)
331 {
332         uint16_t device, function;
333         int fnum;
334         const char *fname = "\\ioctl.dat";
335         NTSTATUS status;
336         union smb_ioctl parms;
337         TALLOC_CTX *mem_ctx;
338
339         mem_ctx = talloc_named_const(tctx, 0, "ioctl_test");
340
341         smbcli_unlink(cli->tree, fname);
342
343         fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
344         if (fnum == -1) {
345                 torture_comment(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
346                 return False;
347         }
348
349         parms.ioctl.level = RAW_IOCTL_IOCTL;
350         parms.ioctl.in.file.fnum = fnum;
351         parms.ioctl.in.request = IOCTL_QUERY_JOB_INFO;
352         status = smb_raw_ioctl(cli->tree, mem_ctx, &parms);
353         torture_comment(tctx, "ioctl job info: %s\n", smbcli_errstr(cli->tree));
354
355         for (device=0;device<0x100;device++) {
356                 torture_comment(tctx, "testing device=0x%x\n", device);
357                 for (function=0;function<0x100;function++) {
358                         parms.ioctl.in.request = (device << 16) | function;
359                         status = smb_raw_ioctl(cli->tree, mem_ctx, &parms);
360
361                         if (NT_STATUS_IS_OK(status)) {
362                                 torture_comment(tctx, "ioctl device=0x%x function=0x%x OK : %d bytes\n", 
363                                         device, function, (int)parms.ioctl.out.blob.length);
364                         }
365                 }
366         }
367
368         return True;
369 }
370
371 static void benchrw_callback(struct smbcli_request *req);
372 enum benchrw_stage {
373         START,
374         OPEN_CONNECTION,
375         CLEANUP_TESTDIR,
376         MK_TESTDIR,
377         OPEN_FILE,
378         INITIAL_WRITE,
379         READ_WRITE_DATA,
380         MAX_OPS_REACHED,
381         ERROR,
382         CLOSE_FILE,
383         CLEANUP,
384         FINISHED
385 };
386
387 struct benchrw_state {
388         struct torture_context *tctx;
389         char *dname;
390         char *fname;
391         uint16_t fnum;
392         int nr;
393         struct smbcli_tree      *cli;           
394         uint8_t *buffer;
395         int writecnt;
396         int readcnt;
397         int completed;
398         int num_parallel_requests;
399         void *req_params;
400         enum benchrw_stage mode;
401         struct params{
402                 struct unclist{
403                         const char *host;
404                         const char *share;
405                 } **unc;
406                 const char *workgroup;
407                 int retry;
408                 unsigned int writeblocks;
409                 unsigned int blocksize;
410                 unsigned int writeratio;
411                 int num_parallel_requests;
412         } *lp_params;
413 };
414
415 /* 
416         init params using lp_parm_xxx 
417         return number of unclist entries
418 */
419 static int init_benchrw_params(struct torture_context *tctx,
420                                struct params *lpar)
421 {
422         char **unc_list = NULL;
423         int num_unc_names = 0, conn_index=0, empty_lines=0;
424         const char *p;
425         lpar->retry = torture_setting_int(tctx, "retry",3);
426         lpar->blocksize = torture_setting_int(tctx, "blocksize",65535);
427         lpar->writeblocks = torture_setting_int(tctx, "writeblocks",15);
428         lpar->writeratio = torture_setting_int(tctx, "writeratio",5);
429         lpar->num_parallel_requests = torture_setting_int(
430                 tctx, "parallel_requests", 5);
431         lpar->workgroup = lp_workgroup();
432         
433         p = torture_setting_string(tctx, "unclist", NULL);
434         if (p) {
435                 char *h, *s;
436                 unc_list = file_lines_load(p, &num_unc_names, NULL);
437                 if (!unc_list || num_unc_names <= 0) {
438                         torture_comment(tctx, "Failed to load unc names list "
439                                         "from '%s'\n", p);
440                         exit(1);
441                 }
442                 
443                 lpar->unc = talloc_array(tctx, struct unclist *,
444                                          (num_unc_names-empty_lines));
445                 for(conn_index = 0; conn_index < num_unc_names; conn_index++) {
446                         /* ignore empty lines */
447                         if(strlen(unc_list[conn_index % num_unc_names])==0){
448                                 empty_lines++;
449                                 continue;
450                         }
451                         if (!smbcli_parse_unc(
452                                     unc_list[conn_index % num_unc_names],
453                                     NULL, &h, &s)) {
454                                 torture_comment(
455                                         tctx, "Failed to parse UNC "
456                                         "name %s\n",
457                                         unc_list[conn_index % num_unc_names]);
458                                 exit(1);
459                         }
460                         lpar->unc[conn_index-empty_lines] =
461                                 talloc(tctx, struct unclist);
462                         lpar->unc[conn_index-empty_lines]->host = h;
463                         lpar->unc[conn_index-empty_lines]->share = s;   
464                 }
465                 return num_unc_names-empty_lines;
466         }else{
467                 lpar->unc = talloc_array(tctx, struct unclist *, 1);
468                 lpar->unc[0] = talloc(tctx,struct unclist);
469                 lpar->unc[0]->host  = torture_setting_string(tctx, "host",
470                                                              NULL);
471                 lpar->unc[0]->share = torture_setting_string(tctx, "share",
472                                                              NULL);
473                 return 1;
474         }
475 }
476
477 /*
478  Called when the reads & writes are finished. closes the file.
479 */
480 static NTSTATUS benchrw_close(struct torture_context *tctx,
481                               struct smbcli_request *req,
482                               struct benchrw_state *state)
483 {
484         union smb_close close_parms;
485         
486         NT_STATUS_NOT_OK_RETURN(req->status);
487         
488         torture_comment(tctx, "Close file %d (%d)\n",state->nr,state->fnum);
489         close_parms.close.level = RAW_CLOSE_CLOSE;
490         close_parms.close.in.file.fnum = state->fnum ;
491         close_parms.close.in.write_time = 0;
492         state->mode=CLOSE_FILE;
493         
494         req = smb_raw_close_send(state->cli, &close_parms);
495         NT_STATUS_HAVE_NO_MEMORY(req);
496         /*register the callback function!*/
497         req->async.fn = benchrw_callback;
498         req->async.private = state;
499         
500         return NT_STATUS_OK;
501 }
502
503 static NTSTATUS benchrw_readwrite(struct torture_context *tctx,
504                                   struct benchrw_state *state);
505 static void benchrw_callback(struct smbcli_request *req);
506
507 static void benchrw_rw_callback(struct smbcli_request *req)
508 {
509         struct benchrw_state *state = req->async.private;
510         struct torture_context *tctx = state->tctx;
511
512         if (!NT_STATUS_IS_OK(req->status)) {
513                 state->mode = ERROR;
514                 return;
515         }
516
517         state->completed++;
518         state->num_parallel_requests--;
519
520         if ((state->completed >= torture_numops)
521             && (state->num_parallel_requests == 0)) {
522                 benchrw_callback(req);
523                 talloc_free(req);
524                 return;
525         }
526
527         talloc_free(req);
528
529         if (state->completed + state->num_parallel_requests
530             < torture_numops) {
531                 benchrw_readwrite(tctx, state);
532         }
533 }
534
535 /*
536  Called when the initial write is completed is done. write or read a file.
537 */
538 static NTSTATUS benchrw_readwrite(struct torture_context *tctx,
539                                   struct benchrw_state *state)
540 {
541         struct smbcli_request *req;
542         union smb_read  rd;
543         union smb_write wr;
544         
545         /* randomize between writes and reads*/
546         if ( random() % state->lp_params->writeratio == 0) {
547                 torture_comment(tctx, "Callback WRITE file:%d (%d/%d)\n",
548                                 state->nr,state->completed,torture_numops);
549                 wr.generic.level = RAW_WRITE_WRITEX  ;
550                 wr.writex.in.file.fnum  = state->fnum ;
551                 wr.writex.in.offset     = 0;
552                 wr.writex.in.wmode      = 0             ;
553                 wr.writex.in.remaining  = 0;
554                 wr.writex.in.count      = state->lp_params->blocksize;
555                 wr.writex.in.data       = state->buffer;
556                 state->readcnt=0;
557                 req = smb_raw_write_send(state->cli,&wr);
558         }
559         else {
560                 torture_comment(tctx,
561                                 "Callback READ file:%d (%d/%d) Offset:%d\n",
562                                 state->nr,state->completed,torture_numops,
563                                 (state->readcnt*state->lp_params->blocksize));
564                 rd.generic.level = RAW_READ_READ    ;
565                 rd.read.in.file.fnum    = state->fnum   ;
566                 rd.read.in.offset       = state->readcnt * 
567                                         state->lp_params->blocksize;
568                 rd.read.in.count        = state->lp_params->blocksize;
569                 rd.read.in.remaining    = 0     ;
570                 rd.read.out.data        = state->buffer;
571                 if(state->readcnt < state->lp_params->writeblocks){
572                         state->readcnt++;       
573                 }else{
574                         /*start reading from beginn of file*/
575                         state->readcnt=0;
576                 }
577                 req = smb_raw_read_send(state->cli,&rd);
578         }
579         state->num_parallel_requests += 1;
580         NT_STATUS_HAVE_NO_MEMORY(req);
581         /*register the callback function!*/
582         req->async.fn = benchrw_rw_callback;
583         req->async.private = state;
584         
585         return NT_STATUS_OK;
586 }
587
588 /*
589  Called when the open is done. writes to the file.
590 */
591 static NTSTATUS benchrw_open(struct torture_context *tctx,
592                              struct smbcli_request *req,
593                              struct benchrw_state *state)
594 {
595         union smb_write wr;
596         if(state->mode == OPEN_FILE){
597                 NTSTATUS status;
598                 status = smb_raw_open_recv(req,tctx,(
599                                         union smb_open*)state->req_params);
600                 NT_STATUS_NOT_OK_RETURN(status);
601         
602                 state->fnum = ((union smb_open*)state->req_params)
603                                                 ->openx.out.file.fnum;
604                 torture_comment(tctx, "File opened (%d)\n",state->fnum);
605                 state->mode=INITIAL_WRITE;
606         }
607                 
608         torture_comment(tctx, "Write initial test file:%d (%d/%d)\n",state->nr,
609                 (state->writecnt+1)*state->lp_params->blocksize,
610                 (state->lp_params->writeblocks*state->lp_params->blocksize));
611         wr.generic.level = RAW_WRITE_WRITEX  ;
612         wr.writex.in.file.fnum  = state->fnum ;
613         wr.writex.in.offset     = state->writecnt * 
614                                         state->lp_params->blocksize;
615         wr.writex.in.wmode      = 0             ;
616         wr.writex.in.remaining  = (state->lp_params->writeblocks *
617                                                 state->lp_params->blocksize)-
618                                                 ((state->writecnt+1)*state->
619                                                 lp_params->blocksize);
620         wr.writex.in.count      = state->lp_params->blocksize;
621         wr.writex.in.data       = state->buffer;
622         state->writecnt++;
623         if(state->writecnt == state->lp_params->writeblocks){
624                 state->mode=READ_WRITE_DATA;
625         }
626         req = smb_raw_write_send(state->cli,&wr);
627         NT_STATUS_HAVE_NO_MEMORY(req);
628         
629         /*register the callback function!*/
630         req->async.fn = benchrw_callback;
631         req->async.private = state;
632         return NT_STATUS_OK;
633
634
635 /*
636  Called when the mkdir is done. Opens a file.
637 */
638 static NTSTATUS benchrw_mkdir(struct torture_context *tctx,
639                               struct smbcli_request *req,
640                               struct benchrw_state *state)
641 {
642         union smb_open *open_parms;     
643         uint8_t *writedata;     
644                 
645         NT_STATUS_NOT_OK_RETURN(req->status);
646         
647         /* open/create the files */
648         torture_comment(tctx, "Open File %d/%d\n",state->nr+1,
649                         lp_parm_int(-1, "torture", "nprocs", 4));
650         open_parms=talloc_zero(tctx, union smb_open);
651         NT_STATUS_HAVE_NO_MEMORY(open_parms);
652         open_parms->openx.level = RAW_OPEN_OPENX;
653         open_parms->openx.in.flags = 0;
654         open_parms->openx.in.open_mode = OPENX_MODE_ACCESS_RDWR;
655         open_parms->openx.in.search_attrs = 
656                         FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
657         open_parms->openx.in.file_attrs = 0;
658         open_parms->openx.in.write_time = 0;
659         open_parms->openx.in.open_func = OPENX_OPEN_FUNC_CREATE;
660         open_parms->openx.in.size = 0;
661         open_parms->openx.in.timeout = 0;
662         open_parms->openx.in.fname = state->fname;
663                 
664         writedata = talloc_size(tctx,state->lp_params->blocksize);
665         NT_STATUS_HAVE_NO_MEMORY(writedata);
666         generate_random_buffer(writedata,state->lp_params->blocksize);
667         state->buffer=writedata;
668         state->writecnt=1;
669         state->readcnt=0;
670         state->req_params=open_parms;           
671         state->mode=OPEN_FILE;  
672                         
673         req = smb_raw_open_send(state->cli,open_parms);
674         NT_STATUS_HAVE_NO_MEMORY(req);
675         
676         /*register the callback function!*/
677         req->async.fn = benchrw_callback;
678         req->async.private = state;
679                 
680         return NT_STATUS_OK;
681 }
682
683 /*
684  handler for completion of a sub-request of the bench-rw test
685 */
686 static void benchrw_callback(struct smbcli_request *req)
687 {
688         struct benchrw_state *state = req->async.private;
689         struct torture_context *tctx = state->tctx;
690         
691         /*dont send new requests when torture_numops is reached*/
692         if ((state->mode == READ_WRITE_DATA)
693             && (state->completed >= torture_numops)) {
694                 state->mode=MAX_OPS_REACHED;
695         }
696         
697         switch (state->mode) {
698         
699         case MK_TESTDIR:
700                 if (!NT_STATUS_IS_OK(benchrw_mkdir(tctx, req,state))) {
701                         torture_comment(tctx, "Failed to create the test "
702                                         "directory - %s\n", 
703                                         nt_errstr(req->status));
704                         state->mode=ERROR;
705                         return;
706                 }
707                 break;  
708         case OPEN_FILE:
709         case INITIAL_WRITE:
710                 if (!NT_STATUS_IS_OK(benchrw_open(tctx, req,state))){
711                         torture_comment(tctx, "Failed to open/write the "
712                                         "file - %s\n", 
713                                         nt_errstr(req->status));
714                         state->mode=ERROR;
715                         return;
716                 }
717                 break;
718         case READ_WRITE_DATA:
719                 while (state->num_parallel_requests
720                        < state->lp_params->num_parallel_requests) {
721                         NTSTATUS status;
722                         status = benchrw_readwrite(tctx,state);
723                         if (!NT_STATUS_IS_OK(status)){
724                                 torture_comment(tctx, "Failed to read/write "
725                                                 "the file - %s\n", 
726                                                 nt_errstr(req->status));
727                                 state->mode=ERROR;
728                                 return;
729                         }
730                 }
731                 break;
732         case MAX_OPS_REACHED:
733                 if (!NT_STATUS_IS_OK(benchrw_close(tctx,req,state))){
734                         torture_comment(tctx, "Failed to read/write/close "
735                                         "the file - %s\n", 
736                                         nt_errstr(req->status));
737                         state->mode=ERROR;
738                         return;
739                 }
740                 break;
741         case CLOSE_FILE:
742                 torture_comment(tctx, "File %d closed\n",state->nr);
743                 if (!NT_STATUS_IS_OK(req->status)) {
744                         torture_comment(tctx, "Failed to close the "
745                                         "file - %s\n",
746                                         nt_errstr(req->status));
747                         state->mode=ERROR;
748                         return;
749                 }
750                 state->mode=CLEANUP;
751                 return; 
752         default:
753                 break;
754         }
755         
756 }
757
758 /* open connection async callback function*/
759 static void async_open_callback(struct composite_context *con)
760 {
761         struct benchrw_state *state = con->async.private_data;
762         struct torture_context *tctx = state->tctx;
763         int retry = state->lp_params->retry;
764                 
765         if (NT_STATUS_IS_OK(con->status)) {
766                 state->cli=((struct smb_composite_connect*)
767                                         state->req_params)->out.tree;
768                 state->mode=CLEANUP_TESTDIR;
769         }else{
770                 if(state->writecnt < retry){
771                         torture_comment(tctx, "Failed to open connection: "
772                                         "%d, Retry (%d/%d)\n",
773                                         state->nr,state->writecnt,retry);
774                         state->writecnt++;
775                         state->mode=START;
776                         usleep(1000);   
777                 }else{
778                         torture_comment(tctx, "Failed to open connection "
779                                         "(%d) - %s\n",
780                                         state->nr, nt_errstr(con->status));
781                         state->mode=ERROR;
782                 }
783                 return;
784         }       
785 }
786
787 /*
788  establishs a smbcli_tree from scratch (async)
789 */
790 static struct composite_context *torture_connect_async(
791                                 struct torture_context *tctx,
792                                 struct smb_composite_connect *smb,
793                                 TALLOC_CTX *mem_ctx,
794                                 struct event_context *ev,
795                                 const char *host,
796                                 const char *share,
797                                 const char *workgroup)
798 {
799         torture_comment(tctx, "Open Connection to %s/%s\n",host,share);
800         smb->in.dest_host=talloc_strdup(mem_ctx,host);
801         smb->in.service=talloc_strdup(mem_ctx,share);
802         smb->in.port=0;
803         smb->in.called_name = strupper_talloc(mem_ctx, host);
804         smb->in.service_type=NULL;
805         smb->in.credentials=cmdline_credentials;
806         smb->in.fallback_to_anonymous=False;
807         smb->in.workgroup=workgroup;
808         
809         return smb_composite_connect_send(smb,mem_ctx,ev);
810 }
811
812 BOOL run_benchrw(struct torture_context *tctx)
813 {
814         struct smb_composite_connect *smb_con;
815         const char *fname = "\\rwtest.dat";
816         struct smbcli_request *req;
817         struct benchrw_state **state;
818         int i , num_unc_names;
819         struct event_context    *ev     ;       
820         struct composite_context *req1;
821         struct params lpparams;
822         union smb_mkdir parms;
823         int finished = 0;
824         BOOL success=True;
825         int torture_nprocs = lp_parm_int(-1, "torture", "nprocs", 4);
826         
827         torture_comment(tctx, "Start BENCH-READWRITE num_ops=%d "
828                         "num_nprocs=%d\n",
829                         torture_numops, torture_nprocs);
830
831         /*init talloc context*/
832         ev = event_context_init(tctx);
833         state = talloc_array(tctx, struct benchrw_state *, torture_nprocs);
834
835         /* init params using lp_parm_xxx */
836         num_unc_names = init_benchrw_params(tctx,&lpparams);
837         
838         /* init private data structs*/
839         for(i = 0; i<torture_nprocs;i++){
840                 state[i]=talloc(tctx,struct benchrw_state);
841                 state[i]->tctx = tctx;
842                 state[i]->completed=0;
843                 state[i]->num_parallel_requests=0;
844                 state[i]->lp_params=&lpparams;
845                 state[i]->nr=i;
846                 state[i]->dname=talloc_asprintf(tctx,"benchrw%d",i);
847                 state[i]->fname=talloc_asprintf(tctx,"%s%s",
848                                                 state[i]->dname,fname); 
849                 state[i]->mode=START;
850                 state[i]->writecnt=0;
851         }
852         
853         torture_comment(tctx, "Starting async requests\n");     
854         while(finished != torture_nprocs){
855                 finished=0;
856                 for(i = 0; i<torture_nprocs;i++){
857                         switch (state[i]->mode){
858                         /*open multiple connections with the same userid */
859                         case START:
860                                 smb_con = talloc(
861                                         tctx,struct smb_composite_connect) ;
862                                 state[i]->req_params=smb_con; 
863                                 state[i]->mode=OPEN_CONNECTION;
864                                 req1 = torture_connect_async(
865                                         tctx, smb_con, tctx,ev,
866                                         lpparams.unc[i % num_unc_names]->host,
867                                         lpparams.unc[i % num_unc_names]->share,
868                                         lpparams.workgroup);
869                                 /* register callback fn + private data */
870                                 req1->async.fn = async_open_callback;
871                                 req1->async.private_data=state[i];
872                                 break;
873                         /*setup test dirs (sync)*/
874                         case CLEANUP_TESTDIR:
875                                 torture_comment(tctx, "Setup test dir %d\n",i);
876                                 smb_raw_exit(state[i]->cli->session);
877                                 if (smbcli_deltree(state[i]->cli, 
878                                                 state[i]->dname) == -1) {
879                                         torture_comment(
880                                                 tctx,
881                                                 "Unable to delete %s - %s\n", 
882                                                 state[i]->dname,
883                                                 smbcli_errstr(state[i]->cli));
884                                         state[i]->mode=ERROR;
885                                         break;
886                                 }
887                                 state[i]->mode=MK_TESTDIR;
888                                 parms.mkdir.level = RAW_MKDIR_MKDIR;
889                                 parms.mkdir.in.path = state[i]->dname;
890                                 req = smb_raw_mkdir_send(state[i]->cli,&parms);
891                                 /* register callback fn + private data */
892                                 req->async.fn = benchrw_callback;
893                                 req->async.private=state[i];
894                                 break;
895                         /* error occured , finish */
896                         case ERROR:
897                                 finished++;
898                                 success=False;
899                                 break;
900                         /* cleanup , close connection */
901                         case CLEANUP:
902                                 torture_comment(tctx, "Deleting test dir %s "
903                                                 "%d/%d\n",state[i]->dname,
904                                                 i+1,torture_nprocs);
905                                 smbcli_deltree(state[i]->cli,state[i]->dname);
906                                 if (NT_STATUS_IS_ERR(smb_tree_disconnect(
907                                                              state[i]->cli))) {
908                                         torture_comment(tctx, "ERROR: Tree "
909                                                         "disconnect failed");
910                                         state[i]->mode=ERROR;
911                                         break;
912                                 }
913                                 state[i]->mode=FINISHED;
914                         case FINISHED:
915                                 finished++;
916                                 break;
917                         default:
918                                 event_loop_once(ev);
919                         }
920                 }
921         }
922                                 
923         return success; 
924 }
925