declare dbf in one spot
[kai/samba.git] / source3 / torture / locktest2.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 2.0
4    byte range lock tester - with local filesystem support
5    Copyright (C) Andrew Tridgell 1999
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 #define NO_SYSLOG
23
24 #include "includes.h"
25
26 static fstring password;
27 static fstring username;
28 static int got_pass;
29 static int numops = 1000;
30 static BOOL showall;
31 static BOOL analyze;
32 static BOOL hide_unlock_fails;
33 static BOOL use_oplocks;
34
35 #define FILENAME "\\locktest.dat"
36 #define LOCKRANGE 100
37 #define LOCKBASE 0
38
39 /*
40 #define LOCKBASE (0x40000000 - 50)
41 */
42
43 #define READ_PCT 50
44 #define LOCK_PCT 25
45 #define UNLOCK_PCT 65
46 #define RANGE_MULTIPLE 1
47
48 #define NSERVERS 2
49 #define NCONNECTIONS 2
50 #define NUMFSTYPES 2
51 #define NFILES 2
52 #define LOCK_TIMEOUT 0
53
54 #define FSTYPE_SMB 0
55 #define FSTYPE_NFS 1
56
57 struct record {
58         char r1, r2;
59         char conn, f, fstype;
60         unsigned start, len;
61         char needed;
62 };
63
64 static struct record *recorded;
65
66 static int try_open(struct cli_state *c, char *nfs, int fstype, char *fname, int flags)
67 {
68         pstring path;
69
70         switch (fstype) {
71         case FSTYPE_SMB:
72                 return cli_open(c, fname, flags, DENY_NONE);
73
74         case FSTYPE_NFS:
75                 slprintf(path, sizeof(path), "%s%s", nfs, fname);
76                 pstring_sub(path,"\\", "/");
77                 return open(path, flags, 0666);
78         }
79
80         return -1;
81 }
82
83 static BOOL try_close(struct cli_state *c, int fstype, int fd)
84 {
85         switch (fstype) {
86         case FSTYPE_SMB:
87                 return cli_close(c, fd);
88
89         case FSTYPE_NFS:
90                 return close(fd) == 0;
91         }
92
93         return False;
94 }
95
96 static BOOL try_lock(struct cli_state *c, int fstype, 
97                      int fd, unsigned start, unsigned len,
98                      int op)
99 {
100         struct flock lock;
101
102         switch (fstype) {
103         case FSTYPE_SMB:
104                 return cli_lock(c, fd, start, len, LOCK_TIMEOUT, op);
105
106         case FSTYPE_NFS:
107                 lock.l_type = (op==READ_LOCK) ? F_RDLCK:F_WRLCK;
108                 lock.l_whence = SEEK_SET;
109                 lock.l_start = start;
110                 lock.l_len = len;
111                 lock.l_pid = getpid();
112                 return fcntl(fd,F_SETLK,&lock) == 0;
113         }
114
115         return False;
116 }
117
118 static BOOL try_unlock(struct cli_state *c, int fstype, 
119                        int fd, unsigned start, unsigned len)
120 {
121         struct flock lock;
122
123         switch (fstype) {
124         case FSTYPE_SMB:
125                 return cli_unlock(c, fd, start, len);
126
127         case FSTYPE_NFS:
128                 lock.l_type = F_UNLCK;
129                 lock.l_whence = SEEK_SET;
130                 lock.l_start = start;
131                 lock.l_len = len;
132                 lock.l_pid = getpid();
133                 return fcntl(fd,F_SETLK,&lock) == 0;
134         }
135
136         return False;
137 }       
138
139 static void print_brl(SMB_DEV_T dev, SMB_INO_T ino, int pid, 
140                       enum brl_type lock_type,
141                       br_off start, br_off size)
142 {
143         printf("%6d   %05x:%05x    %s  %.0f:%.0f(%.0f)\n", 
144                (int)pid, (int)dev, (int)ino, 
145                lock_type==READ_LOCK?"R":"W",
146                (double)start, (double)start+size-1,(double)size);
147
148 }
149
150 /***************************************************** 
151 return a connection to a server
152 *******************************************************/
153 struct cli_state *connect_one(char *share)
154 {
155         struct cli_state *c;
156         struct nmb_name called, calling;
157         char *server_n;
158         fstring server;
159         struct in_addr ip;
160         extern struct in_addr ipzero;
161         fstring myname;
162         static int count;
163
164         fstrcpy(server,share+2);
165         share = strchr_m(server,'\\');
166         if (!share) return NULL;
167         *share = 0;
168         share++;
169
170         server_n = server;
171         
172         ip = ipzero;
173
174         slprintf(myname,sizeof(myname), "lock-%u-%u", getpid(), count++);
175
176         make_nmb_name(&calling, myname, 0x0);
177         make_nmb_name(&called , server, 0x20);
178
179  again:
180         ip = ipzero;
181
182         /* have to open a new connection */
183         if (!(c=cli_initialise(NULL)) || !cli_connect(c, server_n, &ip)) {
184                 DEBUG(0,("Connection to %s failed\n", server_n));
185                 return NULL;
186         }
187
188         if (!cli_session_request(c, &calling, &called)) {
189                 DEBUG(0,("session request to %s failed\n", called.name));
190                 cli_shutdown(c);
191                 if (strcmp(called.name, "*SMBSERVER")) {
192                         make_nmb_name(&called , "*SMBSERVER", 0x20);
193                         goto again;
194                 }
195                 return NULL;
196         }
197
198         DEBUG(4,(" session request ok\n"));
199
200         if (!cli_negprot(c)) {
201                 DEBUG(0,("protocol negotiation failed\n"));
202                 cli_shutdown(c);
203                 return NULL;
204         }
205
206         if (!got_pass) {
207                 char *pass = getpass("Password: ");
208                 if (pass) {
209                         pstrcpy(password, pass);
210                 }
211         }
212
213         if (!cli_session_setup(c, username, 
214                                password, strlen(password),
215                                password, strlen(password),
216                                lp_workgroup())) {
217                 DEBUG(0,("session setup failed: %s\n", cli_errstr(c)));
218                 return NULL;
219         }
220
221         /*
222          * These next two lines are needed to emulate
223          * old client behaviour for people who have
224          * scripts based on client output.
225          * QUESTION ? Do we want to have a 'client compatibility
226          * mode to turn these on/off ? JRA.
227          */
228
229         if (*c->server_domain || *c->server_os || *c->server_type)
230                 DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
231                         c->server_domain,c->server_os,c->server_type));
232         
233         DEBUG(4,(" session setup ok\n"));
234
235         if (!cli_send_tconX(c, share, "?????",
236                             password, strlen(password)+1)) {
237                 DEBUG(0,("tree connect failed: %s\n", cli_errstr(c)));
238                 cli_shutdown(c);
239                 return NULL;
240         }
241
242         DEBUG(4,(" tconx ok\n"));
243
244         c->use_oplocks = use_oplocks;
245
246         return c;
247 }
248
249
250 static void reconnect(struct cli_state *cli[NSERVERS][NCONNECTIONS], 
251                       char *nfs[NSERVERS], 
252                       int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
253                       char *share1, char *share2)
254 {
255         int server, conn, f, fstype;
256         char *share[2];
257         share[0] = share1;
258         share[1] = share2;
259
260         fstype = FSTYPE_SMB;
261
262         for (server=0;server<NSERVERS;server++)
263         for (conn=0;conn<NCONNECTIONS;conn++) {
264                 if (cli[server][conn]) {
265                         for (f=0;f<NFILES;f++) {
266                                 cli_close(cli[server][conn], fnum[server][fstype][conn][f]);
267                         }
268                         cli_ulogoff(cli[server][conn]);
269                         cli_shutdown(cli[server][conn]);
270                         free(cli[server][conn]);
271                         cli[server][conn] = NULL;
272                 }
273                 cli[server][conn] = connect_one(share[server]);
274                 if (!cli[server][conn]) {
275                         DEBUG(0,("Failed to connect to %s\n", share[server]));
276                         exit(1);
277                 }
278         }
279 }
280
281
282
283 static BOOL test_one(struct cli_state *cli[NSERVERS][NCONNECTIONS], 
284                      char *nfs[NSERVERS],
285                      int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
286                      struct record *rec)
287 {
288         unsigned conn = rec->conn;
289         unsigned f = rec->f;
290         unsigned fstype = rec->fstype;
291         unsigned start = rec->start;
292         unsigned len = rec->len;
293         unsigned r1 = rec->r1;
294         unsigned r2 = rec->r2;
295         unsigned op;
296         int server;
297         BOOL ret[NSERVERS];
298
299         if (r1 < READ_PCT) {
300                 op = READ_LOCK; 
301         } else {
302                 op = WRITE_LOCK; 
303         }
304
305         if (r2 < LOCK_PCT) {
306                 /* set a lock */
307                 for (server=0;server<NSERVERS;server++) {
308                         ret[server] = try_lock(cli[server][conn], fstype,
309                                                fnum[server][fstype][conn][f],
310                                                start, len, op);
311                 }
312                 if (showall || ret[0] != ret[1]) {
313                         printf("lock   conn=%u fstype=%u f=%u range=%u:%u(%u) op=%s -> %u:%u\n",
314                                conn, fstype, f, 
315                                start, start+len-1, len,
316                                op==READ_LOCK?"READ_LOCK":"WRITE_LOCK",
317                                ret[0], ret[1]);
318                 }
319                 if (showall) brl_forall(print_brl);
320                 if (ret[0] != ret[1]) return False;
321         } else if (r2 < LOCK_PCT+UNLOCK_PCT) {
322                 /* unset a lock */
323                 for (server=0;server<NSERVERS;server++) {
324                         ret[server] = try_unlock(cli[server][conn], fstype,
325                                                  fnum[server][fstype][conn][f],
326                                                  start, len);
327                 }
328                 if (showall || (!hide_unlock_fails && (ret[0] != ret[1]))) {
329                         printf("unlock conn=%u fstype=%u f=%u range=%u:%u(%u)       -> %u:%u\n",
330                                conn, fstype, f, 
331                                start, start+len-1, len,
332                                ret[0], ret[1]);
333                 }
334                 if (showall) brl_forall(print_brl);
335                 if (!hide_unlock_fails && ret[0] != ret[1]) return False;
336         } else {
337                 /* reopen the file */
338                 for (server=0;server<NSERVERS;server++) {
339                         try_close(cli[server][conn], fstype, fnum[server][fstype][conn][f]);
340                         fnum[server][fstype][conn][f] = try_open(cli[server][conn], nfs[server], fstype, FILENAME,
341                                                                  O_RDWR|O_CREAT);
342                         if (fnum[server][fstype][conn][f] == -1) {
343                                 printf("failed to reopen on share1\n");
344                                 return False;
345                         }
346                 }
347                 if (showall) {
348                         printf("reopen conn=%u fstype=%u f=%u\n",
349                                conn, fstype, f);
350                         brl_forall(print_brl);
351                 }
352         }
353         return True;
354 }
355
356 static void close_files(struct cli_state *cli[NSERVERS][NCONNECTIONS], 
357                         char *nfs[NSERVERS],
358                         int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES])
359 {
360         int server, conn, f, fstype; 
361
362         for (server=0;server<NSERVERS;server++)
363         for (fstype=0;fstype<NUMFSTYPES;fstype++)
364         for (conn=0;conn<NCONNECTIONS;conn++)
365         for (f=0;f<NFILES;f++) {
366                 if (fnum[server][fstype][conn][f] != -1) {
367                         try_close(cli[server][conn], fstype, fnum[server][fstype][conn][f]);
368                         fnum[server][fstype][conn][f] = -1;
369                 }
370         }
371         for (server=0;server<NSERVERS;server++) {
372                 cli_unlink(cli[server][0], FILENAME);
373         }
374 }
375
376 static void open_files(struct cli_state *cli[NSERVERS][NCONNECTIONS], 
377                        char *nfs[NSERVERS],
378                        int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES])
379 {
380         int server, fstype, conn, f; 
381
382         for (server=0;server<NSERVERS;server++)
383         for (fstype=0;fstype<NUMFSTYPES;fstype++)
384         for (conn=0;conn<NCONNECTIONS;conn++)
385         for (f=0;f<NFILES;f++) {
386                 fnum[server][fstype][conn][f] = try_open(cli[server][conn], nfs[server], fstype, FILENAME,
387                                                          O_RDWR|O_CREAT);
388                 if (fnum[server][fstype][conn][f] == -1) {
389                         fprintf(stderr,"Failed to open fnum[%u][%u][%u][%u]\n",
390                                 server, fstype, conn, f);
391                         exit(1);
392                 }
393         }
394 }
395
396
397 static int retest(struct cli_state *cli[NSERVERS][NCONNECTIONS], 
398                   char *nfs[NSERVERS],
399                   int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
400                   int n)
401 {
402         int i;
403         printf("testing %u ...\n", n);
404         for (i=0; i<n; i++) {
405                 if (i && i % 100 == 0) {
406                         printf("%u\n", i);
407                 }
408
409                 if (recorded[i].needed &&
410                     !test_one(cli, nfs, fnum, &recorded[i])) return i;
411         }
412         return n;
413 }
414
415
416 /* each server has two connections open to it. Each connection has two file
417    descriptors open on the file - 8 file descriptors in total 
418
419    we then do random locking ops in tamdem on the 4 fnums from each
420    server and ensure that the results match
421  */
422 static void test_locks(char *share1, char *share2, char *nfspath1, char *nfspath2)
423 {
424         struct cli_state *cli[NSERVERS][NCONNECTIONS];
425         char *nfs[NSERVERS];
426         int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES];
427         int n, i, n1; 
428
429         nfs[0] = nfspath1;
430         nfs[1] = nfspath2;
431
432         ZERO_STRUCT(fnum);
433         ZERO_STRUCT(cli);
434
435         recorded = (struct record *)malloc(sizeof(*recorded) * numops);
436
437         for (n=0; n<numops; n++) {
438                 recorded[n].conn = random() % NCONNECTIONS;
439                 recorded[n].fstype = random() % NUMFSTYPES;
440                 recorded[n].f = random() % NFILES;
441                 recorded[n].start = LOCKBASE + ((unsigned)random() % (LOCKRANGE-1));
442                 recorded[n].len = 1 + 
443                         random() % (LOCKRANGE-(recorded[n].start-LOCKBASE));
444                 recorded[n].start *= RANGE_MULTIPLE;
445                 recorded[n].len *= RANGE_MULTIPLE;
446                 recorded[n].r1 = random() % 100;
447                 recorded[n].r2 = random() % 100;
448                 recorded[n].needed = True;
449         }
450
451         reconnect(cli, nfs, fnum, share1, share2);
452         open_files(cli, nfs, fnum);
453         n = retest(cli, nfs, fnum, numops);
454
455         if (n == numops || !analyze) return;
456         n++;
457
458         while (1) {
459                 n1 = n;
460
461                 close_files(cli, nfs, fnum);
462                 reconnect(cli, nfs, fnum, share1, share2);
463                 open_files(cli, nfs, fnum);
464
465                 for (i=0;i<n-1;i++) {
466                         int m;
467                         recorded[i].needed = False;
468
469                         close_files(cli, nfs, fnum);
470                         open_files(cli, nfs, fnum);
471
472                         m = retest(cli, nfs, fnum, n);
473                         if (m == n) {
474                                 recorded[i].needed = True;
475                         } else {
476                                 if (i < m) {
477                                         memmove(&recorded[i], &recorded[i+1],
478                                                 (m-i)*sizeof(recorded[0]));
479                                 }
480                                 n = m;
481                                 i--;
482                         }
483                 }
484
485                 if (n1 == n) break;
486         }
487
488         close_files(cli, nfs, fnum);
489         reconnect(cli, nfs, fnum, share1, share2);
490         open_files(cli, nfs, fnum);
491         showall = True;
492         n1 = retest(cli, nfs, fnum, n);
493         if (n1 != n-1) {
494                 printf("ERROR - inconsistent result (%u %u)\n", n1, n);
495         }
496         close_files(cli, nfs, fnum);
497
498         for (i=0;i<n;i++) {
499                 printf("{%u, %u, %u, %u, %u, %u, %u, %u},\n",
500                        recorded[i].r1,
501                        recorded[i].r2,
502                        recorded[i].conn,
503                        recorded[i].fstype,
504                        recorded[i].f,
505                        recorded[i].start,
506                        recorded[i].len,
507                        recorded[i].needed);
508         }       
509 }
510
511
512
513 static void usage(void)
514 {
515         printf(
516 "Usage:\n\
517   locktest //server1/share1 //server2/share2 /path1 /path2 [options..]\n\
518   options:\n\
519         -U user%%pass\n\
520         -s seed\n\
521         -o numops\n\
522         -u          hide unlock fails\n\
523         -a          (show all ops)\n\
524         -O          use oplocks\n\
525 ");
526 }
527
528 /****************************************************************************
529   main program
530 ****************************************************************************/
531  int main(int argc,char *argv[])
532 {
533         char *share1, *share2, *nfspath1, *nfspath2;
534         extern char *optarg;
535         extern int optind;
536         int opt;
537         char *p;
538         int seed;
539         static pstring servicesf = CONFIGFILE;
540
541         setlinebuf(stdout);
542
543         dbf = x_stderr;
544
545         if (argc < 5 || argv[1][0] == '-') {
546                 usage();
547                 exit(1);
548         }
549
550         share1 = argv[1];
551         share2 = argv[2];
552         nfspath1 = argv[3];
553         nfspath2 = argv[4];
554
555         all_string_sub(share1,"/","\\",0);
556         all_string_sub(share2,"/","\\",0);
557
558         setup_logging(argv[0],True);
559
560         argc -= 4;
561         argv += 4;
562
563         TimeInit();
564
565         lp_load(servicesf,True,False,False);
566         load_interfaces();
567
568         if (getenv("USER")) {
569                 pstrcpy(username,getenv("USER"));
570         }
571
572         seed = time(NULL);
573
574         while ((opt = getopt(argc, argv, "U:s:ho:aAW:O")) != EOF) {
575                 switch (opt) {
576                 case 'U':
577                         pstrcpy(username,optarg);
578                         p = strchr_m(username,'%');
579                         if (p) {
580                                 *p = 0;
581                                 pstrcpy(password, p+1);
582                                 got_pass = 1;
583                         }
584                         break;
585                 case 's':
586                         seed = atoi(optarg);
587                         break;
588                 case 'u':
589                         hide_unlock_fails = True;
590                         break;
591                 case 'o':
592                         numops = atoi(optarg);
593                         break;
594                 case 'O':
595                         use_oplocks = True;
596                         break;
597                 case 'a':
598                         showall = True;
599                         break;
600                 case 'A':
601                         analyze = True;
602                         break;
603                 case 'h':
604                         usage();
605                         exit(1);
606                 default:
607                         printf("Unknown option %c (%d)\n", (char)opt, opt);
608                         exit(1);
609                 }
610         }
611
612         argc -= optind;
613         argv += optind;
614
615         DEBUG(0,("seed=%u\n", seed));
616         srandom(seed);
617
618         locking_init(1);
619         test_locks(share1, share2, nfspath1, nfspath2);
620
621         return(0);
622 }