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