1 /* -*- c-basic-offset: 2 -*- */
2 /*
3   Copyright(C) 2010-2014 Brazil
4 
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License version 2.1 as published by the Free Software Foundation.
8 
9   This library is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   Lesser General Public License for more details.
13 
14   You should have received a copy of the GNU Lesser General Public
15   License along with this library; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
17 */
18 
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif /* HAVE_CONFIG_H */
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 
30 #ifdef HAVE_SYS_WAIT_H
31 # include <sys/wait.h>
32 #endif /* HAVE_SYS_WAIT_H */
33 #ifdef HAVE_SYS_SOCKET_H
34 # include <sys/socket.h>
35 #endif /* HAVE_SYS_SOCKET_H */
36 #ifndef WIN32
37 # include <netinet/in.h>
38 #endif /* WIN32 */
39 
40 #include <grn_str.h>
41 #include <grn_com.h>
42 #include <grn_db.h>
43 
44 #ifdef WIN32
45 #include <windows.h>
46 #include <stddef.h>
47 #else
48 #include <sys/param.h>
49 #include <sys/utsname.h>
50 #include <sys/statvfs.h>
51 #include <libgen.h>
52 #endif /* WIN32 */
53 
54 /*
55 #define DEBUG_FTP
56 #define DEBUG_HTTP
57 */
58 
59 #define FTPUSER "anonymous"
60 #define FTPPASSWD "grntest"
61 #define FTPSERVER "ftp.groonga.org"
62 #define FTPBUF 20000
63 #define DEFAULT_PORT 10041
64 #define DEFAULT_DEST "localhost"
65 
66 #define OUT_JSON 0
67 #define OUT_TSV  1
68 
69 static int grntest_outtype = OUT_JSON;
70 
71 static grn_critical_section grntest_cs;
72 
73 static int grntest_stop_flag = 0;
74 static int grntest_detail_on = 0;
75 static int grntest_remote_mode = 0;
76 static int grntest_localonly_mode = 0;
77 static int grntest_owndb_mode = 0;
78 static int grntest_onmemory_mode = 0;
79 static grn_bool grntest_ftp_mode = GRN_FALSE;
80 #define TMPFILE "_grntest.tmp"
81 
82 static grn_ctx grntest_server_context;
83 static FILE *grntest_log_file;
84 
85 #define OS_LINUX64   "LINUX64"
86 #define OS_LINUX32   "LINUX32"
87 #define OS_WINDOWS64 "WINDOWS64"
88 #define OS_WINDOWS32 "WINDOWS32"
89 
90 #ifdef WIN32
91 typedef SOCKET socket_t;
92 #define SOCKETERROR INVALID_SOCKET
93 #define socketclose closesocket
94 static const char *groonga_path = "groonga.exe";
95 static PROCESS_INFORMATION grntest_pi;
96 #else
97 static pid_t grntest_server_id = 0;
98 typedef int socket_t;
99 #define socketclose close
100 #define SOCKETERROR -1
101 static const char *groonga_path = "groonga";
102 #endif /* WIN32 */
103 
104 static const char *groonga_protocol = "gqtp";
105 static const char *grntest_osinfo;
106 static int grntest_sigint = 0;
107 
108 
109 
110 static grn_obj *grntest_db = NULL;
111 
112 #define MAX_CON_JOB 10
113 #define MAX_CON 64
114 
115 #define BUF_LEN 1024
116 #define MAX_PATH_LEN 256
117 
118 #define J_DO_LOCAL  1  /* do_local */
119 #define J_DO_GQTP   2  /* do_gqtp */
120 #define J_DO_HTTP   3  /* do_http */
121 #define J_REP_LOCAL 4  /* rep_local */
122 #define J_REP_GQTP  5  /* rep_gqtp */
123 #define J_REP_HTTP  6  /* rep_http */
124 #define J_OUT_LOCAL 7  /* out_local */
125 #define J_OUT_GQTP  8  /* out_gqtp */
126 #define J_OUT_HTTP  9  /* out_http */
127 #define J_TEST_LOCAL 10  /* test_local */
128 #define J_TEST_GQTP  11  /* test_gqtp */
129 #define J_TEST_HTTP  12  /* test_http */
130 
131 static char grntest_username[BUF_LEN];
132 static char grntest_scriptname[BUF_LEN];
133 static char grntest_date[BUF_LEN];
134 static char grntest_serverhost[BUF_LEN];
135 static int grntest_serverport;
136 static const char *grntest_dbpath;
137 
138 struct job {
139   char jobname[BUF_LEN];
140   char commandfile[BUF_LEN];
141   int qnum;
142   int jobtype;
143   int concurrency;
144   int ntimes;
145   int done;
146   long long int max;
147   long long int min;
148   FILE *outputlog;
149   grn_file_reader *inputlog;
150   char logfile[BUF_LEN];
151 };
152 
153 struct task {
154   char *file;
155   grn_obj *commands;
156   int jobtype;
157   int ntimes;
158   int qnum;
159   int job_id;
160   long long int max;
161   long long int min;
162   socket_t http_socket;
163   grn_obj http_response;
164 };
165 
166 static struct task grntest_task[MAX_CON];
167 static struct job grntest_job[MAX_CON];
168 static int grntest_jobdone;
169 static int grntest_jobnum;
170 static grn_ctx grntest_ctx[MAX_CON];
171 static grn_obj *grntest_owndb[MAX_CON];
172 
173 static grn_obj grntest_starttime, grntest_jobs_start;
174 
175 static int
grntest_atoi(const char * str,const char * end,const char ** rest)176 grntest_atoi(const char *str, const char *end, const char **rest)
177 {
178   while (grn_isspace(str, GRN_ENC_UTF8) == 1) {
179     str++;
180   }
181   return grn_atoi(str, end, rest);
182 }
183 
184 static int
out_p(int jobtype)185 out_p(int jobtype)
186 {
187   if (jobtype == J_OUT_LOCAL) {
188     return 1;
189   }
190   if (jobtype == J_OUT_GQTP) {
191     return 1;
192   }
193   if (jobtype == J_OUT_HTTP) {
194     return 1;
195   }
196   return 0;
197 }
198 
199 static int
test_p(int jobtype)200 test_p(int jobtype)
201 {
202   if (jobtype == J_TEST_LOCAL) {
203     return 1;
204   }
205   if (jobtype == J_TEST_GQTP) {
206     return 1;
207   }
208   if (jobtype == J_TEST_HTTP) {
209     return 1;
210   }
211   return 0;
212 }
213 
214 static int
report_p(int jobtype)215 report_p(int jobtype)
216 {
217   if (jobtype == J_REP_LOCAL) {
218     return 1;
219   }
220   if (jobtype == J_REP_GQTP) {
221     return 1;
222   }
223   if (jobtype == J_REP_HTTP) {
224     return 1;
225   }
226   return 0;
227 }
228 
229 static int
gqtp_p(int jobtype)230 gqtp_p(int jobtype)
231 {
232   if (jobtype == J_DO_GQTP) {
233     return 1;
234   }
235   if (jobtype == J_REP_GQTP) {
236     return 1;
237   }
238   if (jobtype == J_OUT_GQTP) {
239     return 1;
240   }
241   if (jobtype == J_TEST_GQTP) {
242     return 1;
243   }
244   return 0;
245 }
246 
247 static int
http_p(int jobtype)248 http_p(int jobtype)
249 {
250   if (jobtype == J_DO_HTTP) {
251     return 1;
252   }
253   if (jobtype == J_REP_HTTP) {
254     return 1;
255   }
256   if (jobtype == J_OUT_HTTP) {
257     return 1;
258   }
259   if (jobtype == J_TEST_HTTP) {
260     return 1;
261   }
262   return 0;
263 }
264 
265 static int
error_exit_in_thread(intptr_t code)266 error_exit_in_thread(intptr_t code)
267 {
268   fprintf(stderr,
269           "Fatal error! Check script file or database!: %ld\n", (long)code);
270   fflush(stderr);
271   CRITICAL_SECTION_ENTER(grntest_cs);
272   grntest_stop_flag = 1;
273   CRITICAL_SECTION_LEAVE(grntest_cs);
274 #ifdef WIN32
275   _endthreadex(code);
276 #else
277   pthread_exit((void *)code);
278 #endif /* WIN32 */
279   return 0;
280 }
281 
282 
283 static void
escape_command(grn_ctx * ctx,const char * in,int ilen,grn_obj * escaped_command)284 escape_command(grn_ctx *ctx, const char *in, int ilen, grn_obj *escaped_command)
285 {
286   int i = 0;
287 
288   while (i < ilen) {
289     if ((in[i] == '\\') || (in[i] == '\"') || (in[i] == '/')) {
290       GRN_TEXT_PUTC(ctx, escaped_command, '\\');
291       GRN_TEXT_PUTC(ctx, escaped_command, in[i]);
292       i++;
293     } else {
294       switch (in[i]) {
295       case '\b':
296         GRN_TEXT_PUTS(ctx, escaped_command, "\\b");
297         i++;
298         break;
299       case '\f':
300         GRN_TEXT_PUTS(ctx, escaped_command, "\\f");
301         i++;
302         break;
303       case '\n':
304         GRN_TEXT_PUTS(ctx, escaped_command, "\\n");
305         i++;
306         break;
307       case '\r':
308         GRN_TEXT_PUTS(ctx, escaped_command, "\\r");
309         i++;
310         break;
311       case '\t':
312         GRN_TEXT_PUTS(ctx, escaped_command, "\\t");
313         i++;
314         break;
315       default:
316         GRN_TEXT_PUTC(ctx, escaped_command, in[i]);
317         i++;
318         break;
319       }
320     }
321   }
322   GRN_TEXT_PUTC(ctx, escaped_command, '\0');
323 }
324 
325 static int
report_command(grn_ctx * ctx,const char * command,const char * ret,int task_id,grn_obj * start_time,grn_obj * end_time)326 report_command(grn_ctx *ctx, const char *command, const char *ret, int task_id,
327                grn_obj *start_time, grn_obj *end_time)
328 {
329   int i, len, clen;
330   long long int start, end;
331   grn_obj result, escaped_command;
332 
333   GRN_TEXT_INIT(&result, 0);
334   if (strncmp(ret, "[[", 2) == 0) {
335     i = 2;
336     len = 1;
337     while (ret[i] != ']') {
338       i++;
339       len++;
340       if (ret[i] == '\0') {
341         fprintf(stderr, "Error results:command=[%s]\n", command);
342         error_exit_in_thread(3);
343       }
344     }
345     len++;
346     grn_text_esc(ctx, &result, ret + 1, len);
347   } else {
348     grn_text_esc(ctx, &result, ret, strlen(ret));
349   }
350 
351   start = GRN_TIME_VALUE(start_time) - GRN_TIME_VALUE(&grntest_starttime);
352   end = GRN_TIME_VALUE(end_time) - GRN_TIME_VALUE(&grntest_starttime);
353   clen = strlen(command);
354   GRN_TEXT_INIT(&escaped_command, 0);
355   escape_command(ctx, command, clen, &escaped_command);
356   if (grntest_outtype == OUT_TSV) {
357     fprintf(grntest_log_file, "report\t%d\t%s\t%" GRN_FMT_LLD "\t%" GRN_FMT_LLD "\t%.*s\n",
358             task_id, GRN_TEXT_VALUE(&escaped_command), start, end,
359             (int)GRN_TEXT_LEN(&result), GRN_TEXT_VALUE(&result));
360   } else {
361     fprintf(grntest_log_file, "[%d, \"%s\", %" GRN_FMT_LLD ", %" GRN_FMT_LLD ", %.*s],\n",
362             task_id, GRN_TEXT_VALUE(&escaped_command), start, end,
363             (int)GRN_TEXT_LEN(&result), GRN_TEXT_VALUE(&result));
364   }
365   fflush(grntest_log_file);
366   GRN_OBJ_FIN(ctx, &escaped_command);
367   GRN_OBJ_FIN(ctx, &result);
368   return 0;
369 }
370 
371 static int
output_result_final(grn_ctx * ctx,int qnum)372 output_result_final(grn_ctx *ctx, int qnum)
373 {
374   grn_obj end_time;
375   long long int latency, self;
376   double sec, qps;
377 
378   GRN_TIME_INIT(&end_time, 0);
379   GRN_TIME_NOW(ctx, &end_time);
380 
381   latency = GRN_TIME_VALUE(&end_time) - GRN_TIME_VALUE(&grntest_starttime);
382   self = latency;
383   sec = self / (double)1000000;
384   qps = (double)qnum / sec;
385   if (grntest_outtype == OUT_TSV) {
386     fprintf(grntest_log_file, "total\t%" GRN_FMT_LLD "\t%f\t%d\n", latency, qps, qnum);
387   } else {
388     fprintf(grntest_log_file,
389            "{\"total\": %" GRN_FMT_LLD ", \"qps\": %f, \"queries\": %d}]\n", latency, qps, qnum);
390   }
391   grn_obj_close(ctx, &end_time);
392   return 0;
393 }
394 
395 static int
output_sysinfo(char * sysinfo)396 output_sysinfo(char *sysinfo)
397 {
398   if (grntest_outtype == OUT_TSV) {
399     fprintf(grntest_log_file, "%s", sysinfo);
400   } else {
401     fprintf(grntest_log_file, "[%s\n", sysinfo);
402   }
403   return 0;
404 }
405 
406 /* #define ENABLE_ERROR_REPORT 1 */
407 #ifdef ENABLE_ERROR_REPORT
408 static int
error_command(grn_ctx * ctx,char * command,int task_id)409 error_command(grn_ctx *ctx, char *command, int task_id)
410 {
411   fprintf(stderr, "error!:command=[%s] task_id = %d\n", command, task_id);
412   fflush(stderr);
413   error_exit_in_thread(1);
414   return 0;
415 }
416 #endif
417 
418 static void
normalize_output(char * output,int length,char ** normalized_output,int * normalized_length)419 normalize_output(char *output, int length,
420                  char **normalized_output, int *normalized_length)
421 {
422   int i;
423 
424   *normalized_output = NULL;
425   *normalized_length = length;
426   for (i = 0; i < length; i++) {
427     if (!strncmp(output + i, "],", 2)) {
428       *normalized_output = output + i + 2;
429       *normalized_length -= i + 2;
430       break;
431     }
432   }
433 
434   if (!*normalized_output) {
435     if (length > 2 && strncmp(output + length - 2, "]]", 2)) {
436       *normalized_output = output + length;
437       *normalized_length = 0;
438     } else {
439       *normalized_output = output;
440     }
441   }
442 }
443 
444 static grn_bool
same_result_p(char * expect,int expected_length,char * result,int result_length)445 same_result_p(char *expect, int expected_length, char *result, int result_length)
446 {
447   char *normalized_expected, *normalized_result;
448   int normalized_expected_length, normalized_result_length;
449 
450   normalize_output(expect, expected_length,
451                    &normalized_expected, &normalized_expected_length);
452   normalize_output(result, result_length,
453                    &normalized_result, &normalized_result_length);
454 
455   return((normalized_expected_length == normalized_result_length) &&
456          strncmp(normalized_expected, normalized_result,
457                  normalized_expected_length) == 0);
458 }
459 
460 static socket_t
open_socket(const char * host,int port)461 open_socket(const char *host, int port)
462 {
463   socket_t sock;
464   struct hostent *servhost;
465   struct sockaddr_in server;
466   u_long inaddr;
467   int ret;
468 
469   servhost = gethostbyname(host);
470   if (servhost == NULL){
471     fprintf(stderr, "Bad hostname [%s]\n", host);
472     return -1;
473   }
474   inaddr = *(u_long*)(servhost->h_addr_list[0]);
475 
476   memset(&server, 0, sizeof(struct sockaddr_in));
477   server.sin_family = AF_INET;
478   server.sin_port = htons(port);
479   server.sin_addr = *(struct in_addr*)&inaddr;
480 
481   sock = socket(AF_INET, SOCK_STREAM, 0);
482   if (sock == -1) {
483     fprintf(stderr, "socket error\n");
484     return -1;
485   }
486   ret = connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_in));
487   if (ret == -1) {
488     fprintf(stderr, "connect error\n");
489     return -1;
490   }
491   return sock;
492 }
493 
494 static int
write_to_server(socket_t socket,const char * buf)495 write_to_server(socket_t socket, const char *buf)
496 {
497 #ifdef DEBUG_FTP
498   fprintf(stderr, "send:%s", buf);
499 #endif
500   send(socket, buf, strlen(buf), 0);
501   return 0;
502 }
503 
504 #define OUTPUT_TYPE "output_type"
505 #define OUTPUT_TYPE_LEN (sizeof(OUTPUT_TYPE) - 1)
506 
507 static void
command_line_to_uri_path(grn_ctx * ctx,grn_obj * uri,const char * command)508 command_line_to_uri_path(grn_ctx *ctx, grn_obj *uri, const char *command)
509 {
510   char tok_type;
511   int offset = 0, have_key = 0;
512   const char *p, *e, *v;
513   grn_obj buf, *expr = NULL;
514   grn_expr_var *vars;
515   unsigned int nvars;
516 
517   GRN_TEXT_INIT(&buf, 0);
518   p = command;
519   e = command + strlen(command);
520   p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
521   if ((expr = grn_ctx_get(ctx, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf)))) {
522     grn_obj params, output_type;
523 
524     GRN_TEXT_INIT(&params, 0);
525     GRN_TEXT_INIT(&output_type, 0);
526     vars = ((grn_proc *)expr)->vars;
527     nvars = ((grn_proc *)expr)->nvars;
528     GRN_TEXT_PUTS(ctx, uri, "/d/");
529     GRN_TEXT_PUT(ctx, uri, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
530     while (p < e) {
531       GRN_BULK_REWIND(&buf);
532       p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
533       v = GRN_TEXT_VALUE(&buf);
534       switch (tok_type) {
535       case GRN_TOK_VOID :
536         p = e;
537         break;
538       case GRN_TOK_SYMBOL :
539         if (GRN_TEXT_LEN(&buf) > 2 && v[0] == '-' && v[1] == '-') {
540           int l = GRN_TEXT_LEN(&buf) - 2;
541           v += 2;
542           if (l == OUTPUT_TYPE_LEN && !memcmp(v, OUTPUT_TYPE, OUTPUT_TYPE_LEN)) {
543             GRN_BULK_REWIND(&output_type);
544             p = grn_text_unesc_tok(ctx, &output_type, p, e, &tok_type);
545             break;
546           }
547           if (GRN_TEXT_LEN(&params)) {
548             GRN_TEXT_PUTS(ctx, &params, "&");
549           }
550           grn_text_urlenc(ctx, &params, v, l);
551           have_key = 1;
552           break;
553         }
554         /* fallthru */
555       case GRN_TOK_STRING :
556       case GRN_TOK_QUOTE :
557         if (!have_key) {
558           if (offset < nvars) {
559             if (GRN_TEXT_LEN(&params)) {
560               GRN_TEXT_PUTS(ctx, &params, "&");
561             }
562             grn_text_urlenc(ctx, &params,
563                             vars[offset].name, vars[offset].name_size);
564             offset++;
565           }
566         }
567         GRN_TEXT_PUTS(ctx, &params, "=");
568         grn_text_urlenc(ctx, &params, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
569         have_key = 0;
570         break;
571       }
572     }
573     GRN_TEXT_PUTS(ctx, uri, ".");
574     if (GRN_TEXT_LEN(&output_type)) {
575       GRN_TEXT_PUT(ctx, uri,
576                    GRN_TEXT_VALUE(&output_type), GRN_TEXT_LEN(&output_type));
577     } else {
578       GRN_TEXT_PUTS(ctx, uri, "json");
579     }
580     if (GRN_TEXT_LEN(&params) > 0) {
581       GRN_TEXT_PUTS(ctx, uri, "?");
582       GRN_TEXT_PUT(ctx, uri, GRN_TEXT_VALUE(&params), GRN_TEXT_LEN(&params));
583     }
584     GRN_OBJ_FIN(ctx, &params);
585     GRN_OBJ_FIN(ctx, &output_type);
586   }
587   GRN_OBJ_FIN(ctx, &buf);
588 }
589 
590 static void
command_send_http(grn_ctx * ctx,const char * command,int type,int task_id)591 command_send_http(grn_ctx *ctx, const char *command, int type, int task_id)
592 {
593   socket_t http_socket;
594   grn_obj buf;
595 
596   http_socket = open_socket(grntest_serverhost, grntest_serverport);
597   if (http_socket == SOCKETERROR) {
598     fprintf(stderr, "failed to connect to groonga at %s:%d via HTTP: ",
599             grntest_serverhost, grntest_serverport);
600 #ifdef WIN32
601     fprintf(stderr, "%lu\n", GetLastError());
602 #else
603     fprintf(stderr, "%s\n", strerror(errno));
604 #endif
605     error_exit_in_thread(100);
606   }
607   grntest_task[task_id].http_socket = http_socket;
608   GRN_BULK_REWIND(&grntest_task[task_id].http_response);
609 
610   GRN_TEXT_INIT(&buf, 0);
611   GRN_TEXT_PUTS(ctx, &buf, "GET ");
612   if (strncmp(command, "/d/", 3) == 0) {
613     GRN_TEXT_PUTS(ctx, &buf, command);
614   } else {
615     command_line_to_uri_path(ctx, &buf, command);
616   }
617 #ifdef DEBUG_HTTP
618   fprintf(stderr, "command: <%s>\n", command);
619   fprintf(stderr, "path:    <%.*s>\n",
620           (int)GRN_TEXT_LEN(&buf), GRN_TEXT_VALUE(&buf));
621 #endif
622   GRN_TEXT_PUTS(ctx, &buf, " HTTP/1.1\r\n");
623   GRN_TEXT_PUTS(ctx, &buf, "Host: ");
624   GRN_TEXT_PUTS(ctx, &buf, grntest_serverhost);
625   GRN_TEXT_PUTS(ctx, &buf, "\r\n");
626   GRN_TEXT_PUTS(ctx, &buf, "User-Agent: grntest/");
627   GRN_TEXT_PUTS(ctx, &buf, grn_get_version());
628   GRN_TEXT_PUTS(ctx, &buf, "\r\n");
629   GRN_TEXT_PUTS(ctx, &buf, "Connection: close\r\n");
630   GRN_TEXT_PUTS(ctx, &buf, "\r\n");
631   GRN_TEXT_PUTC(ctx, &buf, '\0');
632   write_to_server(http_socket, GRN_TEXT_VALUE(&buf));
633   GRN_OBJ_FIN(ctx, &buf);
634 }
635 
636 static void
command_send_ctx(grn_ctx * ctx,const char * command,int type,int task_id)637 command_send_ctx(grn_ctx *ctx, const char *command, int type, int task_id)
638 {
639   grn_ctx_send(ctx, command, strlen(command), 0);
640 /* fix me.
641    when command fails, ctx->rc is not 0 in local mode!
642   if (ctx->rc) {
643     fprintf(stderr, "ctx_send:rc=%d:command:%s\n", ctx->rc, command);
644     error_exit_in_thread(1);
645   }
646 */
647 }
648 
649 static void
command_send(grn_ctx * ctx,const char * command,int type,int task_id)650 command_send(grn_ctx *ctx, const char *command, int type, int task_id)
651 {
652   if (http_p(type)) {
653     command_send_http(ctx, command, type, task_id);
654   } else {
655     command_send_ctx(ctx, command, type, task_id);
656   }
657 }
658 
659 static void
command_recv_http(grn_ctx * ctx,int type,int task_id,char ** res,unsigned int * res_len,int * flags)660 command_recv_http(grn_ctx *ctx, int type, int task_id,
661                   char **res, unsigned int *res_len, int *flags)
662 {
663   int len;
664   char buf[BUF_LEN];
665   char *p, *e;
666   socket_t http_socket;
667   grn_obj *http_response;
668 
669   http_socket = grntest_task[task_id].http_socket;
670   http_response = &grntest_task[task_id].http_response;
671   while ((len = recv(http_socket, buf, BUF_LEN - 1, 0))) {
672 #ifdef DEBUG_HTTP
673     fprintf(stderr, "receive: <%.*s>\n", len, buf);
674 #endif
675     GRN_TEXT_PUT(ctx, http_response, buf, len);
676   }
677 
678   p = GRN_TEXT_VALUE(http_response);
679   e = p + GRN_TEXT_LEN(http_response);
680   while (p < e) {
681     if (p[0] != '\r') {
682       p++;
683       continue;
684     }
685     if (e - p >= 4) {
686       if (!memcmp(p, "\r\n\r\n", 4)) {
687         *res = p + 4;
688         *res_len = e - *res;
689 #ifdef DEBUG_HTTP
690         fprintf(stderr, "body: <%.*s>\n", *res_len, *res);
691 #endif
692         break;
693       }
694       p += 4;
695     } else {
696       *res = NULL;
697       *res_len = 0;
698       break;
699     }
700   }
701 
702   socketclose(http_socket);
703   grntest_task[task_id].http_socket = 0;
704 }
705 
706 static void
command_recv_ctx(grn_ctx * ctx,int type,int task_id,char ** res,unsigned int * res_len,int * flags)707 command_recv_ctx(grn_ctx *ctx, int type, int task_id,
708                  char **res, unsigned int *res_len, int *flags)
709 {
710   grn_ctx_recv(ctx, res, res_len, flags);
711   if (ctx->rc) {
712     fprintf(stderr, "ctx_recv:rc=%d\n", ctx->rc);
713     error_exit_in_thread(1);
714   }
715 }
716 
717 static void
command_recv(grn_ctx * ctx,int type,int task_id,char ** res,unsigned int * res_len,int * flags)718 command_recv(grn_ctx *ctx, int type, int task_id,
719              char **res, unsigned int *res_len, int *flags)
720 {
721   if (http_p(type)) {
722     command_recv_http(ctx, type, task_id, res, res_len, flags);
723   } else {
724     command_recv_ctx(ctx, type, task_id, res, res_len, flags);
725   }
726 }
727 
728 static int
shutdown_server(void)729 shutdown_server(void)
730 {
731   char *res;
732   int flags;
733   unsigned int res_len;
734   int job_type;
735   int task_id = 0;
736 
737   if (grntest_remote_mode) {
738     return 0;
739   }
740   job_type = grntest_task[task_id].jobtype;
741   command_send(&grntest_server_context, "shutdown", job_type, task_id);
742   if (grntest_server_context.rc) {
743     fprintf(stderr, "ctx_send:rc=%d\n", grntest_server_context.rc);
744     exit(1);
745   }
746   command_recv(&grntest_server_context, job_type, task_id,
747                &res, &res_len, &flags);
748 
749   return 0;
750 }
751 
752 static int
do_load_command(grn_ctx * ctx,char * command,int type,int task_id,long long int * load_start)753 do_load_command(grn_ctx *ctx, char *command, int type, int task_id,
754                 long long int *load_start)
755 {
756   char *res;
757   unsigned int res_len;
758   int flags, ret;
759   grn_obj start_time, end_time;
760 
761   GRN_TIME_INIT(&start_time, 0);
762   if (*load_start == 0) {
763     GRN_TIME_NOW(ctx, &start_time);
764     *load_start = GRN_TIME_VALUE(&start_time);
765   } else {
766     GRN_TIME_SET(ctx, &start_time, *load_start);
767   }
768 
769   command_send(ctx, command, type, task_id);
770   do {
771     command_recv(ctx, type, task_id, &res, &res_len, &flags);
772     if (res_len) {
773       long long int self;
774       GRN_TIME_INIT(&end_time, 0);
775       GRN_TIME_NOW(ctx, &end_time);
776 
777       self = GRN_TIME_VALUE(&end_time) - *load_start;
778 
779       if (grntest_task[task_id].max < self) {
780         grntest_task[task_id].max = self;
781       }
782       if (grntest_task[task_id].min > self) {
783         grntest_task[task_id].min = self;
784       }
785 
786       if (report_p(grntest_task[task_id].jobtype)) {
787         char tmpbuf[BUF_LEN];
788 
789         if (res_len < BUF_LEN) {
790           strncpy(tmpbuf, res, res_len);
791           tmpbuf[res_len] = '\0';
792         } else {
793           strncpy(tmpbuf, res, BUF_LEN - 2);
794           tmpbuf[BUF_LEN -2] = '\0';
795         }
796         report_command(ctx, "load", tmpbuf, task_id, &start_time, &end_time);
797       }
798       if (out_p(grntest_task[task_id].jobtype)) {
799         fwrite(res, 1, res_len, grntest_job[grntest_task[task_id].job_id].outputlog);
800         fputc('\n', grntest_job[grntest_task[task_id].job_id].outputlog);
801         fflush(grntest_job[grntest_task[task_id].job_id].outputlog);
802       }
803       if (test_p(grntest_task[task_id].jobtype)) {
804         grn_obj log;
805         grn_file_reader *input;
806         FILE *output;
807         GRN_TEXT_INIT(&log, 0);
808         input = grntest_job[grntest_task[task_id].job_id].inputlog;
809         output = grntest_job[grntest_task[task_id].job_id].outputlog;
810         if (grn_file_reader_read_line(ctx, input, &log) != GRN_SUCCESS) {
811           GRN_LOG(ctx, GRN_ERROR, "Cannot get input-log");
812           error_exit_in_thread(55);
813         }
814         if (GRN_TEXT_VALUE(&log)[GRN_TEXT_LEN(&log) - 1] == '\n') {
815           grn_bulk_truncate(ctx, &log, GRN_TEXT_LEN(&log) - 1);
816         }
817 
818         if (!same_result_p(GRN_TEXT_VALUE(&log), GRN_TEXT_LEN(&log),
819                            res, res_len)) {
820           fprintf(output, "DIFF:command:%s\n", command);
821           fprintf(output, "DIFF:result:");
822           fwrite(res, 1, res_len, output);
823           fputc('\n', output);
824           fprintf(output, "DIFF:expect:%.*s\n",
825                   (int)GRN_TEXT_LEN(&log), GRN_TEXT_VALUE(&log));
826           fflush(output);
827         }
828         GRN_OBJ_FIN(ctx, &log);
829       }
830       grn_obj_close(ctx, &end_time);
831       ret = 1;
832       break;
833     } else {
834       ret = 0;
835       break;
836     }
837   } while ((flags & GRN_CTX_MORE));
838     grn_obj_close(ctx, &start_time);
839 
840   return ret;
841 }
842 
843 
844 static int
do_command(grn_ctx * ctx,char * command,int type,int task_id)845 do_command(grn_ctx *ctx, char *command, int type, int task_id)
846 {
847   char *res;
848   unsigned int res_len;
849   int flags;
850   grn_obj start_time, end_time;
851 
852   GRN_TIME_INIT(&start_time, 0);
853   GRN_TIME_NOW(ctx, &start_time);
854 
855   command_send(ctx, command, type, task_id);
856   do {
857     command_recv(ctx, type, task_id, &res, &res_len, &flags);
858     if (res_len) {
859       long long int self;
860       GRN_TIME_INIT(&end_time, 0);
861       GRN_TIME_NOW(ctx, &end_time);
862 
863       self = GRN_TIME_VALUE(&end_time) - GRN_TIME_VALUE(&start_time);
864 
865       if (grntest_task[task_id].max < self) {
866         grntest_task[task_id].max = self;
867       }
868       if (grntest_task[task_id].min > self) {
869         grntest_task[task_id].min = self;
870       }
871 
872       if (report_p(grntest_task[task_id].jobtype)) {
873         char tmpbuf[BUF_LEN];
874 
875         if (res_len < BUF_LEN) {
876           strncpy(tmpbuf, res, res_len);
877           tmpbuf[res_len] = '\0';
878         } else {
879           strncpy(tmpbuf, res, BUF_LEN - 2);
880           tmpbuf[BUF_LEN -2] = '\0';
881         }
882         report_command(ctx, command, tmpbuf, task_id, &start_time, &end_time);
883       }
884       if (out_p(grntest_task[task_id].jobtype)) {
885         fwrite(res, 1, res_len, grntest_job[grntest_task[task_id].job_id].outputlog);
886         fputc('\n', grntest_job[grntest_task[task_id].job_id].outputlog);
887         fflush(grntest_job[grntest_task[task_id].job_id].outputlog);
888       }
889       if (test_p(grntest_task[task_id].jobtype)) {
890         grn_obj log;
891         grn_file_reader *input;
892         FILE *output;
893         GRN_TEXT_INIT(&log, 0);
894         input = grntest_job[grntest_task[task_id].job_id].inputlog;
895         output = grntest_job[grntest_task[task_id].job_id].outputlog;
896         if (grn_file_reader_read_line(ctx, input, &log) != GRN_SUCCESS) {
897           GRN_LOG(ctx, GRN_ERROR, "Cannot get input-log");
898           error_exit_in_thread(55);
899         }
900         if (GRN_TEXT_VALUE(&log)[GRN_TEXT_LEN(&log) - 1] == '\n') {
901           grn_bulk_truncate(ctx, &log, GRN_TEXT_LEN(&log) - 1);
902         }
903 
904         if (!same_result_p(GRN_TEXT_VALUE(&log), GRN_TEXT_LEN(&log),
905                            res, res_len)) {
906           fprintf(output, "DIFF:command:%s\n", command);
907           fprintf(output, "DIFF:result:");
908           fwrite(res, 1, res_len, output);
909           fputc('\n', output);
910           fprintf(output, "DIFF:expect:%.*s\n",
911                   (int)GRN_TEXT_LEN(&log), GRN_TEXT_VALUE(&log));
912           fflush(output);
913         }
914         GRN_OBJ_FIN(ctx, &log);
915       }
916       grn_obj_close(ctx, &end_time);
917       break;
918     } else {
919 #ifdef ENABLE_ERROR_REPORT
920       error_command(ctx, command, task_id);
921 #endif
922     }
923   } while ((flags & GRN_CTX_MORE));
924 
925   grn_obj_close(ctx, &start_time);
926 
927   return 0;
928 }
929 
930 static int
comment_p(char * command)931 comment_p(char *command)
932 {
933   if (command[0] == '#') {
934     return 1;
935   }
936   return 0;
937 }
938 
939 static int
load_command_p(char * command)940 load_command_p(char *command)
941 {
942   int i = 0;
943 
944   while (grn_isspace(&command[i], GRN_ENC_UTF8) == 1) {
945     i++;
946   }
947   if (command[i] == '\0') {
948     return 0;
949   }
950   if (!strncmp(&command[i], "load", 4)) {
951     return 1;
952   }
953   return 0;
954 }
955 
956 static int
worker_sub(grn_ctx * ctx,grn_obj * log,int task_id)957 worker_sub(grn_ctx *ctx, grn_obj *log, int task_id)
958 {
959   int i, load_mode, load_count;
960   grn_obj end_time;
961   long long int total_elapsed_time, job_elapsed_time;
962   double sec, qps;
963   long long int load_start;
964   struct task *task;
965   struct job *job;
966 
967   task = &(grntest_task[task_id]);
968   task->max = 0LL;
969   task->min = 9223372036854775807LL;
970   task->qnum = 0;
971 
972   for (i = 0; i < task->ntimes; i++) {
973     if (task->file != NULL) {
974       grn_file_reader *reader;
975       grn_obj line;
976       reader = grn_file_reader_open(ctx, task->file);
977       if (!reader) {
978         fprintf(stderr, "Cannot open %s\n",grntest_task[task_id].file);
979         error_exit_in_thread(1);
980       }
981       load_mode = 0;
982       load_count = 0;
983       load_start = 0LL;
984       GRN_TEXT_INIT(&line, 0);
985       while (grn_file_reader_read_line(ctx, reader, &line) == GRN_SUCCESS) {
986         if (GRN_TEXT_VALUE(&line)[GRN_TEXT_LEN(&line) - 1] == '\n') {
987           grn_bulk_truncate(ctx, &line, GRN_TEXT_LEN(&line) - 1);
988         }
989         if (GRN_TEXT_LEN(&line) == 0) {
990           GRN_BULK_REWIND(&line);
991           continue;
992         }
993         GRN_TEXT_PUTC(ctx, &line, '\0');
994         if (comment_p(GRN_TEXT_VALUE(&line))) {
995           GRN_BULK_REWIND(&line);
996           continue;
997         }
998         if (load_command_p(GRN_TEXT_VALUE(&line))) {
999           load_mode = 1;
1000           load_count = 1;
1001         }
1002         if (load_mode == 1) {
1003           if (do_load_command(&grntest_ctx[task_id], GRN_TEXT_VALUE(&line),
1004                               task->jobtype,
1005                               task_id, &load_start)) {
1006             task->qnum += load_count;
1007             load_mode = 0;
1008             load_count = 0;
1009             load_start = 0LL;
1010           }
1011           load_count++;
1012           GRN_BULK_REWIND(&line);
1013           continue;
1014         }
1015         do_command(&grntest_ctx[task_id], GRN_TEXT_VALUE(&line),
1016                    task->jobtype,
1017                    task_id);
1018         task->qnum++;
1019         GRN_BULK_REWIND(&line);
1020         if (grntest_sigint) {
1021           goto exit;
1022         }
1023       }
1024       GRN_OBJ_FIN(ctx, &line);
1025       grn_file_reader_close(ctx, reader);
1026     } else {
1027       int i, n_commands;
1028       grn_obj *commands;
1029       commands = task->commands;
1030       if (!commands) {
1031         error_exit_in_thread(1);
1032       }
1033       load_mode = 0;
1034       n_commands = GRN_BULK_VSIZE(commands) / sizeof(grn_obj *);
1035       for (i = 0; i < n_commands; i++) {
1036         grn_obj *command;
1037         command = GRN_PTR_VALUE_AT(commands, i);
1038         if (load_command_p(GRN_TEXT_VALUE(command))) {
1039           load_mode = 1;
1040         }
1041         if (load_mode == 1) {
1042           if (do_load_command(&grntest_ctx[task_id],
1043                               GRN_TEXT_VALUE(command),
1044                               task->jobtype, task_id, &load_start)) {
1045             load_mode = 0;
1046             load_start = 0LL;
1047             task->qnum++;
1048           }
1049           continue;
1050         }
1051         do_command(&grntest_ctx[task_id],
1052                    GRN_TEXT_VALUE(command),
1053                    task->jobtype, task_id);
1054         task->qnum++;
1055         if (grntest_sigint) {
1056           goto exit;
1057         }
1058       }
1059     }
1060   }
1061 
1062 exit:
1063   GRN_TIME_INIT(&end_time, 0);
1064   GRN_TIME_NOW(&grntest_ctx[task_id], &end_time);
1065   total_elapsed_time = GRN_TIME_VALUE(&end_time) - GRN_TIME_VALUE(&grntest_starttime);
1066   job_elapsed_time = GRN_TIME_VALUE(&end_time) - GRN_TIME_VALUE(&grntest_jobs_start);
1067 
1068   CRITICAL_SECTION_ENTER(grntest_cs);
1069   job = &(grntest_job[task->job_id]);
1070   if (job->max < task->max) {
1071     job->max = task->max;
1072   }
1073   if (job->min > task->min) {
1074     job->min = task->min;
1075   }
1076 
1077   job->qnum += task->qnum;
1078   job->done++;
1079   if (job->done == job->concurrency) {
1080     char tmpbuf[BUF_LEN];
1081     sec = job_elapsed_time / (double)1000000;
1082     qps = (double)job->qnum/ sec;
1083     grntest_jobdone++;
1084     if (grntest_outtype == OUT_TSV) {
1085       sprintf(tmpbuf,
1086               "job\t"
1087               "%s\t"
1088               "%" GRN_FMT_LLD "\t"
1089               "%" GRN_FMT_LLD "\t"
1090               "%f\t"
1091               "%" GRN_FMT_LLD "\t"
1092               "%" GRN_FMT_LLD "\t"
1093               "%d\n",
1094               job->jobname,
1095               total_elapsed_time,
1096               job_elapsed_time,
1097               qps,
1098               job->min,
1099               job->max,
1100               job->qnum);
1101     } else {
1102       sprintf(tmpbuf,
1103               "{\"job\": \"%s\", "
1104               "\"total_elapsed_time\": %" GRN_FMT_LLD ", "
1105               "\"job_elapsed_time\": %" GRN_FMT_LLD ", "
1106               "\"qps\": %f, "
1107               "\"min\": %" GRN_FMT_LLD ", "
1108               "\"max\": %" GRN_FMT_LLD ", "
1109               "\"queries\": %d}",
1110               job->jobname,
1111               total_elapsed_time,
1112               job_elapsed_time,
1113               qps,
1114               job->min,
1115               job->max,
1116               job->qnum);
1117       if (grntest_jobdone < grntest_jobnum) {
1118         grn_strcat(tmpbuf, BUF_LEN, ",");
1119       }
1120     }
1121     GRN_TEXT_PUTS(ctx, log, tmpbuf);
1122     if (grntest_jobdone == grntest_jobnum) {
1123       if (grntest_outtype == OUT_TSV) {
1124         fprintf(grntest_log_file, "%.*s",
1125                 (int)GRN_TEXT_LEN(log), GRN_TEXT_VALUE(log));
1126       } else {
1127         if (grntest_detail_on) {
1128           fseek(grntest_log_file, -2, SEEK_CUR);
1129           fprintf(grntest_log_file, "],\n");
1130         }
1131         fprintf(grntest_log_file, "\"summary\": [");
1132         fprintf(grntest_log_file, "%.*s",
1133                 (int)GRN_TEXT_LEN(log), GRN_TEXT_VALUE(log));
1134         fprintf(grntest_log_file, "]");
1135       }
1136       fflush(grntest_log_file);
1137     }
1138   }
1139   grn_obj_close(&grntest_ctx[task_id], &end_time);
1140   CRITICAL_SECTION_LEAVE(grntest_cs);
1141 
1142   return 0;
1143 }
1144 
1145 typedef struct _grntest_worker {
1146   grn_ctx *ctx;
1147   grn_obj log;
1148   int task_id;
1149 } grntest_worker;
1150 
1151 #ifdef WIN32
1152 static unsigned int
1153 __stdcall
worker(void * val)1154 worker(void *val)
1155 {
1156   grntest_worker *worker = val;
1157   worker_sub(worker->ctx, &worker->log, worker->task_id);
1158   return 0;
1159 }
1160 #else
1161 static void *
worker(void * val)1162 worker(void *val)
1163 {
1164   grntest_worker *worker = val;
1165   worker_sub(worker->ctx, &worker->log, worker->task_id);
1166   return NULL;
1167 }
1168 #endif /* WIN32 */
1169 
1170 #ifdef WIN32
1171 static int
thread_main(grn_ctx * ctx,int num)1172 thread_main(grn_ctx *ctx, int num)
1173 {
1174   int  i;
1175   int  ret;
1176   HANDLE pthread[MAX_CON];
1177   grntest_worker *workers[MAX_CON];
1178 
1179   for (i = 0; i < num; i++) {
1180     workers[i] = GRN_MALLOC(sizeof(grntest_worker));
1181     workers[i]->ctx = &grntest_ctx[i];
1182     GRN_TEXT_INIT(&workers[i]->log, 0);
1183     workers[i]->task_id = i;
1184     pthread[i] = (HANDLE)_beginthreadex(NULL, 0, worker, (void *)workers[i],
1185                                         0, NULL);
1186     if (pthread[i]== (HANDLE)0) {
1187        fprintf(stderr, "thread failed:%d\n", i);
1188        error_exit_in_thread(1);
1189     }
1190   }
1191 
1192   ret = WaitForMultipleObjects(num, pthread, TRUE, INFINITE);
1193   if (ret == WAIT_TIMEOUT) {
1194      fprintf(stderr, "timeout\n");
1195      error_exit_in_thread(1);
1196   }
1197 
1198   for (i = 0; i < num; i++) {
1199     CloseHandle(pthread[i]);
1200     GRN_OBJ_FIN(workers[i]->ctx, &workers[i]->log);
1201     GRN_FREE(workers[i]);
1202   }
1203   return 0;
1204 }
1205 #else
1206 static int
thread_main(grn_ctx * ctx,int num)1207 thread_main(grn_ctx *ctx, int num)
1208 {
1209   intptr_t i;
1210   int ret;
1211   pthread_t pthread[MAX_CON];
1212   grntest_worker *workers[MAX_CON];
1213 
1214   for (i = 0; i < num; i++) {
1215     workers[i] = GRN_MALLOC(sizeof(grntest_worker));
1216     workers[i]->ctx = &grntest_ctx[i];
1217     GRN_TEXT_INIT(&workers[i]->log, 0);
1218     workers[i]->task_id = i;
1219     ret = pthread_create(&pthread[i], NULL, worker, (void *)workers[i]);
1220     if (ret) {
1221       fprintf(stderr, "Cannot create thread:ret=%d\n", ret);
1222       error_exit_in_thread(1);
1223     }
1224   }
1225 
1226   for (i = 0; i < num; i++) {
1227     ret = pthread_join(pthread[i], NULL);
1228     GRN_OBJ_FIN(workers[i]->ctx, &workers[i]->log);
1229     GRN_FREE(workers[i]);
1230     if (ret) {
1231       fprintf(stderr, "Cannot join thread:ret=%d\n", ret);
1232       error_exit_in_thread(1);
1233     }
1234   }
1235   return 0;
1236 }
1237 #endif
1238 
1239 static int
error_exit(grn_ctx * ctx,int ret)1240 error_exit(grn_ctx *ctx, int ret)
1241 {
1242   fflush(stderr);
1243   shutdown_server();
1244   grn_ctx_fin(ctx);
1245   grn_fin();
1246   exit(ret);
1247 }
1248 
1249 static int
get_sysinfo(const char * path,char * result,int olen)1250 get_sysinfo(const char *path, char *result, int olen)
1251 {
1252   char tmpbuf[256];
1253 
1254 #ifdef WIN32
1255   ULARGE_INTEGER dinfo;
1256   char cpustring[64];
1257   SYSTEM_INFO sinfo;
1258   MEMORYSTATUSEX minfo;
1259   OSVERSIONINFO osinfo;
1260 
1261   if (grntest_outtype == OUT_TSV) {
1262     result[0] = '\0';
1263     sprintf(tmpbuf, "script\t%s\n", grntest_scriptname);
1264     grn_strcat(result, olen, tmpbuf);
1265     sprintf(tmpbuf, "user\t%s\n", grntest_username);
1266     grn_strcat(result, olen, tmpbuf);
1267     sprintf(tmpbuf, "date\t%s\n", grntest_date);
1268     grn_strcat(result, olen, tmpbuf);
1269   } else {
1270     grn_strcpy(result, olen, "{");
1271     sprintf(tmpbuf, "\"script\": \"%s.scr\",\n", grntest_scriptname);
1272     grn_strcat(result, olen, tmpbuf);
1273     sprintf(tmpbuf, "  \"user\": \"%s\",\n", grntest_username);
1274     grn_strcat(result, olen, tmpbuf);
1275     sprintf(tmpbuf, "  \"date\": \"%s\",\n", grntest_date);
1276     grn_strcat(result, olen, tmpbuf);
1277   }
1278 
1279   memset(cpustring, 0, 64);
1280 #ifndef __GNUC__
1281   {
1282     int cinfo[4];
1283     __cpuid(cinfo, 0x80000002);
1284     memcpy(cpustring, cinfo, 16);
1285     __cpuid(cinfo, 0x80000003);
1286     memcpy(cpustring+16, cinfo, 16);
1287     __cpuid(cinfo, 0x80000004);
1288     memcpy(cpustring+32, cinfo, 16);
1289   }
1290 #endif
1291 
1292   if (grntest_outtype == OUT_TSV) {
1293     sprintf(tmpbuf, "%s\n", cpustring);
1294   } else {
1295     sprintf(tmpbuf, "  \"CPU\": \"%s\",\n", cpustring);
1296   }
1297   grn_strcat(result, olen, tmpbuf);
1298 
1299   if (sizeof(int *) == 8) {
1300     grntest_osinfo = OS_WINDOWS64;
1301     if (grntest_outtype == OUT_TSV) {
1302       sprintf(tmpbuf, "64BIT\n");
1303     } else {
1304       sprintf(tmpbuf, "  \"BIT\": 64,\n");
1305     }
1306   } else {
1307     grntest_osinfo = OS_WINDOWS32;
1308     if (grntest_outtype == OUT_TSV) {
1309       sprintf(tmpbuf, "32BIT\n");
1310     } else {
1311       sprintf(tmpbuf, "  \"BIT\": 32,\n");
1312     }
1313   }
1314   grn_strcat(result, olen, tmpbuf);
1315 
1316   GetSystemInfo(&sinfo);
1317   if (grntest_outtype == OUT_TSV) {
1318     sprintf(tmpbuf, "CORE\t%lu\n", sinfo.dwNumberOfProcessors);
1319   } else {
1320     sprintf(tmpbuf, "  \"CORE\": %lu,\n", sinfo.dwNumberOfProcessors);
1321   }
1322   grn_strcat(result, olen, tmpbuf);
1323 
1324   minfo.dwLength = sizeof(MEMORYSTATUSEX);
1325   GlobalMemoryStatusEx(&minfo);
1326   if (grntest_outtype == OUT_TSV) {
1327     sprintf(tmpbuf, "RAM\t%I64dMByte\n", minfo.ullTotalPhys/(1024*1024));
1328   } else {
1329     sprintf(tmpbuf, "  \"RAM\": \"%I64dMByte\",\n", minfo.ullTotalPhys/(1024*1024));
1330   }
1331   grn_strcat(result, olen, tmpbuf);
1332 
1333   GetDiskFreeSpaceEx(NULL, NULL, &dinfo, NULL);
1334   if (grntest_outtype == OUT_TSV) {
1335     sprintf(tmpbuf, "HDD\t%I64dKBytes\n", dinfo.QuadPart/1024 );
1336   } else {
1337     sprintf(tmpbuf, "  \"HDD\": \"%I64dKBytes\",\n", dinfo.QuadPart/1024 );
1338   }
1339   grn_strcat(result, olen, tmpbuf);
1340 
1341   osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osinfo);
1342   if (grntest_outtype == OUT_TSV) {
1343     sprintf(tmpbuf, "Windows %ld.%ld\n",
1344             osinfo.dwMajorVersion, osinfo.dwMinorVersion);
1345   } else {
1346     sprintf(tmpbuf, "  \"OS\": \"Windows %lu.%lu\",\n", osinfo.dwMajorVersion,
1347             osinfo.dwMinorVersion);
1348   }
1349   grn_strcat(result, olen, tmpbuf);
1350 
1351   if (grntest_outtype == OUT_TSV) {
1352     sprintf(tmpbuf, "%s\n", grntest_serverhost);
1353   } else {
1354     sprintf(tmpbuf, "  \"HOST\": \"%s\",\n", grntest_serverhost);
1355   }
1356   grn_strcat(result, olen, tmpbuf);
1357 
1358   if (grntest_outtype == OUT_TSV) {
1359     sprintf(tmpbuf, "%d\n", grntest_serverport);
1360   } else {
1361     sprintf(tmpbuf, "  \"PORT\": \"%d\",\n", grntest_serverport);
1362   }
1363   grn_strcat(result, olen, tmpbuf);
1364 
1365   if (grntest_outtype == OUT_TSV) {
1366     sprintf(tmpbuf, "%s\"\n", grn_get_version());
1367   } else {
1368     sprintf(tmpbuf, "  \"VERSION\": \"%s\"\n", grn_get_version());
1369   }
1370 
1371   grn_strcat(result, olen, tmpbuf);
1372   if (grntest_outtype != OUT_TSV) {
1373     grn_strcat(result, olen, "}");
1374   }
1375 
1376 #else /* linux only */
1377   FILE *fp;
1378   int ret;
1379   int cpunum = 0;
1380   int minfo = 0;
1381   int unevictable = 0;
1382   int mlocked = 0;
1383 #define CPU_STRING_SIZE 256
1384   char cpu_string[CPU_STRING_SIZE];
1385   struct utsname ubuf;
1386   struct statvfs vfsbuf;
1387 
1388   if (grntest_outtype == OUT_TSV) {
1389     result[0] = '\0';
1390     sprintf(tmpbuf, "sctipt\t%s\n", grntest_scriptname);
1391     grn_strcat(result, olen, tmpbuf);
1392     sprintf(tmpbuf, "user\t%s\n", grntest_username);
1393     grn_strcat(result, olen, tmpbuf);
1394     sprintf(tmpbuf, "date\t%s\n", grntest_date);
1395     grn_strcat(result, olen, tmpbuf);
1396   } else {
1397     grn_strcpy(result, olen, "{");
1398     sprintf(tmpbuf, "\"script\": \"%s.scr\",\n", grntest_scriptname);
1399     grn_strcat(result, olen, tmpbuf);
1400     sprintf(tmpbuf, "  \"user\": \"%s\",\n", grntest_username);
1401     grn_strcat(result, olen, tmpbuf);
1402     sprintf(tmpbuf, "  \"date\": \"%s\",\n", grntest_date);
1403     grn_strcat(result, olen, tmpbuf);
1404   }
1405 
1406   fp = fopen("/proc/cpuinfo", "r");
1407   if (!fp) {
1408     fprintf(stderr, "Cannot open cpuinfo\n");
1409     exit(1);
1410   }
1411   while (fgets(tmpbuf, 256, fp) != NULL) {
1412     tmpbuf[strlen(tmpbuf)-1] = '\0';
1413     if (!strncmp(tmpbuf, "model name\t: ", 13)) {
1414       grn_strcpy(cpu_string, CPU_STRING_SIZE, &tmpbuf[13]);
1415     }
1416   }
1417   fclose(fp);
1418 #undef CPU_STRING_SIZE
1419 
1420   cpunum = sysconf(_SC_NPROCESSORS_CONF);
1421 
1422   if (grntest_outtype == OUT_TSV) {
1423     sprintf(tmpbuf, "%s\n", cpu_string);
1424   } else {
1425     sprintf(tmpbuf, "  \"CPU\": \"%s\",\n", cpu_string);
1426   }
1427   grn_strcat(result, olen, tmpbuf);
1428 
1429   if (sizeof(int *) == 8) {
1430     grntest_osinfo = OS_LINUX64;
1431     if (grntest_outtype == OUT_TSV) {
1432       sprintf(tmpbuf, "64BIT\n");
1433     } else {
1434       sprintf(tmpbuf, "  \"BIT\": 64,\n");
1435     }
1436   } else {
1437     grntest_osinfo = OS_LINUX32;
1438     if (grntest_outtype == OUT_TSV) {
1439       sprintf(tmpbuf, "32BIT\n");
1440     } else {
1441       sprintf(tmpbuf, "  \"BIT\": 32,\n");
1442     }
1443   }
1444   grn_strcat(result, olen, tmpbuf);
1445 
1446   if (grntest_outtype == OUT_TSV) {
1447     sprintf(tmpbuf, "CORE\t%d\n", cpunum);
1448   } else {
1449     sprintf(tmpbuf, "  \"CORE\": %d,\n", cpunum);
1450   }
1451   grn_strcat(result, olen, tmpbuf);
1452 
1453   fp = fopen("/proc/meminfo", "r");
1454   if (!fp) {
1455     fprintf(stderr, "Cannot open meminfo\n");
1456     exit(1);
1457   }
1458   while (fgets(tmpbuf, 256, fp) != NULL) {
1459     tmpbuf[strlen(tmpbuf)-1] = '\0';
1460     if (!strncmp(tmpbuf, "MemTotal:", 9)) {
1461       minfo = grntest_atoi(&tmpbuf[10], &tmpbuf[10] + 40, NULL);
1462     }
1463     if (!strncmp(tmpbuf, "Unevictable:", 12)) {
1464       unevictable = grntest_atoi(&tmpbuf[13], &tmpbuf[13] + 40, NULL);
1465     }
1466     if (!strncmp(tmpbuf, "Mlocked:", 8)) {
1467       mlocked = grntest_atoi(&tmpbuf[9], &tmpbuf[9] + 40, NULL);
1468     }
1469   }
1470   fclose(fp);
1471   if (grntest_outtype == OUT_TSV) {
1472     sprintf(tmpbuf, "%dMBytes\n", minfo/1024);
1473     grn_strcat(result, olen, tmpbuf);
1474     sprintf(tmpbuf, "%dMBytes_Unevictable\n", unevictable/1024);
1475     grn_strcat(result, olen, tmpbuf);
1476     sprintf(tmpbuf, "%dMBytes_Mlocked\n", mlocked/1024);
1477     grn_strcat(result, olen, tmpbuf);
1478   } else {
1479     sprintf(tmpbuf, "  \"RAM\": \"%dMBytes\",\n", minfo/1024);
1480     grn_strcat(result, olen, tmpbuf);
1481     sprintf(tmpbuf, "  \"Unevictable\": \"%dMBytes\",\n", unevictable/1024);
1482     grn_strcat(result, olen, tmpbuf);
1483     sprintf(tmpbuf, "  \"Mlocked\": \"%dMBytes\",\n", mlocked/1024);
1484     grn_strcat(result, olen, tmpbuf);
1485   }
1486 
1487   ret = statvfs(path, &vfsbuf);
1488   if (ret) {
1489     fprintf(stderr, "Cannot access %s\n", path);
1490     exit(1);
1491   }
1492 
1493   if (grntest_outtype == OUT_TSV) {
1494     sprintf(tmpbuf, "%" GRN_FMT_INT64U "KBytes\n", vfsbuf.f_blocks * 4);
1495   } else {
1496     sprintf(tmpbuf,
1497             "  \"HDD\": \"%" GRN_FMT_INT64U "KBytes\",\n",
1498             vfsbuf.f_blocks * 4);
1499   }
1500   grn_strcat(result, olen, tmpbuf);
1501 
1502   uname(&ubuf);
1503   if (grntest_outtype == OUT_TSV) {
1504     sprintf(tmpbuf, "%s %s\n", ubuf.sysname, ubuf.release);
1505   } else {
1506     sprintf(tmpbuf, "  \"OS\": \"%s %s\",\n", ubuf.sysname, ubuf.release);
1507   }
1508   grn_strcat(result, olen, tmpbuf);
1509 
1510   if (grntest_outtype == OUT_TSV) {
1511     sprintf(tmpbuf, "%s\n", grntest_serverhost);
1512   } else {
1513     sprintf(tmpbuf, "  \"HOST\": \"%s\",\n", grntest_serverhost);
1514   }
1515   grn_strcat(result, olen, tmpbuf);
1516 
1517   if (grntest_outtype == OUT_TSV) {
1518     sprintf(tmpbuf, "%d\n", grntest_serverport);
1519   } else {
1520     sprintf(tmpbuf, "  \"PORT\": \"%d\",\n", grntest_serverport);
1521   }
1522   grn_strcat(result, olen, tmpbuf);
1523 
1524   if (grntest_outtype == OUT_TSV) {
1525     sprintf(tmpbuf, "%s\n", grn_get_version());
1526   } else {
1527     sprintf(tmpbuf, "  \"VERSION\": \"%s\"\n", grn_get_version());
1528   }
1529   grn_strcat(result, olen, tmpbuf);
1530 
1531   if (grntest_outtype != OUT_TSV) {
1532     grn_strcat(result, olen, "},");
1533   }
1534 #endif /* WIN32 */
1535   if (strlen(result) >= olen) {
1536     fprintf(stderr, "buffer overrun in get_sysinfo!\n");
1537     exit(1);
1538   }
1539   return 0;
1540 }
1541 
1542 static int
start_server(const char * dbpath,int r)1543 start_server(const char *dbpath, int r)
1544 {
1545   int ret;
1546   char optbuf[BUF_LEN];
1547 #ifdef WIN32
1548   char tmpbuf[BUF_LEN];
1549 
1550   STARTUPINFO si;
1551 
1552   if (strlen(dbpath) > BUF_LEN - 100) {
1553     fprintf(stderr, "too long dbpath!\n");
1554     exit(1);
1555   }
1556 
1557   grn_strcpy(tmpbuf, BUF_LEN, groonga_path);
1558   grn_strcat(tmpbuf, BUF_LEN, " -s --protocol ");
1559   grn_strcat(tmpbuf, BUF_LEN, groonga_protocol);
1560   grn_strcat(tmpbuf, BUF_LEN, " -p ");
1561   sprintf(optbuf, "%d ", grntest_serverport);
1562   grn_strcat(tmpbuf, BUF_LEN, optbuf);
1563   grn_strcat(tmpbuf, BUF_LEN, dbpath);
1564   memset(&si, 0, sizeof(STARTUPINFO));
1565   si.cb=sizeof(STARTUPINFO);
1566   ret = CreateProcess(NULL, tmpbuf, NULL, NULL, FALSE,
1567 		      0, NULL, NULL, &si, &grntest_pi);
1568 
1569   if (ret == 0) {
1570     fprintf(stderr, "Cannot start groonga server: <%s>: error=%lu\n",
1571             groonga_path, GetLastError());
1572     exit(1);
1573   }
1574 
1575 #else
1576   pid_t pid;
1577   pid = fork();
1578   if (pid < 0) {
1579     fprintf(stderr, "Cannot start groonga server:Cannot fork\n");
1580     exit(1);
1581   }
1582   sprintf(optbuf, "%d", grntest_serverport);
1583   if (pid == 0) {
1584     ret = execlp(groonga_path, groonga_path,
1585                  "-s",
1586                  "--protocol", groonga_protocol,
1587                  "-p", optbuf,
1588                  dbpath, (char*)NULL);
1589     if (ret == -1) {
1590       fprintf(stderr, "Cannot start groonga server: <%s>: errno=%d\n",
1591               groonga_path, errno);
1592       exit(1);
1593     }
1594   }
1595   else {
1596     grntest_server_id = pid;
1597   }
1598 
1599 #endif /* WIN32 */
1600 
1601   return 0;
1602 }
1603 
1604 static int
parse_line(grn_ctx * ctx,char * buf,int start,int end,int num)1605 parse_line(grn_ctx *ctx, char *buf, int start, int end, int num)
1606 {
1607   int i, j, error_flag = 0, out_or_test = 0;
1608   char tmpbuf[BUF_LEN];
1609 
1610   grntest_job[num].concurrency = 1;
1611   grntest_job[num].ntimes = 1;
1612   grntest_job[num].done = 0;
1613   grntest_job[num].qnum = 0;
1614   grntest_job[num].max = 0LL;
1615   grntest_job[num].min = 9223372036854775807LL;
1616   grntest_job[num].outputlog = NULL;
1617   grntest_job[num].inputlog = NULL;
1618 
1619   strncpy(grntest_job[num].jobname, &buf[start], end - start);
1620   grntest_job[num].jobname[end - start] = '\0';
1621   i = start;
1622   while (i < end) {
1623     if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1624       i++;
1625       continue;
1626     }
1627     if (!strncmp(&buf[i], "do_local", 8)) {
1628       grntest_job[num].jobtype = J_DO_LOCAL;
1629       i = i + 8;
1630       break;
1631     }
1632     if (!strncmp(&buf[i], "do_gqtp", 7)) {
1633       grntest_job[num].jobtype = J_DO_GQTP;
1634       i = i + 7;
1635       break;
1636     }
1637     if (!strncmp(&buf[i], "do_http", 7)) {
1638       grntest_job[num].jobtype = J_DO_HTTP;
1639       i = i + 7;
1640       break;
1641     }
1642     if (!strncmp(&buf[i], "rep_local", 9)) {
1643       grntest_job[num].jobtype = J_REP_LOCAL;
1644       i = i + 9;
1645       break;
1646     }
1647     if (!strncmp(&buf[i], "rep_gqtp", 8)) {
1648       grntest_job[num].jobtype = J_REP_GQTP;
1649       i = i + 8;
1650       break;
1651     }
1652     if (!strncmp(&buf[i], "rep_http", 8)) {
1653       grntest_job[num].jobtype = J_REP_HTTP;
1654       i = i + 8;
1655       break;
1656     }
1657     if (!strncmp(&buf[i], "out_local", 9)) {
1658       grntest_job[num].jobtype = J_OUT_LOCAL;
1659       i = i + 9;
1660       out_or_test = 1;
1661       break;
1662     }
1663     if (!strncmp(&buf[i], "out_gqtp", 8)) {
1664       grntest_job[num].jobtype = J_OUT_GQTP;
1665       i = i + 8;
1666       out_or_test = 1;
1667       break;
1668     }
1669     if (!strncmp(&buf[i], "out_http", 8)) {
1670       grntest_job[num].jobtype = J_OUT_HTTP;
1671       i = i + 8;
1672       out_or_test = 1;
1673       break;
1674     }
1675     if (!strncmp(&buf[i], "test_local", 10)) {
1676       grntest_job[num].jobtype = J_TEST_LOCAL;
1677       i = i + 10;
1678       out_or_test = 1;
1679       break;
1680     }
1681     if (!strncmp(&buf[i], "test_gqtp", 9)) {
1682       grntest_job[num].jobtype = J_TEST_GQTP;
1683       i = i + 9;
1684       out_or_test = 1;
1685       break;
1686     }
1687     if (!strncmp(&buf[i], "test_http", 9)) {
1688       grntest_job[num].jobtype = J_TEST_HTTP;
1689       i = i + 9;
1690       out_or_test = 1;
1691       break;
1692     }
1693     error_flag = 1;
1694     i++;
1695   }
1696 
1697   if (error_flag) {
1698     return 3;
1699   }
1700   if (i == end) {
1701     return 1;
1702   }
1703 
1704   if (grn_isspace(&buf[i], GRN_ENC_UTF8) != 1) {
1705     return 4;
1706   }
1707   i++;
1708 
1709   while (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1710     i++;
1711     continue;
1712   }
1713   j = 0;
1714   while (i < end) {
1715     if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1716       break;
1717     }
1718     grntest_job[num].commandfile[j] = buf[i];
1719     i++;
1720     j++;
1721     if (j > 255) {
1722       return 5;
1723     }
1724   }
1725   grntest_job[num].commandfile[j] = '\0';
1726 
1727   while (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1728     i++;
1729   }
1730 
1731   if (i == end) {
1732     if (out_or_test) {
1733       fprintf(stderr, "log(test)_local(gqtp|http) needs log(test)_filename\n");
1734       return 11;
1735     }
1736     return 0;
1737   }
1738 
1739   j = 0;
1740   while (i < end) {
1741     if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1742       break;
1743     }
1744     tmpbuf[j] = buf[i];
1745     i++;
1746     j++;
1747     if (j >= BUF_LEN) {
1748       return 6;
1749     }
1750   }
1751   tmpbuf[j] ='\0';
1752   if (out_or_test) {
1753     if (out_p(grntest_job[num].jobtype)) {
1754       grntest_job[num].outputlog = fopen(tmpbuf, "wb");
1755       if (grntest_job[num].outputlog == NULL) {
1756         fprintf(stderr, "Cannot open %s\n", tmpbuf);
1757         return 13;
1758       }
1759     } else {
1760       char outlog[BUF_LEN];
1761       grntest_job[num].inputlog = grn_file_reader_open(ctx, tmpbuf);
1762       if (grntest_job[num].inputlog == NULL) {
1763         fprintf(stderr, "Cannot open %s\n", tmpbuf);
1764         return 14;
1765       }
1766       sprintf(outlog, "%s.diff", tmpbuf);
1767       grntest_job[num].outputlog = fopen(outlog, "wb");
1768       if (grntest_job[num].outputlog == NULL) {
1769         fprintf(stderr, "Cannot open %s\n", outlog);
1770         return 15;
1771       }
1772     }
1773     grn_strcpy(grntest_job[num].logfile, BUF_LEN, tmpbuf);
1774     return 0;
1775   } else {
1776     grntest_job[num].concurrency = grntest_atoi(tmpbuf, tmpbuf + j, NULL);
1777     if (grntest_job[num].concurrency == 0) {
1778       return 7;
1779     }
1780   }
1781 
1782   while (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1783     i++;
1784   }
1785 
1786   if (i == end) {
1787     return 0;
1788   }
1789 
1790   j = 0;
1791   while (i < end) {
1792     if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1793       break;
1794     }
1795     tmpbuf[j] = buf[i];
1796     i++;
1797     j++;
1798     if (j > 16) {
1799       return 8;
1800     }
1801   }
1802   tmpbuf[j] ='\0';
1803   grntest_job[num].ntimes = grntest_atoi(tmpbuf, tmpbuf + j, NULL);
1804   if (grntest_job[num].ntimes == 0) {
1805     return 9;
1806   }
1807   if (i == end) {
1808     return 0;
1809   }
1810 
1811   while (i < end) {
1812     if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1813       i++;
1814       continue;
1815     }
1816     return 10;
1817   }
1818   return 0;
1819 }
1820 
1821 static int
get_jobs(grn_ctx * ctx,char * buf,int line)1822 get_jobs(grn_ctx *ctx, char *buf, int line)
1823 {
1824   int i, len, start, end, ret;
1825   int jnum = 0;
1826 
1827   len = strlen(buf);
1828   i = 0;
1829   while (i < len) {
1830     if ((buf[i] == '#') || (buf[i] == '\r') || (buf[i] == '\n')) {
1831       buf[i] = '\0';
1832       len = i;
1833       break;
1834     }
1835     i++;
1836   }
1837 
1838   i = 0;
1839   start = 0;
1840   while (i < len) {
1841     if (buf[i] == ';') {
1842       end = i;
1843       ret = parse_line(ctx, buf, start, end, jnum);
1844       if (ret) {
1845         if (ret > 1) {
1846           fprintf(stderr, "Syntax error:line=%d:ret=%d:%s\n", line, ret, buf);
1847           error_exit(ctx, 1);
1848         }
1849       } else {
1850         jnum++;
1851       }
1852       start = end + 1;
1853     }
1854     i++;
1855   }
1856   end = len;
1857   ret = parse_line(ctx, buf, start, end, jnum);
1858   if (ret) {
1859     if (ret > 1) {
1860       fprintf(stderr, "Syntax error:line=%d:ret=%d:%s\n", line, ret, buf);
1861       error_exit(ctx, 1);
1862     }
1863   } else {
1864     jnum++;
1865   }
1866   return jnum;
1867 }
1868 
1869 static int
make_task_table(grn_ctx * ctx,int jobnum)1870 make_task_table(grn_ctx *ctx, int jobnum)
1871 {
1872   int i, j;
1873   int tid = 0;
1874   grn_obj *commands = NULL;
1875 
1876   for (i = 0; i < jobnum; i++) {
1877     if ((grntest_job[i].concurrency == 1) && (!grntest_onmemory_mode)) {
1878       grntest_task[tid].file = grntest_job[i].commandfile;
1879       grntest_task[tid].commands = NULL;
1880       grntest_task[tid].ntimes = grntest_job[i].ntimes;
1881       grntest_task[tid].jobtype = grntest_job[i].jobtype;
1882       grntest_task[tid].job_id = i;
1883       tid++;
1884       continue;
1885     }
1886     for (j = 0; j < grntest_job[i].concurrency; j++) {
1887       if (j == 0) {
1888         grn_file_reader *reader;
1889         grn_obj line;
1890         GRN_TEXT_INIT(&line, 0);
1891         commands = grn_obj_open(ctx, GRN_PVECTOR, 0, GRN_VOID);
1892         if (!commands) {
1893           fprintf(stderr, "Cannot alloc commands\n");
1894           error_exit(ctx, 1);
1895         }
1896         reader = grn_file_reader_open(ctx, grntest_job[i].commandfile);
1897         if (!reader) {
1898           fprintf(stderr, "Cannot alloc commandfile:%s\n",
1899                    grntest_job[i].commandfile);
1900           error_exit(ctx, 1);
1901         }
1902         while (grn_file_reader_read_line(ctx, reader, &line) == GRN_SUCCESS) {
1903           grn_obj *command;
1904           if (GRN_TEXT_VALUE(&line)[GRN_TEXT_LEN(&line) - 1] == '\n') {
1905             grn_bulk_truncate(ctx, &line, GRN_TEXT_LEN(&line) - 1);
1906           }
1907           if (GRN_TEXT_LEN(&line) == 0) {
1908             GRN_BULK_REWIND(&line);
1909             continue;
1910           }
1911           GRN_TEXT_PUTC(ctx, &line, '\0');
1912           if (comment_p(GRN_TEXT_VALUE(&line))) {
1913             GRN_BULK_REWIND(&line);
1914             continue;
1915           }
1916           command = grn_obj_open(ctx, GRN_BULK, 0, GRN_VOID);
1917           if (!command) {
1918             fprintf(stderr, "Cannot alloc command: %s: %s\n",
1919                     grntest_job[i].commandfile, GRN_TEXT_VALUE(&line));
1920             GRN_OBJ_FIN(ctx, &line);
1921             error_exit(ctx, 1);
1922           }
1923           GRN_TEXT_SET(ctx, command, GRN_TEXT_VALUE(&line), GRN_TEXT_LEN(&line));
1924           GRN_PTR_PUT(ctx, commands, command);
1925           GRN_BULK_REWIND(&line);
1926         }
1927         grn_file_reader_close(ctx, reader);
1928         GRN_OBJ_FIN(ctx, &line);
1929       }
1930       grntest_task[tid].file = NULL;
1931       grntest_task[tid].commands = commands;
1932       grntest_task[tid].ntimes = grntest_job[i].ntimes;
1933       grntest_task[tid].jobtype = grntest_job[i].jobtype;
1934       grntest_task[tid].job_id = i;
1935       tid++;
1936     }
1937   }
1938   return tid;
1939 }
1940 
1941 /*
1942 static int
1943 print_commandlist(int task_id)
1944 {
1945   int i;
1946 
1947   for (i = 0; i < GRN_TEXT_LEN(grntest_task[task_id].commands); i++) {
1948     grn_obj *command;
1949     command = GRN_PTR_VALUE_AT(grntest_task[task_id].commands, i);
1950     printf("%s\n", GRN_TEXT_VALUE(command));
1951   }
1952   return 0;
1953 }
1954 */
1955 
1956 /* return num of query */
1957 static int
do_jobs(grn_ctx * ctx,int jobnum,int line)1958 do_jobs(grn_ctx *ctx, int jobnum, int line)
1959 {
1960   int i, task_num, ret, qnum = 0, thread_num = 0;
1961 
1962   for (i = 0; i < jobnum; i++) {
1963 /*
1964 printf("%d:type =%d:file=%s:con=%d:ntimes=%d\n", i, grntest_job[i].jobtype,
1965         grntest_job[i].commandfile, JobTable[i].concurrency, JobTable[i].ntimes);
1966 
1967 */
1968     thread_num = thread_num + grntest_job[i].concurrency;
1969   }
1970 
1971   if (thread_num >= MAX_CON) {
1972     fprintf(stderr, "Too many threads requested(MAX=64):line=%d\n", line);
1973     error_exit(ctx, 1);
1974   }
1975 
1976   task_num = make_task_table(ctx, jobnum);
1977   if (task_num != thread_num) {
1978     fprintf(stderr, "Logical error\n");
1979     error_exit(ctx, 9);
1980   }
1981 
1982   grntest_detail_on = 0;
1983   for (i = 0; i < task_num; i++) {
1984     grn_ctx_init(&grntest_ctx[i], 0);
1985     grntest_owndb[i] = NULL;
1986     if (gqtp_p(grntest_task[i].jobtype)) {
1987       ret = grn_ctx_connect(&grntest_ctx[i], grntest_serverhost, grntest_serverport, 0);
1988       if (ret) {
1989         fprintf(stderr, "Cannot connect groonga server:host=%s:port=%d:ret=%d\n",
1990                 grntest_serverhost, grntest_serverport, ret);
1991         error_exit(ctx, 1);
1992       }
1993     } else if (http_p(grntest_task[i].jobtype)) {
1994       grntest_task[i].http_socket = 0;
1995       GRN_TEXT_INIT(&grntest_task[i].http_response, 0);
1996       if (grntest_owndb_mode) {
1997         grntest_owndb[i] = grn_db_open(&grntest_ctx[i], grntest_dbpath);
1998         if (grntest_owndb[i] == NULL) {
1999           fprintf(stderr, "Cannot open db:%s\n", grntest_dbpath);
2000           exit(1);
2001         }
2002       } else {
2003         grntest_owndb[i] = grn_db_create(&grntest_ctx[i], NULL, NULL);
2004       }
2005     } else {
2006       if (grntest_owndb_mode) {
2007         grntest_owndb[i] = grn_db_open(&grntest_ctx[i], grntest_dbpath);
2008         if (grntest_owndb[i] == NULL) {
2009           fprintf(stderr, "Cannot open db:%s\n", grntest_dbpath);
2010           exit(1);
2011         }
2012       }
2013       else {
2014         grn_ctx_use(&grntest_ctx[i], grntest_db);
2015       }
2016     }
2017     if (report_p(grntest_task[i].jobtype)) {
2018       grntest_detail_on++;
2019     }
2020   }
2021   if (grntest_detail_on) {
2022     if (grntest_outtype == OUT_TSV) {
2023       ;
2024     }
2025     else {
2026       fprintf(grntest_log_file, "\"detail\": [\n");
2027     }
2028     fflush(grntest_log_file);
2029   }
2030 
2031   thread_main(ctx, task_num);
2032 
2033   for (i = 0; i < task_num; i++) {
2034     if (grntest_owndb[i]) {
2035       grn_obj_close(&grntest_ctx[i], grntest_owndb[i]);
2036     }
2037     if (http_p(grntest_task[i].jobtype)) {
2038       GRN_OBJ_FIN(&grntest_ctx[i], &grntest_task[i].http_response);
2039     }
2040     grn_ctx_fin(&grntest_ctx[i]);
2041     qnum = qnum + grntest_task[i].qnum;
2042   }
2043 
2044   i = 0;
2045   while (i < task_num) {
2046     int job_id;
2047     if (grntest_task[i].commands) {
2048       job_id = grntest_task[i].job_id;
2049       GRN_OBJ_FIN(ctx, grntest_task[i].commands);
2050       while (job_id == grntest_task[i].job_id) {
2051         i++;
2052       }
2053     } else {
2054       i++;
2055     }
2056   }
2057   for (i = 0; i < jobnum; i++) {
2058     if (grntest_job[i].outputlog) {
2059       int ret;
2060       ret = fclose(grntest_job[i].outputlog);
2061       if (ret) {
2062         fprintf(stderr, "Cannot close %s\n", grntest_job[i].logfile);
2063         exit(1);
2064       }
2065     }
2066     if (grntest_job[i].inputlog) {
2067       grn_file_reader_close(ctx, grntest_job[i].inputlog);
2068     }
2069   }
2070   return qnum;
2071 }
2072 
2073 /* return num of query */
2074 static int
do_script(grn_ctx * ctx,const char * script_file_path)2075 do_script(grn_ctx *ctx, const char *script_file_path)
2076 {
2077   int n_lines = 0;
2078   int n_jobs;
2079   int n_queries, total_n_queries = 0;
2080   grn_file_reader *script_file;
2081   grn_obj line;
2082 
2083   script_file = grn_file_reader_open(ctx, script_file_path);
2084   if (script_file == NULL) {
2085     fprintf(stderr, "Cannot open script file: <%s>\n", script_file_path);
2086     error_exit(ctx, 1);
2087   }
2088 
2089   GRN_TEXT_INIT(&line, 0);
2090   while (grn_file_reader_read_line(ctx, script_file, &line) == GRN_SUCCESS) {
2091     if (grntest_sigint) {
2092       break;
2093     }
2094     n_lines++;
2095     grntest_jobdone = 0;
2096     n_jobs = get_jobs(ctx, GRN_TEXT_VALUE(&line), n_lines);
2097     grntest_jobnum = n_jobs;
2098 
2099     if (n_jobs > 0) {
2100       GRN_TIME_INIT(&grntest_jobs_start, 0);
2101       GRN_TIME_NOW(ctx, &grntest_jobs_start);
2102       if (grntest_outtype == OUT_TSV) {
2103         fprintf(grntest_log_file, "jobs-start\t%s\n", GRN_TEXT_VALUE(&line));
2104       } else {
2105         fprintf(grntest_log_file, "{\"jobs\": \"%s\",\n", GRN_TEXT_VALUE(&line));
2106       }
2107       n_queries = do_jobs(ctx, n_jobs, n_lines);
2108       if (grntest_outtype == OUT_TSV) {
2109         fprintf(grntest_log_file, "jobs-end\t%s\n", GRN_TEXT_VALUE(&line));
2110       } else {
2111         fprintf(grntest_log_file, "},\n");
2112       }
2113       total_n_queries += n_queries;
2114 
2115       grn_obj_close(ctx, &grntest_jobs_start);
2116     }
2117     if (grntest_stop_flag) {
2118       fprintf(stderr, "Error:Quit\n");
2119       break;
2120     }
2121     GRN_BULK_REWIND(&line);
2122   }
2123   grn_obj_unlink(ctx, &line);
2124 
2125   grn_file_reader_close(ctx, script_file);
2126 
2127   return total_n_queries;
2128 }
2129 
2130 static int
start_local(grn_ctx * ctx,const char * dbpath)2131 start_local(grn_ctx *ctx, const char *dbpath)
2132 {
2133   grntest_db = grn_db_open(ctx, dbpath);
2134   if (!grntest_db) {
2135     grntest_db = grn_db_create(ctx, dbpath, NULL);
2136   }
2137   if (!grntest_db) {
2138     fprintf(stderr, "Cannot open db:%s\n", dbpath);
2139     exit(1);
2140   }
2141   return 0;
2142 }
2143 
2144 static int
check_server(grn_ctx * ctx)2145 check_server(grn_ctx *ctx)
2146 {
2147   int ret, retry = 0;
2148   while (1) {
2149     ret = grn_ctx_connect(ctx, grntest_serverhost, grntest_serverport, 0);
2150     if (ret == GRN_CONNECTION_REFUSED) {
2151       grn_sleep(1);
2152       retry++;
2153       if (retry > 5) {
2154         fprintf(stderr, "Cannot connect groonga server:host=%s:port=%d:ret=%d\n",
2155                 grntest_serverhost, grntest_serverport, ret);
2156         return 1;
2157       }
2158       continue;
2159     }
2160     if (ret) {
2161       fprintf(stderr, "Cannot connect groonga server:host=%s:port=%d:ret=%d\n",
2162               grntest_serverhost, grntest_serverport, ret);
2163       return 1;
2164     }
2165     break;
2166   }
2167   return 0;
2168 }
2169 
2170 #define MODE_LIST 1
2171 #define MODE_GET  2
2172 #define MODE_PUT  3
2173 #define MODE_TIME 4
2174 
2175 static int
check_response(char * buf)2176 check_response(char *buf)
2177 {
2178   if (buf[0] == '1') {
2179     return 1;
2180   }
2181   if (buf[0] == '2') {
2182     return 1;
2183   }
2184   if (buf[0] == '3') {
2185     return 1;
2186   }
2187   return 0;
2188 }
2189 
2190 static int
read_response(socket_t socket,char * buf)2191 read_response(socket_t socket, char *buf)
2192 {
2193   int ret;
2194   ret = recv(socket, buf, BUF_LEN - 1, 0);
2195   if (ret == -1) {
2196     fprintf(stderr, "recv error:3\n");
2197     exit(1);
2198   }
2199   buf[ret] ='\0';
2200 #ifdef DEBUG_FTP
2201   fprintf(stderr, "recv:%s", buf);
2202 #endif
2203   return ret;
2204 }
2205 
2206 static int
put_file(socket_t socket,const char * filename)2207 put_file(socket_t socket, const char *filename)
2208 {
2209   FILE *fp;
2210   int c, ret, size = 0;
2211   char buf[1];
2212 
2213   fp = fopen(filename, "rb");
2214   if (!fp) {
2215     fprintf(stderr, "LOCAL:no such file:%s\n", filename);
2216     return 0;
2217   }
2218 
2219   while ((c = fgetc(fp)) != EOF) {
2220     buf[0] = c;
2221     ret = send(socket, buf, 1, 0);
2222     if (ret == -1) {
2223       fprintf(stderr, "send error\n");
2224       exit(1);
2225     }
2226     size++;
2227   }
2228   fclose(fp);
2229   return size;
2230 }
2231 
2232 static int
ftp_list(socket_t data_socket)2233 ftp_list(socket_t data_socket)
2234 {
2235   int ret;
2236   char buf[BUF_LEN];
2237 
2238   while (1) {
2239     ret = recv(data_socket, buf, BUF_LEN - 2, 0);
2240     if (ret == 0) {
2241       fflush(stdout);
2242       return 0;
2243     }
2244     buf[ret] = '\0';
2245     fprintf(stdout, "%s", buf);
2246   }
2247 
2248   return 0;
2249 }
2250 
2251 static int
get_file(socket_t socket,const char * filename,int size)2252 get_file(socket_t socket, const char *filename, int size)
2253 {
2254   FILE *fp;
2255   int ret, total;
2256   char buf[FTPBUF];
2257 
2258   fp = fopen(filename, "wb");
2259   if (!fp) {
2260     fprintf(stderr, "Cannot open %s\n",  filename);
2261     return -1;
2262   }
2263 
2264   total = 0;
2265   while (total != size) {
2266     ret = recv(socket, buf, FTPBUF, 0);
2267     if (ret == -1) {
2268       fprintf(stderr, "recv error:2:ret=%d:size=%d:total\n", ret, size);
2269       return -1;
2270     }
2271     if (ret == 0) {
2272       break;
2273     }
2274     fwrite(buf, ret, 1, fp);
2275     total = total + ret;
2276   }
2277 
2278   fclose(fp);
2279   return size;
2280 }
2281 
2282 static int
get_port(char * buf,char * host,int * port)2283 get_port(char *buf, char *host, int *port)
2284 {
2285   int ret,d1,d2,d3,d4,d5,d6;
2286   ret = sscanf(buf, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)",
2287          &d1, &d2, &d3, &d4, &d5, &d6);
2288   if (ret != 6) {
2289     fprintf(stderr, "Cannot enter passsive mode\n");
2290     return 0;
2291   }
2292 
2293   *port = d5 * 256 + d6;
2294   sprintf(host, "%d.%d.%d.%d", d1, d2, d3, d4);
2295   return 1;
2296 }
2297 
2298 static char *
get_ftp_date(char * buf)2299 get_ftp_date(char *buf)
2300 {
2301   while (*buf !=' ') {
2302     buf++;
2303     if (*buf == '\0') {
2304       return NULL;
2305     }
2306   }
2307   buf++;
2308 
2309   return buf;
2310 }
2311 
2312 static int
get_size(char * buf)2313 get_size(char *buf)
2314 {
2315   int size;
2316 
2317   while (*buf !='(') {
2318     buf++;
2319     if (*buf == '\0') {
2320       return 0;
2321     }
2322   }
2323   buf++;
2324   size = grntest_atoi(buf, buf + strlen(buf), NULL);
2325 
2326   return size;
2327 }
2328 
2329 int
ftp_sub(const char * user,const char * passwd,const char * host,const char * filename,int mode,const char * cd_dirname,char * retval)2330 ftp_sub(const char *user, const char *passwd, const char *host,
2331         const char *filename, int mode,
2332         const char *cd_dirname, char *retval)
2333 {
2334   int size = 0;
2335   int status = 0;
2336   socket_t command_socket, data_socket;
2337   int data_port;
2338   char data_host[BUF_LEN];
2339   char send_mesg[BUF_LEN];
2340   char buf[BUF_LEN];
2341 #ifdef WIN32
2342   char base[BUF_LEN];
2343   char fname[BUF_LEN];
2344   char ext[BUF_LEN];
2345 #else
2346   char *base;
2347 #endif /* WIN32 */
2348 
2349 #ifdef WIN32
2350   WSADATA ws;
2351 
2352   WSAStartup(MAKEWORD(2,0), &ws);
2353 #endif /* WIN32 */
2354 
2355   if ((filename != NULL) && (strlen(filename) >= MAX_PATH_LEN)) {
2356     fprintf(stderr, "too long filename\n");
2357     exit(1);
2358   }
2359 
2360   if ((cd_dirname != NULL) && (strlen(cd_dirname) >= MAX_PATH_LEN)) {
2361     fprintf(stderr, "too long dirname\n");
2362     exit(1);
2363   }
2364 
2365   command_socket = open_socket(host, 21);
2366   if (command_socket == SOCKETERROR) {
2367     return 0;
2368   }
2369 
2370   read_response(command_socket, buf);
2371   if (!check_response(buf)) {
2372     goto exit;
2373   }
2374 
2375   /* send username */
2376   sprintf(send_mesg, "USER %s\r\n", user);
2377   write_to_server(command_socket, send_mesg);
2378   read_response(command_socket, buf);
2379   if (!check_response(buf)) {
2380     goto exit;
2381   }
2382 
2383   /* send passwd */
2384   sprintf(send_mesg, "PASS %s\r\n", passwd);
2385   write_to_server(command_socket, send_mesg);
2386   read_response(command_socket, buf);
2387   if (!check_response(buf)) {
2388     goto exit;
2389   }
2390 
2391   /* send TYPE I */
2392   sprintf(send_mesg, "TYPE I\r\n");
2393   write_to_server(command_socket, send_mesg);
2394   read_response(command_socket, buf);
2395   if (!check_response(buf)) {
2396     goto exit;
2397   }
2398 
2399   /* send PASV */
2400   sprintf(send_mesg, "PASV\r\n");
2401   write_to_server(command_socket, send_mesg);
2402   read_response(command_socket, buf);
2403   if (!check_response(buf)) {
2404     goto exit;
2405   }
2406 
2407   if (!get_port(buf, data_host, &data_port)) {
2408     goto exit;
2409   }
2410 
2411   data_socket = open_socket(data_host, data_port);
2412   if (data_socket == SOCKETERROR) {
2413     goto exit;
2414   }
2415 
2416   if (cd_dirname) {
2417     sprintf(send_mesg, "CWD %s\r\n", cd_dirname);
2418     write_to_server(command_socket, send_mesg);
2419   }
2420 
2421   read_response(command_socket, buf);
2422   if (!check_response(buf)) {
2423     socketclose(data_socket);
2424     goto exit;
2425   }
2426 
2427 #ifdef WIN32
2428   _splitpath(filename, NULL, NULL, fname, ext);
2429   grn_strcpy(base, BUF_LEN, fname);
2430   strcat(base, ext);
2431 #else
2432   grn_strcpy(buf, BUF_LEN, filename);
2433   base = basename(buf);
2434 #endif /* WIN32 */
2435 
2436   switch (mode) {
2437   case MODE_LIST:
2438     if (filename) {
2439       sprintf(send_mesg, "LIST %s\r\n", filename);
2440     } else {
2441       sprintf(send_mesg, "LIST \r\n");
2442     }
2443     write_to_server(command_socket, send_mesg);
2444     break;
2445   case MODE_PUT:
2446     sprintf(send_mesg, "STOR %s\r\n", base);
2447     write_to_server(command_socket, send_mesg);
2448     break;
2449   case MODE_GET:
2450     sprintf(send_mesg, "RETR %s\r\n", base);
2451     write_to_server(command_socket, send_mesg);
2452     break;
2453   case MODE_TIME:
2454     sprintf(send_mesg, "MDTM %s\r\n", base);
2455     write_to_server(command_socket, send_mesg);
2456     break;
2457   default:
2458     fprintf(stderr, "invalid mode\n");
2459     socketclose(data_socket);
2460     goto exit;
2461   }
2462 
2463   read_response(command_socket, buf);
2464   if (!check_response(buf)) {
2465     socketclose(data_socket);
2466     goto exit;
2467   }
2468   if (!strncmp(buf, "150", 3)) {
2469     size = get_size(buf);
2470   }
2471   if (!strncmp(buf, "213", 3)) {
2472     retval[BUF_LEN-2] = '\0';
2473     grn_strcpy(retval, BUF_LEN - 2, get_ftp_date(buf));
2474     if (retval[BUF_LEN-2] != '\0' ) {
2475       fprintf(stderr, "buffer over run in ftp\n");
2476       exit(1);
2477     }
2478   }
2479 
2480   switch (mode) {
2481   case MODE_LIST:
2482     ftp_list(data_socket);
2483     break;
2484   case MODE_GET:
2485     if (get_file(data_socket, filename, size) == -1) {
2486       socketclose(data_socket);
2487       goto exit;
2488     }
2489     fprintf(stderr, "get:%s\n", filename);
2490     break;
2491   case MODE_PUT:
2492     if (put_file(data_socket, filename) == -1) {
2493       socketclose(data_socket);
2494       goto exit;
2495     }
2496     fprintf(stderr, "put:%s\n", filename);
2497     break;
2498   default:
2499     break;
2500   }
2501 
2502   socketclose(data_socket);
2503   if ((mode == MODE_GET) || (mode == MODE_PUT)) {
2504     read_response(command_socket, buf);
2505   }
2506   write_to_server(command_socket, "QUIT\n");
2507   status = 1;
2508 exit:
2509   socketclose(command_socket);
2510 
2511 #ifdef WIN32
2512   WSACleanup();
2513 #endif
2514   return status;
2515 }
2516 
2517 /*
2518 static int
2519 ftp_main(int argc, char **argv)
2520 {
2521   char val[BUF_LEN];
2522   val[0] = '\0';
2523   ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, argv[2],
2524           grntest_atoi(argv[3], argv[3] + strlen(argv[3]), NULL), argv[4], val);
2525   if (val[0] != '\0') {
2526     printf("val=%s\n", val);
2527   }
2528   return 0;
2529 }
2530 */
2531 
2532 static int
get_username(char * name,int maxlen)2533 get_username(char *name, int maxlen)
2534 {
2535   char *env=NULL;
2536   grn_strcpy(name, maxlen, "nobody");
2537 #ifdef WIN32
2538   env = getenv("USERNAME");
2539 #else
2540   env = getenv("USER");
2541 #endif /* WIN32 */
2542   if (strlen(env) > maxlen) {
2543     fprintf(stderr, "too long username:%s\n", env);
2544     exit(1);
2545   }
2546   if (env) {
2547     grn_strcpy(name, maxlen, env);
2548   }
2549   return 0;
2550 }
2551 
2552 static int
get_date(char * date,time_t * sec)2553 get_date(char *date, time_t *sec)
2554 {
2555 #if defined(WIN32) && !defined(__GNUC__)
2556   struct tm tmbuf;
2557   struct tm *tm = &tmbuf;
2558   localtime_s(tm, sec);
2559 #else /* defined(WIN32) && !defined(__GNUC__) */
2560 #  ifdef HAVE_LOCALTIME_R
2561   struct tm result;
2562   struct tm *tm = &result;
2563   localtime_r(sec, tm);
2564 #  else /* HAVE_LOCALTIME_R */
2565   struct tm *tm = localtime(sec);
2566 #  endif /* HAVE_LOCALTIME_R */
2567 #endif /* defined(WIN32) && !defined(__GNUC__) */
2568 
2569 #ifdef WIN32
2570   strftime(date, 128, "%Y-%m-%d %H:%M:%S", tm);
2571 #else
2572   strftime(date, 128, "%F %T", tm);
2573 #endif /* WIN32 */
2574 
2575   return 1;
2576 }
2577 
2578 static int
get_scriptname(const char * path,char * name,size_t name_len,const char * suffix)2579 get_scriptname(const char *path, char *name, size_t name_len, const char *suffix)
2580 {
2581   int slen = strlen(suffix);
2582   int len = strlen(path);
2583 
2584   if (len >= BUF_LEN) {
2585     fprintf(stderr, "too long script name\n");
2586     exit(1);
2587   }
2588   if (slen > len) {
2589     fprintf(stderr, "too long suffux\n");
2590     exit(1);
2591   }
2592 
2593   grn_strcpy(name, name_len, path);
2594   if (strncmp(&name[len-slen], suffix, slen)) {
2595     name[0] = '\0';
2596     return 0;
2597   }
2598   name[len-slen] = '\0';
2599   return 1;
2600 }
2601 
2602 #ifdef WIN32
2603 static int
get_tm_from_serverdate(char * serverdate,struct tm * tm)2604 get_tm_from_serverdate(char *serverdate, struct tm *tm)
2605 {
2606   int res;
2607   int year, month, day, hour, minute, second;
2608 
2609   res = sscanf(serverdate, "%4d%2d%2d%2d%2d%2d",
2610                &year, &month, &day, &hour, &minute, &second);
2611 
2612 /*
2613   printf("%d %d %d %d %d %d\n", year, month, day, hour, minute, second);
2614 */
2615 
2616   tm->tm_sec = second;
2617   tm->tm_min = minute;
2618   tm->tm_hour = hour;
2619   tm->tm_mday = day;
2620   tm->tm_mon = month - 1;
2621   tm->tm_year = year - 1900;
2622   tm->tm_isdst = -1;
2623 
2624   return 0;
2625 }
2626 #endif /* WIN32 */
2627 
2628 
2629 
2630 static int
sync_sub(grn_ctx * ctx,const char * filename)2631 sync_sub(grn_ctx *ctx, const char *filename)
2632 {
2633   int ret;
2634   char serverdate[BUF_LEN];
2635 #ifdef WIN32
2636   struct _stat statbuf;
2637 #else
2638   struct stat statbuf;
2639 #endif /* WIN32 */
2640   time_t st, lt;
2641   struct tm stm;
2642 
2643   ret = ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, filename, MODE_TIME, "data",
2644                serverdate);
2645   if (ret == 0) {
2646     fprintf(stderr, "[%s] does not exist in server\n", filename);
2647     return 0;
2648   }
2649 #ifdef WIN32
2650   get_tm_from_serverdate(serverdate, &stm);
2651 #else
2652   strptime(serverdate, "%Y%m%d %H%M%S", &stm);
2653 #endif /* WIN32 */
2654 
2655   /* fixme! needs timezone info */
2656   st = mktime(&stm) + 3600 * 9;
2657   lt = st;
2658 
2659 #ifdef WIN32
2660   ret = _stat(filename, &statbuf);
2661 #else
2662   ret = stat(filename, &statbuf);
2663 #endif /* WIN32 */
2664 
2665   if (!ret) {
2666     lt = statbuf.st_mtime;
2667     if (lt < st) {
2668       fprintf(stderr, "newer [%s] exists in server\n", filename);
2669       fflush(stderr);
2670       ret = ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, filename, MODE_GET, "data",
2671                     NULL);
2672       return ret;
2673     }
2674   } else {
2675     fprintf(stderr, "[%s] does not exist in local\n", filename);
2676     fflush(stderr);
2677     ret = ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, filename, MODE_GET, "data",
2678                   NULL);
2679     return ret;
2680   }
2681   return 0;
2682 }
2683 
2684 static int
cache_file(grn_ctx * ctx,char ** flist,char * file,int fnum)2685 cache_file(grn_ctx *ctx, char **flist, char *file, int fnum)
2686 {
2687   int i;
2688 
2689   for (i = 0; i < fnum; i++) {
2690     if (!strcmp(flist[i], file) ) {
2691       return fnum;
2692     }
2693   }
2694   flist[fnum] = GRN_STRDUP(file);
2695   fnum++;
2696   if (fnum >= BUF_LEN) {
2697     fprintf(stderr, "too many uniq commands file!\n");
2698     exit(1);
2699   }
2700   return fnum;
2701 }
2702 
2703 static int
sync_datafile(grn_ctx * ctx,const char * script_file_path)2704 sync_datafile(grn_ctx *ctx, const char *script_file_path)
2705 {
2706   int line = 0;
2707   int fnum = 0;
2708   int i, job_num;
2709   FILE *fp;
2710   char buf[BUF_LEN];
2711   char *filelist[BUF_LEN];
2712 
2713   fp = fopen(script_file_path, "r");
2714   if (fp == NULL) {
2715     fprintf(stderr, "Cannot open script file: <%s>\n", script_file_path);
2716     error_exit(ctx, 1);
2717   }
2718   buf[BUF_LEN-2] = '\0';
2719   while (fgets(buf, BUF_LEN, fp) != NULL) {
2720     line++;
2721     if (buf[BUF_LEN-2] != '\0') {
2722       fprintf(stderr, "Too long line in script file:%d\n", line);
2723       error_exit(ctx, 1);
2724     }
2725     job_num = get_jobs(ctx, buf, line);
2726 
2727     if (job_num > 0) {
2728       for (i = 0; i < job_num; i++) {
2729 /*
2730 printf("commandfile=[%s]:buf=%s\n", grntest_job[i].commandfile, buf);
2731 */
2732         fnum = cache_file(ctx, filelist, grntest_job[i].commandfile, fnum);
2733       }
2734     }
2735   }
2736   for (i = 0; i < fnum; i++) {
2737     if (sync_sub(ctx, filelist[i])) {
2738       fprintf(stderr, "updated!:%s\n", filelist[i]);
2739       fflush(stderr);
2740     }
2741     GRN_FREE(filelist[i]);
2742   }
2743   fclose(fp);
2744   return fnum;
2745 }
2746 
2747 static int
sync_script(grn_ctx * ctx,const char * filename)2748 sync_script(grn_ctx *ctx, const char *filename)
2749 {
2750   int ret, filenum;
2751 
2752   ret = sync_sub(ctx, filename);
2753   if (!ret) {
2754     return 0;
2755   }
2756 
2757   fprintf(stderr, "updated!:%s\n", filename);
2758   fflush(stderr);
2759   filenum = sync_datafile(ctx, filename);
2760   return 1;
2761 }
2762 
2763 static void
usage(void)2764 usage(void)
2765 {
2766   fprintf(stderr,
2767          "Usage: grntest [options...] [script] [db]\n"
2768          "options:\n"
2769          "  --dir:                     show script files on ftp server\n"
2770          "  -i, --host <ip/hostname>:  server address to listen (default: %s)\n"
2771          "  --localonly:               omit server connection\n"
2772          "  --log-output-dir:          specify output dir (default: current)\n"
2773          "  --ftp:                     connect to ftp server\n"
2774          "  --onmemory:                load all commands into memory\n"
2775          "  --output-type <tsv/json>:  specify output-type (default: json)\n"
2776          "  --owndb:                   open dbs for each ctx\n"
2777          "  -p, --port <port number>:  server port number (default: %d)\n"
2778          "  --groonga <groonga_path>:  groonga command path (default: %s)\n"
2779          "  --protocol <gqtp|http>:    groonga server protocol (default: %s)\n"
2780          "  --log-path <path>:         specify log file path\n"
2781          "  --pid-path <path>:         specify file path to store PID file\n",
2782           DEFAULT_DEST, DEFAULT_PORT,
2783           groonga_path, groonga_protocol);
2784   exit(1);
2785 }
2786 
2787 enum {
2788   mode_default = 0,
2789   mode_list,
2790   mode_usage,
2791 };
2792 
2793 #define MODE_MASK      0x007f
2794 #define MODE_FTP       0x0080
2795 #define MODE_LOCALONLY 0x0100
2796 #define MODE_OWNDB     0x0800
2797 #define MODE_ONMEMORY  0x1000
2798 
2799 
2800 static int
get_token(char * line,char * token,int maxlen,char ** next)2801 get_token(char *line, char *token, int maxlen, char **next)
2802 {
2803   int i = 0;
2804 
2805   *next = NULL;
2806   token[i] = '\0';
2807 
2808   while (*line) {
2809     if (grn_isspace(line, GRN_ENC_UTF8) == 1) {
2810       line++;
2811       continue;
2812     }
2813     if (*line == ';') {
2814       token[0] = ';';
2815       token[1] = '\0';
2816       *next = line + 1;
2817       return 1;
2818     }
2819     if (*line == '#') {
2820       token[0] = ';';
2821       token[1] = '\0';
2822       *next = line + 1;
2823       return 1;
2824     }
2825     break;
2826   }
2827 
2828   while (*line) {
2829     token[i] = *line;
2830     i++;
2831     if (grn_isspace(line + 1, GRN_ENC_UTF8) == 1) {
2832       token[i] = '\0';
2833       *next = line + 1;
2834       return 1;
2835     }
2836     if (*(line + 1) == ';') {
2837       token[i] = '\0';
2838       *next = line + 1;
2839       return 1;
2840     }
2841     if (*(line + 1) == '#') {
2842       token[i] = '\0';
2843       *next = line + 1;
2844       return 1;
2845     }
2846     if (*(line + 1) == '\0') {
2847       token[i] = '\0';
2848       return 1;
2849     }
2850 
2851     line++;
2852   }
2853   return 0;
2854 }
2855 
2856 /* SET_PORT and SET_HOST */
2857 static grn_bool
check_script(grn_ctx * ctx,const char * script_file_path)2858 check_script(grn_ctx *ctx, const char *script_file_path)
2859 {
2860   grn_file_reader *script_file;
2861   grn_obj line;
2862   char token[BUF_LEN];
2863   char prev[BUF_LEN];
2864   char *next = NULL;
2865 
2866   script_file = grn_file_reader_open(ctx, script_file_path);
2867   if (!script_file) {
2868     fprintf(stderr, "Cannot open script file: <%s>\n", script_file_path);
2869     return GRN_FALSE;
2870   }
2871 
2872   GRN_TEXT_INIT(&line, 0);
2873   while (grn_file_reader_read_line(ctx, script_file, &line) == GRN_SUCCESS) {
2874     GRN_TEXT_VALUE(&line)[GRN_TEXT_LEN(&line) - 1] = '\0';
2875     get_token(GRN_TEXT_VALUE(&line), token, BUF_LEN, &next);
2876     grn_strcpy(prev, BUF_LEN, token);
2877 
2878     while (next) {
2879       get_token(next, token, BUF_LEN, &next);
2880       if (!strncmp(prev, "SET_PORT", 8)) {
2881         grntest_serverport = grn_atoi(token, token + strlen(token), NULL);
2882       }
2883       if (!strncmp(prev, "SET_HOST", 8)) {
2884         grn_strcpy(grntest_serverhost, BUF_LEN, token);
2885         grntest_remote_mode = 1;
2886       }
2887       grn_strcpy(prev, BUF_LEN, token);
2888     }
2889   }
2890   grn_obj_unlink(ctx, &line);
2891 
2892   grn_file_reader_close(ctx, script_file);
2893   return GRN_TRUE;
2894 }
2895 
2896 #ifndef WIN32
2897 static void
timeout(int sig)2898 timeout(int sig)
2899 {
2900   fprintf(stderr, "timeout:groonga server cannot shutdown!!\n");
2901   fprintf(stderr, "Use \"kill -9 %d\"\n",  grntest_server_id);
2902   alarm(0);
2903 }
2904 
2905 static void
setexit(int sig)2906 setexit(int sig)
2907 {
2908   grntest_sigint = 1;
2909 }
2910 
2911 static int
setsigalarm(int sec)2912 setsigalarm(int sec)
2913 {
2914   int	ret;
2915   struct sigaction sig;
2916 
2917   alarm(sec);
2918   sig.sa_handler = timeout;
2919   sig.sa_flags = 0;
2920   sigemptyset(&sig.sa_mask);
2921   ret = sigaction(SIGALRM, &sig, NULL);
2922   if (ret == -1) {
2923     fprintf(stderr, "setsigalarm:errno= %d\n", errno);
2924   }
2925   return ret;
2926 }
2927 
2928 static int
setsigint(void)2929 setsigint(void)
2930 {
2931   int	ret;
2932   struct sigaction sig;
2933 
2934   sig.sa_handler = setexit;
2935   sig.sa_flags = 0;
2936   sigemptyset(&sig.sa_mask);
2937   ret = sigaction(SIGINT, &sig, NULL);
2938   if (ret == -1) {
2939     fprintf(stderr, "setsigint:errno= %d\n", errno);
2940   }
2941   return ret;
2942 }
2943 #endif /* WIN32 */
2944 
2945 int
main(int argc,char ** argv)2946 main(int argc, char **argv)
2947 {
2948   int qnum, i, mode = 0;
2949   int exit_code = EXIT_SUCCESS;
2950   grn_ctx context;
2951   char sysinfo[BUF_LEN];
2952   char log_path_buffer[BUF_LEN];
2953   const char *log_path = NULL;
2954   const char *pid_path = NULL;
2955   const char *portstr = NULL, *hoststr = NULL, *dbname = NULL, *scrname = NULL, *outdir = NULL, *outtype = NULL;
2956   time_t sec;
2957 
2958   static grn_str_getopt_opt opts[] = {
2959     {'i', "host", NULL, 0, GETOPT_OP_NONE},
2960     {'p', "port", NULL, 0, GETOPT_OP_NONE},
2961     {'\0', "log-output-dir", NULL, 0, GETOPT_OP_NONE},
2962     {'\0', "output-type", NULL, 0, GETOPT_OP_NONE},
2963     {'\0', "dir", NULL, mode_list, GETOPT_OP_UPDATE},
2964     {'\0', "ftp", NULL, MODE_FTP, GETOPT_OP_ON},
2965     {'h', "help", NULL, mode_usage, GETOPT_OP_UPDATE},
2966     {'\0', "localonly", NULL, MODE_LOCALONLY, GETOPT_OP_ON},
2967     {'\0', "onmemory", NULL, MODE_ONMEMORY, GETOPT_OP_ON},
2968     {'\0', "owndb", NULL, MODE_OWNDB, GETOPT_OP_ON},
2969     {'\0', "groonga", NULL, 0, GETOPT_OP_NONE},
2970     {'\0', "protocol", NULL, 0, GETOPT_OP_NONE},
2971     {'\0', "log-path", NULL, 0, GETOPT_OP_NONE},
2972     {'\0', "pid-path", NULL, 0, GETOPT_OP_NONE},
2973     {'\0', NULL, NULL, 0, 0}
2974   };
2975 
2976   opts[0].arg = &hoststr;
2977   opts[1].arg = &portstr;
2978   opts[2].arg = &outdir;
2979   opts[3].arg = &outtype;
2980   opts[10].arg = &groonga_path;
2981   opts[11].arg = &groonga_protocol;
2982   opts[12].arg = &log_path;
2983   opts[13].arg = &pid_path;
2984 
2985   i = grn_str_getopt(argc, argv, opts, &mode);
2986   if (i < 0) {
2987     usage();
2988   }
2989 
2990   switch (mode & MODE_MASK) {
2991   case mode_list :
2992     ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, "*.scr", 1, "data",
2993              NULL);
2994     return 0;
2995     break;
2996   case mode_usage :
2997     usage();
2998     break;
2999   default :
3000     break;
3001   }
3002 
3003   if (pid_path) {
3004     FILE *pid_file;
3005     pid_file = fopen(pid_path, "w");
3006     if (pid_file) {
3007       fprintf(pid_file, "%d", grn_getpid());
3008       fclose(pid_file);
3009     } else {
3010       fprintf(stderr,
3011               "failed to open PID file: <%s>: %s\n",
3012               pid_path, strerror(errno));
3013     }
3014   }
3015 
3016   if (i < argc) {
3017     scrname = argv[i];
3018   }
3019   if (i < argc - 1) {
3020     dbname = argv[i+1];
3021   }
3022   grntest_dbpath = dbname;
3023 
3024   if (mode & MODE_LOCALONLY) {
3025     grntest_localonly_mode = 1;
3026     grntest_remote_mode = 1;
3027   }
3028 
3029   if (mode & MODE_OWNDB) {
3030     grntest_localonly_mode = 1;
3031     grntest_remote_mode = 1;
3032     grntest_owndb_mode = 1;
3033   }
3034 
3035   if (mode & MODE_ONMEMORY) {
3036     grntest_onmemory_mode= 1;
3037   }
3038 
3039   if (mode & MODE_FTP) {
3040     grntest_ftp_mode = GRN_TRUE;
3041   }
3042 
3043   if ((scrname == NULL) || (dbname == NULL)) {
3044     usage();
3045   }
3046 
3047   grn_strcpy(grntest_serverhost, BUF_LEN, DEFAULT_DEST);
3048   if (hoststr) {
3049     grntest_remote_mode = 1;
3050     grn_strcpy(grntest_serverhost, BUF_LEN, hoststr);
3051   }
3052   grntest_serverport = DEFAULT_PORT;
3053   if (portstr) {
3054     grntest_serverport = grn_atoi(portstr, portstr + strlen(portstr), NULL);
3055   }
3056 
3057   if (outtype && !strcmp(outtype, "tsv")) {
3058     grntest_outtype = OUT_TSV;
3059   }
3060 
3061   grn_default_logger_set_path(GRN_LOG_PATH);
3062 
3063   grn_init();
3064   CRITICAL_SECTION_INIT(grntest_cs);
3065 
3066   grn_ctx_init(&context, 0);
3067   grn_ctx_init(&grntest_server_context, 0);
3068   grn_db_create(&grntest_server_context, NULL, NULL);
3069   grn_set_default_encoding(GRN_ENC_UTF8);
3070 
3071   if (grntest_ftp_mode) {
3072     sync_script(&context, scrname);
3073   }
3074   if (!check_script(&context, scrname)) {
3075     exit_code = EXIT_FAILURE;
3076     goto exit;
3077   }
3078 
3079   start_local(&context, dbname);
3080   if (!grntest_remote_mode) {
3081     start_server(dbname, 0);
3082   }
3083 
3084   if (!grntest_localonly_mode) {
3085     if (check_server(&grntest_server_context)) {
3086       goto exit;
3087     }
3088   }
3089 
3090   get_scriptname(scrname, grntest_scriptname, BUF_LEN, ".scr");
3091   get_username(grntest_username, 256);
3092 
3093   GRN_TIME_INIT(&grntest_starttime, 0);
3094   GRN_TIME_NOW(&context, &grntest_starttime);
3095   sec = (time_t)(GRN_TIME_VALUE(&grntest_starttime)/1000000);
3096   get_date(grntest_date, &sec);
3097 
3098   if (!log_path) {
3099     if (outdir) {
3100       sprintf(log_path_buffer,
3101               "%s/%s-%s-%" GRN_FMT_LLD "-%s.log", outdir, grntest_scriptname,
3102               grntest_username,
3103               GRN_TIME_VALUE(&grntest_starttime), grn_get_version());
3104     } else {
3105       sprintf(log_path_buffer,
3106               "%s-%s-%" GRN_FMT_LLD "-%s.log", grntest_scriptname,
3107               grntest_username,
3108               GRN_TIME_VALUE(&grntest_starttime), grn_get_version());
3109     }
3110     log_path = log_path_buffer;
3111   }
3112 
3113   grntest_log_file = fopen(log_path, "w+b");
3114   if (!grntest_log_file) {
3115     fprintf(stderr, "Cannot open log file: <%s>\n", log_path);
3116     goto exit;
3117   }
3118 
3119   get_sysinfo(dbname, sysinfo, BUF_LEN);
3120   output_sysinfo(sysinfo);
3121 
3122 #ifndef WIN32
3123   setsigint();
3124 #endif /* WIN32 */
3125   qnum = do_script(&context, scrname);
3126   output_result_final(&context, qnum);
3127   fclose(grntest_log_file);
3128 
3129   if (grntest_ftp_mode) {
3130     ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, log_path, 3,
3131             "report", NULL);
3132   }
3133   fprintf(stderr, "grntest done. logfile=%s\n", log_path);
3134 
3135 exit:
3136   if (pid_path) {
3137     remove(pid_path);
3138   }
3139 
3140   shutdown_server();
3141 #ifdef WIN32
3142   if (!grntest_remote_mode) {
3143     int ret;
3144     ret = WaitForSingleObject(grntest_pi.hProcess, 20000);
3145     if (ret == WAIT_TIMEOUT) {
3146       fprintf(stderr, "timeout:groonga server cannot shutdown!!\n");
3147       fprintf(stderr, "Cannot wait\n");
3148       exit(1);
3149     }
3150   }
3151 #else
3152   if (grntest_server_id) {
3153     int ret, pstatus;
3154     setsigalarm(20);
3155     ret = waitpid(grntest_server_id, &pstatus, 0);
3156     if (ret < 0) {
3157       fprintf(stderr, "Cannot wait\n");
3158       exit(1);
3159     }
3160 /*
3161     else {
3162       fprintf(stderr, "pstatus = %d\n", pstatus);
3163     }
3164 */
3165     alarm(0);
3166   }
3167 #endif /* WIN32 */
3168   CRITICAL_SECTION_FIN(grntest_cs);
3169   grn_obj_close(&context, &grntest_starttime);
3170   grn_obj_close(&context, grntest_db);
3171   grn_ctx_fin(&context);
3172   grn_obj_close(&grntest_server_context, grn_ctx_db(&grntest_server_context));
3173   grn_ctx_fin(&grntest_server_context);
3174   grn_fin();
3175   return exit_code;
3176 }
3177