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