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  %9.0f   %9.0f   %9.0f\n", 
91                (int)pid, (int)dev, (int)ino, 
92                lock_type==READ_LOCK?"R":"W",
93                (double)start, (double)size, (double)start+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 op=%s -> %d:%d\n",
254                                conn, f, start, len, op==READ_LOCK?"READ_LOCK":"WRITE_LOCK",
255                                ret1, ret2);
256                 }
257                 if (showall) brl_forall(print_brl);
258                 if (ret1 != ret2) return False;
259         } else if (r2 < LOCK_PCT+UNLOCK_PCT) {
260                 /* unset a lock */
261                 ret1 = cli_unlock(cli[0][conn], 
262                                   fnum[0][conn][f],
263                                   start, len);
264                 ret2 = cli_unlock(cli[1][conn], 
265                                   fnum[1][conn][f],
266                                   start, len);
267                 if (showall || ret1 != ret2) {
268                         printf("unlock conn=%d f=%d %d:%d       -> %d:%d\n",
269                                conn, f, start, len,
270                                ret1, ret2);
271                 }
272                 if (showall) brl_forall(print_brl);
273                 if (ret1 != ret2) return False;
274         } else {
275                 /* reopen the file */
276                 cli_close(cli[0][conn], fnum[0][conn][f]);
277                 cli_close(cli[1][conn], fnum[1][conn][f]);
278                 fnum[0][conn][f] = cli_open(cli[0][conn], FILENAME,
279                                             O_RDWR|O_CREAT,
280                                             DENY_NONE);
281                 fnum[1][conn][f] = cli_open(cli[1][conn], FILENAME,
282                                             O_RDWR|O_CREAT,
283                                             DENY_NONE);
284                 if (fnum[0][conn][f] == -1) {
285                         printf("failed to reopen on share1\n");
286                         return False;
287                 }
288                 if (fnum[1][conn][f] == -1) {
289                         printf("failed to reopen on share2\n");
290                         return False;
291                 }
292                 if (showall) {
293                         printf("reopen conn=%d f=%d\n",
294                                conn, f);
295                         brl_forall(print_brl);
296                 }
297         }
298         return True;
299 }
300
301 static void close_files(struct cli_state *cli[2][2], 
302                        int fnum[2][2][2])
303 {
304         int server, conn, f; 
305
306         for (server=0;server<2;server++)
307         for (conn=0;conn<NCONNECTIONS;conn++)
308         for (f=0;f<NFILES;f++) {
309                 if (fnum[server][conn][f] != -1) {
310                         cli_close(cli[server][conn], fnum[server][conn][f]);
311                         fnum[server][conn][f] = -1;
312                 }
313         }
314         cli_unlink(cli[0][0], FILENAME);
315         cli_unlink(cli[1][0], FILENAME);
316 }
317
318 static void open_files(struct cli_state *cli[2][2], 
319                        int fnum[2][2][2])
320 {
321         int server, conn, f; 
322
323         for (server=0;server<2;server++)
324         for (conn=0;conn<NCONNECTIONS;conn++)
325         for (f=0;f<NFILES;f++) {
326                 fnum[server][conn][f] = cli_open(cli[server][conn], FILENAME,
327                                                  O_RDWR|O_CREAT,
328                                                  DENY_NONE);
329                 if (fnum[server][conn][f] == -1) {
330                         fprintf(stderr,"Failed to open fnum[%d][%d][%d]\n",
331                                 server, conn, f);
332                         exit(1);
333                 }
334         }
335 }
336
337
338 static int retest(struct cli_state *cli[2][2], 
339                    int fnum[2][2][2],
340                    int n)
341 {
342         int i;
343         printf("testing %d ...\n", n);
344         for (i=0; i<n; i++) {
345                 if (i && i % 100 == 0) {
346                         printf("%d\n", i);
347                 }
348
349                 if (recorded[i].needed &&
350                     !test_one(cli, fnum, &recorded[i])) return i;
351         }
352         return n;
353 }
354
355
356 /* each server has two connections open to it. Each connection has two file
357    descriptors open on the file - 8 file descriptors in total 
358
359    we then do random locking ops in tamdem on the 4 fnums from each
360    server and ensure that the results match
361  */
362 static void test_locks(char *share1, char *share2)
363 {
364         struct cli_state *cli[2][2];
365         int fnum[2][2][2];
366         int n, i, n1; 
367
368         ZERO_STRUCT(fnum);
369         ZERO_STRUCT(cli);
370
371         recorded = (struct record *)malloc(sizeof(*recorded) * numops);
372
373         for (n=0; n<numops; n++) {
374                 if (n < sizeof(preset) / sizeof(preset[0])) {
375                         recorded[n] = preset[n];
376                 } else {
377                         recorded[n].conn = random() % NCONNECTIONS;
378                         recorded[n].f = random() % NFILES;
379                         recorded[n].start = random() % (LOCKRANGE-1);
380                         recorded[n].len = 1 + random() % (LOCKRANGE-recorded[n].start);
381                         recorded[n].start *= RANGE_MULTIPLE;
382                         recorded[n].len *= RANGE_MULTIPLE;
383                         recorded[n].r1 = random() % 100;
384                         recorded[n].r2 = random() % 100;
385                         recorded[n].needed = True;
386                 }
387         }
388
389         reconnect(cli, fnum, share1, share2);
390         open_files(cli, fnum);
391         n = retest(cli, fnum, numops);
392
393         if (n == numops || !analyze) return;
394         n++;
395
396         while (1) {
397                 n1 = n;
398
399                 close_files(cli, fnum);
400                 reconnect(cli, fnum, share1, share2);
401                 open_files(cli, fnum);
402
403                 for (i=0;i<n-1;i++) {
404                         int m;
405                         recorded[i].needed = False;
406
407                         close_files(cli, fnum);
408                         open_files(cli, fnum);
409
410                         m = retest(cli, fnum, n);
411                         if (m == n) {
412                                 recorded[i].needed = True;
413                         } else {
414                                 if (i < m) {
415                                         memmove(&recorded[i], &recorded[i+1],
416                                                 (m-i)*sizeof(recorded[0]));
417                                 }
418                                 n = m;
419                                 i--;
420                         }
421                 }
422
423                 if (n1 == n) break;
424         }
425
426         close_files(cli, fnum);
427         reconnect(cli, fnum, share1, share2);
428         open_files(cli, fnum);
429         showall = True;
430         n1 = retest(cli, fnum, n);
431         if (n1 != n-1) {
432                 printf("ERROR - inconsistent result (%d %d)\n", n1, n);
433         }
434         close_files(cli, fnum);
435
436         for (i=0;i<n;i++) {
437                 printf("{%d, %d, %d, %d, %d, %d, %d},\n",
438                        recorded[i].r1,
439                        recorded[i].r2,
440                        recorded[i].conn,
441                        recorded[i].f,
442                        recorded[i].start,
443                        recorded[i].len,
444                        recorded[i].needed);
445         }       
446 }
447
448
449
450 static void usage(void)
451 {
452         printf(
453 "Usage:\n\
454   locktest //server1/share1 //server2/share2 [options..]\n\
455   options:\n\
456         -U user%%pass\n\
457         -s seed\n\
458         -o numops\n\
459         -a          (show all ops)\n\
460 ");
461 }
462
463 /****************************************************************************
464   main program
465 ****************************************************************************/
466  int main(int argc,char *argv[])
467 {
468         char *share1, *share2;
469         extern char *optarg;
470         extern int optind;
471         extern FILE *dbf;
472         int opt;
473         char *p;
474         int seed;
475         static pstring servicesf = CONFIGFILE;
476
477         setlinebuf(stdout);
478
479         dbf = stderr;
480
481         if (argv[1][0] == '-' || argc < 3) {
482                 usage();
483                 exit(1);
484         }
485
486         share1 = argv[1];
487         share2 = argv[2];
488
489         all_string_sub(share1,"/","\\",0);
490         all_string_sub(share2,"/","\\",0);
491
492         setup_logging(argv[0],True);
493
494         argc -= 2;
495         argv += 2;
496
497         TimeInit();
498         charset_initialise();
499
500         lp_load(servicesf,True,False,False);
501         load_interfaces();
502
503         if (getenv("USER")) {
504                 pstrcpy(username,getenv("USER"));
505         }
506
507         seed = time(NULL);
508
509         while ((opt = getopt(argc, argv, "U:s:ho:aA")) != EOF) {
510                 switch (opt) {
511                 case 'U':
512                         pstrcpy(username,optarg);
513                         p = strchr(username,'%');
514                         if (p) {
515                                 *p = 0;
516                                 pstrcpy(password, p+1);
517                                 got_pass = 1;
518                         }
519                         break;
520                 case 's':
521                         seed = atoi(optarg);
522                         break;
523                 case 'o':
524                         numops = atoi(optarg);
525                         break;
526                 case 'a':
527                         showall = True;
528                         break;
529                 case 'A':
530                         analyze = True;
531                         break;
532                 case 'h':
533                         usage();
534                         exit(1);
535                 default:
536                         printf("Unknown option %c (%d)\n", (char)opt, opt);
537                         exit(1);
538                 }
539         }
540
541         argc -= optind;
542         argv += optind;
543
544         DEBUG(0,("seed=%d\n", seed));
545         srandom(seed);
546
547         locking_init(1);
548         test_locks(share1, share2);
549
550         return(0);
551 }