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