s3: Early start of an async nbench
[nivanova/samba-autobuild/.git] / source3 / torture / nbench.c
1 /*
2    Unix SMB/CIFS implementation.
3    In-memory cache
4    Copyright (C) Volker Lendecke 2007
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "torture/proto.h"
22
23 static long long int ival(const char *str)
24 {
25         return strtoll(str, NULL, 0);
26 }
27
28 struct nbench_state {
29         struct tevent_context *ev;
30         struct cli_state *cli;
31         const char *cliname;
32         FILE *loadfile;
33         struct ftable *ftable;
34         void (*bw_report)(size_t nread,
35                           size_t nwritten,
36                           void *private_data);
37         void *bw_report_private;
38 };
39
40 struct lock_info {
41         struct lock_info *next, *prev;
42         off_t offset;
43         int size;
44 };
45
46 struct createx_params {
47         char *fname;
48         unsigned int cr_options;
49         unsigned int cr_disposition;
50         int handle;
51 };
52
53 struct ftable {
54         struct ftable *next, *prev;
55         struct createx_params cp;
56         struct lock_info *locks;
57         uint16_t fnum; /* the fd that we got back from the server */
58 };
59
60 enum nbench_cmd {
61         NBENCH_CMD_NTCREATEX,
62         NBENCH_CMD_CLOSE,
63         NBENCH_CMD_RENAME,
64         NBENCH_CMD_UNLINK,
65         NBENCH_CMD_DELTREE,
66         NBENCH_CMD_RMDIR,
67         NBENCH_CMD_MKDIR,
68         NBENCH_CMD_QUERY_PATH_INFORMATION,
69         NBENCH_CMD_QUERY_FILE_INFORMATION,
70         NBENCH_CMD_QUERY_FS_INFORMATION,
71         NBENCH_CMD_SET_FILE_INFORMATION,
72         NBENCH_CMD_FIND_FIRST,
73         NBENCH_CMD_WRITEX,
74         NBENCH_CMD_WRITE,
75         NBENCH_CMD_LOCKX,
76         NBENCH_CMD_UNLOCKX,
77         NBENCH_CMD_READX,
78         NBENCH_CMD_FLUSH,
79         NBENCH_CMD_SLEEP,
80 };
81
82 struct nbench_cmd_struct {
83         char **params;
84         int num_params;
85         NTSTATUS status;
86         enum nbench_cmd cmd;
87 };
88
89 static struct nbench_cmd_struct *nbench_parse(TALLOC_CTX *mem_ctx,
90                                               const char *line)
91 {
92         struct nbench_cmd_struct *result;
93         char *cmd;
94         char *status;
95
96         result = TALLOC_P(mem_ctx, struct nbench_cmd_struct);
97         if (result == NULL) {
98                 return NULL;
99         }
100         result->params = str_list_make_shell(mem_ctx, line, " ");
101         if (result->params == NULL) {
102                 goto fail;
103         }
104         result->num_params = talloc_array_length(result->params) - 1;
105         if (result->num_params < 2) {
106                 goto fail;
107         }
108         status = result->params[result->num_params-1];
109         if (strncmp(status, "NT_STATUS_", 10) != 0 &&
110             strncmp(status, "0x", 2) != 0) {
111                 goto fail;
112         }
113         /* accept numeric or string status codes */
114         if (strncmp(status, "0x", 2) == 0) {
115                 result->status = NT_STATUS(strtoul(status, NULL, 16));
116         } else {
117                 result->status = nt_status_string_to_code(status);
118         }
119
120         cmd = result->params[0];
121
122         if (!strcmp(cmd, "NTCreateX")) {
123                 result->cmd = NBENCH_CMD_NTCREATEX;
124         } else if (!strcmp(cmd, "Close")) {
125                 result->cmd = NBENCH_CMD_CLOSE;
126         } else if (!strcmp(cmd, "Rename")) {
127                 result->cmd = NBENCH_CMD_RENAME;
128         } else if (!strcmp(cmd, "Unlink")) {
129                 result->cmd = NBENCH_CMD_UNLINK;
130         } else if (!strcmp(cmd, "Deltree")) {
131                 result->cmd = NBENCH_CMD_DELTREE;
132         } else if (!strcmp(cmd, "Rmdir")) {
133                 result->cmd = NBENCH_CMD_RMDIR;
134         } else if (!strcmp(cmd, "Mkdir")) {
135                 result->cmd = NBENCH_CMD_MKDIR;
136         } else if (!strcmp(cmd, "QUERY_PATH_INFORMATION")) {
137                 result->cmd = NBENCH_CMD_QUERY_PATH_INFORMATION;
138         } else if (!strcmp(cmd, "QUERY_FILE_INFORMATION")) {
139                 result->cmd = NBENCH_CMD_QUERY_FILE_INFORMATION;
140         } else if (!strcmp(cmd, "QUERY_FS_INFORMATION")) {
141                 result->cmd = NBENCH_CMD_QUERY_FS_INFORMATION;
142         } else if (!strcmp(cmd, "SET_FILE_INFORMATION")) {
143                 result->cmd = NBENCH_CMD_SET_FILE_INFORMATION;
144         } else if (!strcmp(cmd, "FIND_FIRST")) {
145                 result->cmd = NBENCH_CMD_FIND_FIRST;
146         } else if (!strcmp(cmd, "WriteX")) {
147                 result->cmd = NBENCH_CMD_WRITEX;
148         } else if (!strcmp(cmd, "Write")) {
149                 result->cmd = NBENCH_CMD_WRITE;
150         } else if (!strcmp(cmd, "LockX")) {
151                 result->cmd = NBENCH_CMD_LOCKX;
152         } else if (!strcmp(cmd, "UnlockX")) {
153                 result->cmd = NBENCH_CMD_UNLOCKX;
154         } else if (!strcmp(cmd, "ReadX")) {
155                 result->cmd = NBENCH_CMD_READX;
156         } else if (!strcmp(cmd, "Flush")) {
157                 result->cmd = NBENCH_CMD_FLUSH;
158         } else if (!strcmp(cmd, "Sleep")) {
159                 result->cmd = NBENCH_CMD_SLEEP;
160         } else {
161                 goto fail;
162         }
163         return result;
164 fail:
165         TALLOC_FREE(result);
166         return NULL;
167 }
168
169 static struct ftable *ft_find(struct ftable *ftlist, int handle)
170 {
171         while (ftlist != NULL) {
172                 if (ftlist->cp.handle == handle) {
173                         return ftlist;
174                 }
175                 ftlist = ftlist->next;
176         }
177         return NULL;
178 }
179
180 struct nbench_cmd_state {
181         struct tevent_context *ev;
182         struct nbench_state *state;
183         struct nbench_cmd_struct *cmd;
184         struct ftable *ft;
185         bool eof;
186 };
187
188 static void nbench_cmd_done(struct tevent_req *subreq);
189
190 static struct tevent_req *nbench_cmd_send(TALLOC_CTX *mem_ctx,
191                                           struct tevent_context *ev,
192                                           struct nbench_state *nb_state)
193 {
194         struct tevent_req *req, *subreq;
195         struct nbench_cmd_state *state;
196         char line[1024];
197         size_t len;
198
199         req = tevent_req_create(mem_ctx, &state, struct nbench_cmd_state);
200         if (req == NULL) {
201                 return NULL;
202         }
203         state->ev = ev;
204         state->state = nb_state;
205
206         if (fgets(line, sizeof(line), nb_state->loadfile) == NULL) {
207                 tevent_req_nterror(req, NT_STATUS_END_OF_FILE);
208                 return tevent_req_post(req, ev);
209         }
210         len = strlen(line);
211         if (len == 0) {
212                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
213                 return tevent_req_post(req, ev);
214         }
215         if (line[len-1] == '\n') {
216                 line[len-1] = '\0';
217         }
218
219         state->cmd = nbench_parse(state, line);
220         if (state->cmd == NULL) {
221                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
222                 return tevent_req_post(req, ev);
223         }
224
225         switch (state->cmd->cmd) {
226         case NBENCH_CMD_NTCREATEX: {
227                 uint32_t desired_access;
228                 uint32_t share_mode;
229                 unsigned int flags = 0;
230
231                 state->ft = talloc(state, struct ftable);
232                 if (tevent_req_nomem(state->ft, req)) {
233                         return tevent_req_post(req, ev);
234                 }
235
236                 state->ft->cp.fname = talloc_all_string_sub(
237                         state->ft, state->cmd->params[1], "client1",
238                         nb_state->cliname);
239                 if (tevent_req_nomem(state->ft->cp.fname, req)) {
240                         return tevent_req_post(req, ev);
241                 }
242                 state->ft->cp.cr_options = ival(state->cmd->params[2]);
243                 state->ft->cp.cr_disposition = ival(state->cmd->params[3]);
244                 state->ft->cp.handle = ival(state->cmd->params[4]);
245
246                 if (state->ft->cp.cr_options & FILE_DIRECTORY_FILE) {
247                         desired_access = SEC_FILE_READ_DATA;
248                 } else {
249                         desired_access =
250                                 SEC_FILE_READ_DATA |
251                                 SEC_FILE_WRITE_DATA |
252                                 SEC_FILE_READ_ATTRIBUTE |
253                                 SEC_FILE_WRITE_ATTRIBUTE;
254                         flags = EXTENDED_RESPONSE_REQUIRED
255                                 | REQUEST_OPLOCK | REQUEST_BATCH_OPLOCK;
256                 }
257                 share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
258
259                 subreq = cli_ntcreate_send(
260                         state, ev, nb_state->cli, state->ft->cp.fname, flags,
261                         desired_access, 0, share_mode,
262                         state->ft->cp.cr_disposition,
263                         state->ft->cp.cr_options, 0);
264                 break;
265         }
266         case NBENCH_CMD_CLOSE: {
267                 state->ft = ft_find(state->state->ftable,
268                                     ival(state->cmd->params[1]));
269                 if (state->ft == NULL) {
270                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
271                         return tevent_req_post(req, ev);
272                 }
273                 subreq = cli_close_send(
274                         state, ev, nb_state->cli, state->ft->fnum);
275                 break;
276         }
277         case NBENCH_CMD_MKDIR: {
278                 char *fname;
279                 fname = talloc_all_string_sub(
280                         state, state->cmd->params[1], "client1",
281                         nb_state->cliname);
282                 if (tevent_req_nomem(state->ft->cp.fname, req)) {
283                         return tevent_req_post(req, ev);
284                 }
285                 subreq = cli_mkdir_send(state, ev, nb_state->cli, fname);
286                 break;
287         }
288         case NBENCH_CMD_QUERY_PATH_INFORMATION: {
289                 char *fname;
290                 fname = talloc_all_string_sub(
291                         state, state->cmd->params[1], "client1",
292                         nb_state->cliname);
293                 if (tevent_req_nomem(state->ft->cp.fname, req)) {
294                         return tevent_req_post(req, ev);
295                 }
296                 subreq = cli_qpathinfo_send(state, ev, nb_state->cli, fname,
297                                             ival(state->cmd->params[2]),
298                                             0, nb_state->cli->max_xmit);
299                 break;
300         }
301         default:
302                 tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
303                 return tevent_req_post(req, ev);
304         }
305
306         if (tevent_req_nomem(subreq, req)) {
307                 return tevent_req_post(req, ev);
308         }
309         tevent_req_set_callback(subreq, nbench_cmd_done, req);
310         return req;
311 }
312
313 static bool status_wrong(struct tevent_req *req, NTSTATUS expected,
314                          NTSTATUS status)
315 {
316         if (NT_STATUS_EQUAL(expected, status)) {
317                 return false;
318         }
319         if (NT_STATUS_IS_OK(status)) {
320                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
321         }
322         tevent_req_nterror(req, status);
323         return true;
324 }
325
326 static void nbench_cmd_done(struct tevent_req *subreq)
327 {
328         struct tevent_req *req = tevent_req_callback_data(
329                 subreq, struct tevent_req);
330         struct nbench_cmd_state *state = tevent_req_data(
331                 req, struct nbench_cmd_state);
332         struct nbench_state *nbstate = state->state;
333         NTSTATUS status;
334
335         switch (state->cmd->cmd) {
336         case NBENCH_CMD_NTCREATEX: {
337                 struct ftable *ft;
338                 status = cli_ntcreate_recv(subreq, &state->ft->fnum);
339                 TALLOC_FREE(subreq);
340                 if (status_wrong(req, state->cmd->status, status)) {
341                         return;
342                 }
343                 if (!NT_STATUS_IS_OK(status)) {
344                         tevent_req_done(req);
345                         return;
346                 }
347                 ft = talloc_move(nbstate, &state->ft);
348                 DLIST_ADD(nbstate->ftable, ft);
349                 break;
350         }
351         case NBENCH_CMD_CLOSE: {
352                 status = cli_close_recv(subreq);
353                 TALLOC_FREE(subreq);
354                 if (status_wrong(req, state->cmd->status, status)) {
355                         return;
356                 }
357                 DLIST_REMOVE(state->state->ftable, state->ft);
358                 TALLOC_FREE(state->ft);
359                 break;
360         }
361         case NBENCH_CMD_MKDIR: {
362                 status = cli_mkdir_recv(subreq);
363                 TALLOC_FREE(subreq);
364                 if (status_wrong(req, state->cmd->status, status)) {
365                         return;
366                 }
367                 break;
368         }
369         case NBENCH_CMD_QUERY_PATH_INFORMATION: {
370                 status = cli_qpathinfo_recv(subreq, NULL, NULL, NULL);
371                 TALLOC_FREE(subreq);
372                 if (status_wrong(req, state->cmd->status, status)) {
373                         return;
374                 }
375                 break;
376         }
377         default:
378                 break;
379         }
380         tevent_req_done(req);
381 }
382
383 static NTSTATUS nbench_cmd_recv(struct tevent_req *req)
384 {
385         return tevent_req_simple_recv_ntstatus(req);
386 }
387
388 static void nbench_done(struct tevent_req *subreq);
389
390 static struct tevent_req *nbench_send(
391         TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
392         const char *cliname, FILE *loadfile,
393         void (*bw_report)(size_t nread, size_t nwritten, void *private_data),
394         void *bw_report_private)
395 {
396         struct tevent_req *req, *subreq;
397         struct nbench_state *state;
398
399         req = tevent_req_create(mem_ctx, &state, struct nbench_state);
400         if (req == NULL) {
401                 return NULL;
402         }
403         state->ev = ev;
404         state->cli = cli;
405         state->cliname = cliname;
406         state->loadfile = loadfile;
407         state->bw_report = bw_report;
408         state->bw_report_private = bw_report_private;
409
410         subreq = nbench_cmd_send(state, ev, state);
411         if (tevent_req_nomem(subreq, req)) {
412                 return tevent_req_post(req, ev);
413         }
414         tevent_req_set_callback(subreq, nbench_done, req);
415         return req;
416 }
417
418 static void nbench_done(struct tevent_req *subreq)
419 {
420         struct tevent_req *req = tevent_req_callback_data(
421                 subreq, struct tevent_req);
422         struct nbench_state *state = tevent_req_data(
423                 req, struct nbench_state);
424         NTSTATUS status;
425
426         status = nbench_cmd_recv(subreq);
427         TALLOC_FREE(subreq);
428
429         if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
430                 tevent_req_done(req);
431                 return;
432         }
433         if (!NT_STATUS_IS_OK(status)) {
434                 tevent_req_nterror(req, status);
435                 return;
436         }
437         subreq = nbench_cmd_send(state, state->ev, state);
438         if (tevent_req_nomem(subreq, req)) {
439                 return;
440         }
441         tevent_req_set_callback(subreq, nbench_done, req);
442 }
443
444 static NTSTATUS nbench_recv(struct tevent_req *req)
445 {
446         return tevent_req_simple_recv_ntstatus(req);
447 }
448
449 bool run_nbench2(int dummy)
450 {
451         TALLOC_CTX *frame = talloc_stackframe();
452         struct tevent_context *ev;
453         struct cli_state *cli = NULL;
454         FILE *loadfile;
455         bool ret = false;
456         struct tevent_req *req;
457         NTSTATUS status;
458
459         loadfile = fopen("client.txt", "r");
460         if (loadfile == NULL) {
461                 fprintf(stderr, "Could not open \"client.txt\": %s\n",
462                         strerror(errno));
463                 return false;
464         }
465         ev = tevent_context_init(talloc_tos());
466         if (ev == NULL) {
467                 goto fail;
468         }
469         if (!torture_open_connection(&cli, 0)) {
470                 goto fail;
471         }
472
473         req = nbench_send(talloc_tos(), ev, cli, "client1", loadfile,
474                           NULL, NULL);
475         if (req == NULL) {
476                 goto fail;
477         }
478         if (!tevent_req_poll(req, ev)) {
479                 goto fail;
480         }
481         status = nbench_recv(req);
482         TALLOC_FREE(req);
483         printf("nbench returned %s\n", nt_errstr(status));
484
485         ret = true;
486 fail:
487         if (cli != NULL) {
488                 torture_close_connection(cli);
489         }
490         TALLOC_FREE(ev);
491         if (loadfile != NULL) {
492                 fclose(loadfile);
493                 loadfile = NULL;
494         }
495         TALLOC_FREE(frame);
496         return ret;
497 }