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