Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
groonga.c
Go to the documentation of this file.
1 /* -*- c-basic-offset: 2 -*- */
2 /*
3  Copyright(C) 2009-2013 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-1301 USA
17 */
18 
19 #ifdef WIN32
20 # define GROONGA_MAIN
21 #endif /* WIN32 */
22 #include "lib/groonga_in.h"
23 
24 #include "lib/com.h"
25 #include "lib/ctx_impl.h"
26 #include "lib/proc.h"
27 #include "lib/db.h"
28 #include "lib/util.h"
29 #include <string.h>
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <fcntl.h>
33 #ifdef HAVE_SYS_WAIT_H
34 # include <sys/wait.h>
35 #endif /* HAVE_SYS_WAIT_H */
36 #ifdef HAVE_SYS_SOCKET_H
37 # include <sys/socket.h>
38 #endif /* HAVE_SYS_SOCKET_H */
39 #ifdef HAVE_NETINET_IN_H
40 # include <netinet/in.h>
41 #endif /* HAVE_NETINET_IN_H */
42 
43 #ifdef HAVE_SYS_RESOURCE_H
44 # include <sys/resource.h>
45 #endif /* HAVE_SYS_RESOURCE_H */
46 
47 #ifdef HAVE_SYS_SYSCTL_H
48 # include <sys/sysctl.h>
49 #endif /* HAVE_SYS_SYSCTL_H */
50 
51 #ifndef USE_MSG_NOSIGNAL
52 # ifdef MSG_NOSIGNAL
53 # undef MSG_NOSIGNAL
54 # endif
55 # define MSG_NOSIGNAL 0
56 #endif /* USE_MSG_NOSIGNAL */
57 
58 #ifndef STDIN_FILENO
59 # define STDIN_FILENO 0
60 #endif /* STDIN_FILENO */
61 #ifndef STDOUT_FILENO
62 # define STDOUT_FILENO 1
63 #endif /* STDOUT_FILENO */
64 #ifndef STDERR_FILENO
65 # define STDERR_FILENO 2
66 #endif /* STDERR_FILENO */
67 
68 #define DEFAULT_PORT 10041
69 #define DEFAULT_DEST "localhost"
70 #define DEFAULT_MAX_NFTHREADS 8
71 #define MAX_CON 0x10000
72 
73 #define RLIMIT_NOFILE_MINIMUM 4096
74 
75 static char bind_address[HOST_NAME_MAX + 1];
76 static char hostname[HOST_NAME_MAX + 1];
77 static int port = DEFAULT_PORT;
78 static int batchmode;
79 static int number_of_lines = 0;
80 static int newdb;
81 static int useql;
82 static grn_bool is_daemon_mode = GRN_FALSE;
83 static int (*do_client)(int argc, char **argv);
84 static int (*do_server)(char *path);
85 static const char *pid_file_path = NULL;
86 static const char *input_path = NULL;
87 static FILE *output = NULL;
88 
89 static int ready_notify_pipe[2];
90 #define PIPE_READ 0
91 #define PIPE_WRITE 1
92 
93 static grn_encoding encoding;
94 static grn_command_version default_command_version;
95 static int64_t default_match_escalation_threshold;
96 static int log_level;
97 
98 static int
99 grn_rc_to_exit_code(grn_rc rc)
100 {
101  if (rc == GRN_SUCCESS) {
102  return EXIT_SUCCESS;
103  } else {
104  return EXIT_FAILURE;
105  }
106 }
107 
108 #ifdef GRN_WITH_LIBEDIT
109 #include <locale.h>
110 #include <histedit.h>
111 static EditLine *line_editor = NULL;
112 static HistoryW *line_editor_history = NULL;
113 static HistEventW line_editor_history_event;
114 static char line_editor_history_path[PATH_MAX] = "";
115 
116 static const wchar_t *
117 line_editor_prompt(EditLine *e __attribute__((unused)))
118 {
119  return L"> ";
120 }
121 static const wchar_t * const line_editor_editor = L"emacs";
122 
123 static void
124 line_editor_init(int argc __attribute__((unused)), char *argv[])
125 {
126  const char * const HOME_PATH = getenv("HOME");
127  const char * const HISTORY_PATH = "/.groonga-history";
128 
129  setlocale(LC_ALL, "");
130 
131  if (strlen(HOME_PATH) + strlen(HISTORY_PATH) < PATH_MAX) {
132  strcpy(line_editor_history_path, HOME_PATH);
133  strcat(line_editor_history_path, HISTORY_PATH);
134  } else {
135  line_editor_history_path[0] = '\0';
136  }
137 
138  line_editor_history = history_winit();
139  history_w(line_editor_history, &line_editor_history_event, H_SETSIZE, 200);
140  if (line_editor_history_path[0]) {
141  history_w(line_editor_history, &line_editor_history_event,
142  H_LOAD, line_editor_history_path);
143  }
144 
145  line_editor = el_init(argv[0], stdin, stdout, stderr);
146  el_wset(line_editor, EL_PROMPT, &line_editor_prompt);
147  el_wset(line_editor, EL_EDITOR, line_editor_editor);
148  el_wset(line_editor, EL_HIST, history_w, line_editor_history);
149  el_source(line_editor, NULL);
150 }
151 
152 static void
153 line_editor_fin(void)
154 {
155  if (line_editor) {
156  el_end(line_editor);
157  if (line_editor_history) {
158  if (line_editor_history_path[0]) {
159  history_w(line_editor_history, &line_editor_history_event,
160  H_SAVE, line_editor_history_path);
161  }
162  history_wend(line_editor_history);
163  }
164  }
165 }
166 
167 static grn_rc
168 line_editor_fgets(grn_ctx *ctx, grn_obj *buf)
169 {
170  grn_rc rc = GRN_SUCCESS;
171  const wchar_t *line;
172  int nchar;
173  line = el_wgets(line_editor, &nchar);
174  if (nchar > 0) {
175  int i;
176  char multibyte_buf[MB_CUR_MAX];
177  size_t multibyte_len;
178  mbstate_t ps;
179  history_w(line_editor_history, &line_editor_history_event, H_ENTER, line);
180  memset(&ps, 0, sizeof(ps));
181  wcrtomb(NULL, L'\0', &ps);
182  for (i = 0; i < nchar; i++) {
183  multibyte_len = wcrtomb(multibyte_buf, line[i], &ps);
184  if (multibyte_len == (size_t)-1) {
186  "[prompt][libedit] failed to read input: %s", strerror(errno));
188  } else {
189  GRN_TEXT_PUT(ctx, buf, multibyte_buf, multibyte_len);
190  }
191  }
192  } else {
193  rc = GRN_END_OF_DATA;
194  }
195  return rc;
196 }
197 #endif /* GRN_WITH_LIBEDIT */
198 
199 inline static grn_rc
200 read_next_line(grn_ctx *ctx, grn_obj *buf)
201 {
202  static int the_first_read = GRN_TRUE;
203  grn_rc rc = GRN_SUCCESS;
204  if (!batchmode) {
205 #ifdef GRN_WITH_LIBEDIT
206  rc = line_editor_fgets(ctx, buf);
207 #else
208  fprintf(stderr, "> ");
209  rc = grn_text_fgets(ctx, buf, stdin);
210 #endif
211  } else {
212  rc = grn_text_fgets(ctx, buf, stdin);
213  if (rc != GRN_END_OF_DATA) {
214  number_of_lines++;
215  }
216  }
217  if (the_first_read && GRN_TEXT_LEN(buf) > 0) {
218  const char bom[] = {0xef, 0xbb, 0xbf};
219  if (GRN_CTX_GET_ENCODING(ctx) == GRN_ENC_UTF8 &&
220  GRN_TEXT_LEN(buf) > 3 && !memcmp(GRN_TEXT_VALUE(buf), bom, 3)) {
221  grn_obj buf_without_bom;
222  GRN_TEXT_INIT(&buf_without_bom, 0);
223  GRN_TEXT_PUT(ctx, &buf_without_bom,
224  GRN_TEXT_VALUE(buf) + 3, GRN_TEXT_LEN(buf) - 3);
225  GRN_TEXT_SET(ctx, buf,
226  GRN_TEXT_VALUE(&buf_without_bom),
227  GRN_TEXT_LEN(&buf_without_bom));
228  grn_obj_unlink(ctx, &buf_without_bom);
229  }
230  the_first_read = GRN_FALSE;
231  }
232  if (GRN_TEXT_LEN(buf) > 0 &&
233  GRN_TEXT_VALUE(buf)[GRN_TEXT_LEN(buf) - 1] == '\n') {
234  grn_bulk_truncate(ctx, buf, GRN_TEXT_LEN(buf) - 1);
235  }
236  if (GRN_TEXT_LEN(buf) > 0 &&
237  GRN_TEXT_VALUE(buf)[GRN_TEXT_LEN(buf) - 1] == '\r') {
238  grn_bulk_truncate(ctx, buf, GRN_TEXT_LEN(buf) - 1);
239  }
240  return rc;
241 }
242 
243 inline static grn_rc
244 prompt(grn_ctx *ctx, grn_obj *buf)
245 {
246  grn_rc rc = GRN_SUCCESS;
247  grn_bool need_next_line = GRN_TRUE;
248  GRN_BULK_REWIND(buf);
249  while (need_next_line) {
250  rc = read_next_line(ctx, buf);
251  if (rc == GRN_SUCCESS &&
252  GRN_TEXT_LEN(buf) > 0 &&
253  GRN_TEXT_VALUE(buf)[GRN_TEXT_LEN(buf) - 1] == '\\') {
254  grn_bulk_truncate(ctx, buf, GRN_TEXT_LEN(buf) - 1);
255  need_next_line = GRN_TRUE;
256  } else {
257  need_next_line = GRN_FALSE;
258  }
259  }
260  return rc;
261 }
262 
263 typedef enum {
268 
269 static void
270 output_envelope(grn_ctx *ctx, grn_rc rc, grn_obj *head, grn_obj *body, grn_obj *foot)
271 {
272  grn_output_envelope(ctx, rc, head, body, foot, input_path, number_of_lines);
273 }
274 
275 static void
276 s_output(grn_ctx *ctx, int flags, void *arg)
277 {
278  if (ctx && ctx->impl && (flags & GRN_CTX_TAIL)) {
279  grn_obj *buf = ctx->impl->outbuf;
280  grn_obj *command;
281  if (GRN_TEXT_LEN(buf) || ctx->rc) {
282  FILE * stream = (FILE *) arg;
283  grn_obj head, foot;
284  GRN_TEXT_INIT(&head, 0);
285  GRN_TEXT_INIT(&foot, 0);
286  output_envelope(ctx, ctx->rc, &head, buf, &foot);
287  fwrite(GRN_TEXT_VALUE(&head), 1, GRN_TEXT_LEN(&head), stream);
288  fwrite(GRN_TEXT_VALUE(buf), 1, GRN_TEXT_LEN(buf), stream);
289  fwrite(GRN_TEXT_VALUE(&foot), 1, GRN_TEXT_LEN(&foot), stream);
290  fputc('\n', stream);
291  fflush(stream);
292  GRN_BULK_REWIND(buf);
293  GRN_OBJ_FIN(ctx, &head);
294  GRN_OBJ_FIN(ctx, &foot);
295  }
296  command = GRN_CTX_USER_DATA(ctx)->ptr;
297  GRN_BULK_REWIND(command);
298  }
299 }
300 
301 static int
302 do_alone(int argc, char **argv)
303 {
304  int exit_code = EXIT_FAILURE;
305  char *path = NULL;
306  grn_obj *db;
307  grn_ctx ctx_, *ctx = &ctx_;
308  grn_ctx_init(ctx, (useql ? GRN_CTX_USE_QL : 0)|(batchmode ? GRN_CTX_BATCH_MODE : 0));
309  if (argc > 0 && argv) { path = *argv++; argc--; }
310  db = (newdb || !path) ? grn_db_create(ctx, path, NULL) : grn_db_open(ctx, path);
311  if (db) {
312  grn_obj command;
313  GRN_TEXT_INIT(&command, 0);
314  GRN_CTX_USER_DATA(ctx)->ptr = &command;
315  grn_ctx_recv_handler_set(ctx, s_output, output);
316  if (!argc) {
317  grn_obj text;
318  GRN_TEXT_INIT(&text, 0);
319  while (prompt(ctx, &text) != GRN_END_OF_DATA) {
320  GRN_TEXT_PUT(ctx, &command, GRN_TEXT_VALUE(&text), GRN_TEXT_LEN(&text));
321  grn_ctx_send(ctx, GRN_TEXT_VALUE(&text), GRN_TEXT_LEN(&text), 0);
322  if (ctx->stat == GRN_CTX_QUIT) { break; }
323  }
324  exit_code = grn_rc_to_exit_code(ctx->rc);
325  grn_obj_unlink(ctx, &text);
326  } else {
327  grn_rc rc;
328  rc = grn_ctx_sendv(ctx, argc, argv, 0);
329  exit_code = grn_rc_to_exit_code(rc);
330  }
331  grn_obj_unlink(ctx, &command);
332  grn_obj_close(ctx, db);
333  } else {
334  fprintf(stderr, "db open failed (%s): %s\n", path, ctx->errbuf);
335  }
336  grn_ctx_fin(ctx);
337  return exit_code;
338 }
339 
340 static int
341 c_output(grn_ctx *ctx)
342 {
343  int flags;
344  char *str;
345  unsigned int str_len;
346  do {
347  grn_ctx_recv(ctx, &str, &str_len, &flags);
348  /*
349  if (ctx->rc) {
350  fprintf(stderr, "grn_ctx_recv failed\n");
351  return -1;
352  }
353  */
354  if (str_len || ctx->rc) {
355  grn_obj head, body, foot;
356  GRN_TEXT_INIT(&head, 0);
358  GRN_TEXT_INIT(&foot, 0);
359  if (ctx->rc == GRN_SUCCESS) {
360  GRN_TEXT_SET(ctx, &body, str, str_len);
361  } else {
362  ERR(ctx->rc, "%.*s", str_len, str);
363  }
364  output_envelope(ctx, ctx->rc, &head, &body, &foot);
365  fwrite(GRN_TEXT_VALUE(&head), 1, GRN_TEXT_LEN(&head), output);
366  fwrite(GRN_TEXT_VALUE(&body), 1, GRN_TEXT_LEN(&body), output);
367  fwrite(GRN_TEXT_VALUE(&foot), 1, GRN_TEXT_LEN(&foot), output);
368  fputc('\n', output);
369  fflush(output);
370  GRN_OBJ_FIN(ctx, &head);
371  GRN_OBJ_FIN(ctx, &body);
372  GRN_OBJ_FIN(ctx, &foot);
373  }
374  } while ((flags & GRN_CTX_MORE));
375  return 0;
376 }
377 
378 static int
379 g_client(int argc, char **argv)
380 {
381  int exit_code = EXIT_FAILURE;
382  grn_ctx ctx_, *ctx = &ctx_;
383  const char *hostname = DEFAULT_DEST;
384  if (argc > 0 && argv) { hostname = *argv++; argc--; }
385  grn_ctx_init(ctx, (batchmode ? GRN_CTX_BATCH_MODE : 0));
386  if (!grn_ctx_connect(ctx, hostname, port, 0)) {
387  if (!argc) {
388  grn_obj text;
389  GRN_TEXT_INIT(&text, 0);
390  while (prompt(ctx, &text) != GRN_END_OF_DATA) {
391  grn_ctx_send(ctx, GRN_TEXT_VALUE(&text), GRN_TEXT_LEN(&text), 0);
392  exit_code = grn_rc_to_exit_code(ctx->rc);
393  if (ctx->rc != GRN_SUCCESS) { break; }
394  if (c_output(ctx)) { goto exit; }
395  if (ctx->stat == GRN_CTX_QUIT) { break; }
396  }
397  grn_obj_unlink(ctx, &text);
398  } else {
399  grn_rc rc;
400  rc = grn_ctx_sendv(ctx, argc, argv, 0);
401  exit_code = grn_rc_to_exit_code(rc);
402  if (c_output(ctx)) { goto exit; }
403  }
404  } else {
405  fprintf(stderr, "grn_ctx_connect failed (%s:%d)\n", hostname, port);
406  }
407 exit :
408  grn_ctx_fin(ctx);
409  return exit_code;
410 }
411 
412 /* server */
413 
414 typedef void (*grn_edge_dispatcher_func)(grn_ctx *ctx, grn_edge *edge);
415 typedef void (*grn_handler_func)(grn_ctx *ctx, grn_obj *msg);
416 
417 static grn_com_queue ctx_new;
418 static grn_com_queue ctx_old;
419 static grn_mutex q_mutex;
420 static grn_cond q_cond;
421 static uint32_t nthreads = 0, nfthreads = 0, max_nfthreads;
422 
423 static void
424 reset_ready_notify_pipe(void)
425 {
426  ready_notify_pipe[PIPE_READ] = 0;
427  ready_notify_pipe[PIPE_WRITE] = 0;
428 }
429 
430 static void
431 close_ready_notify_pipe(void)
432 {
433  if (ready_notify_pipe[PIPE_READ] > 0) {
434  close(ready_notify_pipe[PIPE_READ]);
435  }
436  if (ready_notify_pipe[PIPE_WRITE] > 0) {
437  close(ready_notify_pipe[PIPE_WRITE]);
438  }
439  reset_ready_notify_pipe();
440 }
441 
442 static void
443 send_ready_notify(void)
444 {
445  if (ready_notify_pipe[PIPE_WRITE] > 0) {
446  const char *ready_notify_message = "ready";
447  write(ready_notify_pipe[PIPE_WRITE],
448  ready_notify_message,
449  strlen(ready_notify_message));
450  }
451  close_ready_notify_pipe();
452 }
453 
454 static int
455 daemonize(void)
456 {
457  int exit_code = EXIT_SUCCESS;
458 #ifndef WIN32
459  pid_t pid;
460 
461  if (pipe(ready_notify_pipe) == -1) {
462  reset_ready_notify_pipe();
463  }
464 
465  switch (fork()) {
466  case 0:
467  break;
468  case -1:
469  perror("fork");
470  return EXIT_FAILURE;
471  default:
472  wait(NULL);
473  if (ready_notify_pipe[PIPE_READ] > 0) {
474  int max_fd;
475  fd_set read_fds;
476  FD_ZERO(&read_fds);
477  FD_SET(ready_notify_pipe[PIPE_READ], &read_fds);
478  max_fd = ready_notify_pipe[PIPE_READ] + 1;
479  select(max_fd, &read_fds, NULL, NULL, NULL);
480  }
481  close_ready_notify_pipe();
482  _exit(EXIT_SUCCESS);
483  }
484  switch (fork()) {
485  case 0:
486  {
487  FILE *pid_file = NULL;
488  if (pid_file_path) {
489  pid_file = fopen(pid_file_path, "w");
490  }
491  pid = getpid();
492  if (!pid_file) {
493  fprintf(stderr, "%d\n", pid);
494  } else {
495  fprintf(pid_file, "%d\n", pid);
496  fclose(pid_file);
497  pid_file = NULL;
498  }
499  }
500  break;
501  case -1:
502  perror("fork");
503  return EXIT_FAILURE;
504  default:
505  close_ready_notify_pipe();
506  _exit(EXIT_SUCCESS);
507  }
508  {
509  int null_fd = GRN_OPEN("/dev/null", O_RDWR, 0);
510  if (null_fd != -1) {
511  dup2(null_fd, STDIN_FILENO);
512  dup2(null_fd, STDOUT_FILENO);
513  dup2(null_fd, STDERR_FILENO);
514  if (null_fd > STDERR_FILENO) { GRN_CLOSE(null_fd); }
515  }
516  }
517 #endif /* WIN32 */
518  return exit_code;
519 }
520 
521 static void
522 clean_pid_file(void)
523 {
524 #ifndef WIN32
525  if (pid_file_path) {
526  unlink(pid_file_path);
527  }
528 #endif
529 }
530 
531 static void
532 run_server_loop(grn_ctx *ctx, grn_com_event *ev)
533 {
534  while (!grn_com_event_poll(ctx, ev, 1000) && grn_gctx.stat != GRN_CTX_QUIT) {
535  grn_edge *edge;
536  while ((edge = (grn_edge *)grn_com_queue_deque(ctx, &ctx_old))) {
537  grn_obj *msg;
538  while ((msg = (grn_obj *)grn_com_queue_deque(ctx, &edge->send_old))) {
539  grn_msg_close(&edge->ctx, msg);
540  }
541  while ((msg = (grn_obj *)grn_com_queue_deque(ctx, &edge->recv_new))) {
542  grn_msg_close(ctx, msg);
543  }
544  grn_ctx_fin(&edge->ctx);
545  if (edge->com->has_sid && edge->com->opaque == edge) {
546  grn_com_close(ctx, edge->com);
547  }
548  grn_edges_delete(ctx, edge);
549  }
550  /* todo : log stat */
551  }
552  for (;;) {
553  MUTEX_LOCK(q_mutex);
554  if (nthreads == nfthreads) { break; }
555  MUTEX_UNLOCK(q_mutex);
556  grn_nanosleep(1000000);
557  }
558  {
559  grn_edge *edge;
560  GRN_HASH_EACH(ctx, grn_edges, id, NULL, NULL, &edge, {
561  grn_obj *obj;
562  while ((obj = (grn_obj *)grn_com_queue_deque(ctx, &edge->send_old))) {
563  grn_msg_close(&edge->ctx, obj);
564  }
565  while ((obj = (grn_obj *)grn_com_queue_deque(ctx, &edge->recv_new))) {
566  grn_msg_close(ctx, obj);
567  }
568  grn_ctx_fin(&edge->ctx);
569  if (edge->com->has_sid) {
570  grn_com_close(ctx, edge->com);
571  }
572  grn_edges_delete(ctx, edge);
573  });
574  }
575  {
576  grn_com *com;
577  GRN_HASH_EACH(ctx, ev->hash, id, NULL, NULL, &com, { grn_com_close(ctx, com); });
578  }
579 }
580 
581 static int
582 run_server(grn_ctx *ctx, grn_obj *db, grn_com_event *ev,
583  grn_edge_dispatcher_func dispatcher, grn_handler_func handler)
584 {
585  int exit_code = EXIT_SUCCESS;
586  struct hostent *he;
587  if (!(he = gethostbyname(hostname))) {
588  send_ready_notify();
589  SERR("gethostbyname");
590  } else {
591  ev->opaque = db;
592  grn_edges_init(ctx, dispatcher);
593  if (!grn_com_sopen(ctx, ev, bind_address, port, handler, he)) {
594  send_ready_notify();
595  run_server_loop(ctx, ev);
596  exit_code = EXIT_SUCCESS;
597  } else {
598  send_ready_notify();
599  fprintf(stderr, "grn_com_sopen failed (%s:%d): %s\n",
600  bind_address, port, ctx->errbuf);
601  }
602  grn_edges_fin(ctx);
603  }
604  return exit_code;
605 }
606 
607 static int
608 start_service(grn_ctx *ctx, const char *db_path,
609  grn_edge_dispatcher_func dispatcher, grn_handler_func handler)
610 {
611  int exit_code = EXIT_SUCCESS;
612  grn_com_event ev;
613 
614  if (is_daemon_mode) {
615  exit_code = daemonize();
616  if (exit_code != EXIT_SUCCESS) {
617  return exit_code;
618  }
619  }
620 
621  if (!grn_com_event_init(ctx, &ev, MAX_CON, sizeof(grn_com))) {
622  grn_obj *db;
623  db = (newdb || !db_path) ? grn_db_create(ctx, db_path, NULL) : grn_db_open(ctx, db_path);
624  if (db) {
625  exit_code = run_server(ctx, db, &ev, dispatcher, handler);
626  grn_obj_close(ctx, db);
627  } else {
628  fprintf(stderr, "db open failed (%s)\n", db_path);
629  exit_code = EXIT_FAILURE;
630  send_ready_notify();
631  }
632  grn_com_event_fin(ctx, &ev);
633  } else {
634  fprintf(stderr, "grn_com_event_init failed\n");
635  exit_code = EXIT_FAILURE;
636  send_ready_notify();
637  }
638 
639  if (is_daemon_mode) {
640  clean_pid_file();
641  }
642 
643  return exit_code;
644 }
645 
646 typedef struct {
648 } ht_context;
649 
650 static void
651 h_output(grn_ctx *ctx, int flags, void *arg)
652 {
653  grn_rc expr_rc = ctx->rc;
654  ht_context *hc = (ht_context *)arg;
655  grn_sock fd = hc->msg->u.fd;
656  grn_obj header, head, foot, *outbuf = ctx->impl->outbuf;
657  if (!(flags & GRN_CTX_TAIL)) { return; }
658  GRN_TEXT_INIT(&header, 0);
659  GRN_TEXT_INIT(&head, 0);
660  GRN_TEXT_INIT(&foot, 0);
661  output_envelope(ctx, expr_rc, &head, outbuf, &foot);
662  switch (expr_rc) {
663  case GRN_SUCCESS :
664  GRN_TEXT_SETS(ctx, &header, "HTTP/1.1 200 OK\r\n");
665  break;
666  case GRN_INVALID_ARGUMENT :
667  GRN_TEXT_SETS(ctx, &header, "HTTP/1.1 400 Bad Request\r\n");
668  break;
670  GRN_TEXT_SETS(ctx, &header, "HTTP/1.1 404 Not Found\r\n");
671  break;
672  default :
673  GRN_TEXT_SETS(ctx, &header, "HTTP/1.1 500 Internal Server Error\r\n");
674  break;
675  }
676  GRN_TEXT_PUTS(ctx, &header, "Connection: close\r\n");
677  GRN_TEXT_PUTS(ctx, &header, "Content-Type: ");
678  GRN_TEXT_PUTS(ctx, &header, grn_ctx_get_mime_type(ctx));
679  GRN_TEXT_PUTS(ctx, &header, "\r\nContent-Length: ");
680  grn_text_lltoa(ctx, &header,
681  GRN_TEXT_LEN(&head) + GRN_TEXT_LEN(outbuf) + GRN_TEXT_LEN(&foot));
682  GRN_TEXT_PUTS(ctx, &header, "\r\n\r\n");
683  {
684  ssize_t ret, len;
685 #ifdef WIN32
686  WSABUF wsabufs[4];
687  wsabufs[0].buf = GRN_TEXT_VALUE(&header);
688  wsabufs[0].len = GRN_TEXT_LEN(&header);
689  wsabufs[1].buf = GRN_TEXT_VALUE(&head);
690  wsabufs[1].len = GRN_TEXT_LEN(&head);
691  wsabufs[2].buf = GRN_TEXT_VALUE(outbuf);
692  wsabufs[2].len = GRN_TEXT_LEN(outbuf);
693  wsabufs[3].buf = GRN_TEXT_VALUE(&foot);
694  wsabufs[3].len = GRN_TEXT_LEN(&foot);
695  if (WSASend(fd, wsabufs, 4, &ret, 0, NULL, NULL) == SOCKET_ERROR) {
696  SERR("WSASend");
697  }
698 #else /* WIN32 */
699  struct iovec msg_iov[4];
700  struct msghdr msg;
701  msg.msg_name = NULL;
702  msg.msg_namelen = 0;
703  msg.msg_iov = msg_iov;
704  msg.msg_iovlen = 4;
705  msg.msg_control = NULL;
706  msg.msg_controllen = 0;
707  msg.msg_flags = 0;
708  msg_iov[0].iov_base = GRN_TEXT_VALUE(&header);
709  msg_iov[0].iov_len = GRN_TEXT_LEN(&header);
710  msg_iov[1].iov_base = GRN_TEXT_VALUE(&head);
711  msg_iov[1].iov_len = GRN_TEXT_LEN(&head);
712  msg_iov[2].iov_base = GRN_TEXT_VALUE(outbuf);
713  msg_iov[2].iov_len = GRN_TEXT_LEN(outbuf);
714  msg_iov[3].iov_base = GRN_TEXT_VALUE(&foot);
715  msg_iov[3].iov_len = GRN_TEXT_LEN(&foot);
716  if ((ret = sendmsg(fd, &msg, MSG_NOSIGNAL)) == -1) {
717  SERR("sendmsg");
718  }
719 #endif /* WIN32 */
720  len = GRN_TEXT_LEN(&header) + GRN_TEXT_LEN(&head) +
721  GRN_TEXT_LEN(outbuf) + GRN_TEXT_LEN(&foot);
722  if (ret != len) {
724  "couldn't send all data (%" GRN_FMT_LLD "/%" GRN_FMT_LLD ")",
725  (long long int)ret, (long long int)len);
726  }
727  }
728  GRN_BULK_REWIND(outbuf);
729  GRN_OBJ_FIN(ctx, &foot);
730  GRN_OBJ_FIN(ctx, &head);
731  GRN_OBJ_FIN(ctx, &header);
732 }
733 
734 static void
735 do_htreq(grn_ctx *ctx, grn_msg *msg)
736 {
737  grn_sock fd = msg->u.fd;
739  grn_com_header *header = &msg->header;
740  switch (header->qtype) {
741  case 'G' : /* GET */
743  break;
744  case 'P' : /* POST */
746  break;
747  }
748  if (t) {
749  char *path = NULL;
750  char *pathe = GRN_BULK_HEAD((grn_obj *)msg);
751  char *e = GRN_BULK_CURR((grn_obj *)msg);
752  for (;; pathe++) {
753  if (e <= pathe + 6) {
754  /* invalid request */
755  goto exit;
756  }
757  if (*pathe == ' ') {
758  if (!path) {
759  path = pathe + 1;
760  } else {
761  if (!memcmp(pathe + 1, "HTTP/1", 6)) {
762  break;
763  }
764  }
765  }
766  }
767  grn_ctx_send(ctx, path, pathe - path, 0);
768  }
769 exit :
770  /* TODO: support "Connection: keep-alive" */
771  ctx->stat = GRN_CTX_QUIT;
772  /* TODO: support a command in multi requests. e.g.: load command */
773  grn_ctx_set_next_expr(ctx, NULL);
774  /* if (ctx->rc != GRN_OPERATION_WOULD_BLOCK) {...} */
775  grn_msg_close(ctx, (grn_obj *)msg);
776  /* if not keep alive connection */
777  grn_sock_close(fd);
779 }
780 
781 enum {
785  MBRES_E2BIG = 0x03,
786  MBRES_EINVAL = 0x04,
789  MBRES_ENOMEM = 0x82,
790 };
791 
792 enum {
793  MBCMD_GET = 0x00,
794  MBCMD_SET = 0x01,
795  MBCMD_ADD = 0x02,
797  MBCMD_DELETE = 0x04,
800  MBCMD_QUIT = 0x07,
801  MBCMD_FLUSH = 0x08,
802  MBCMD_GETQ = 0x09,
803  MBCMD_NOOP = 0x0a,
805  MBCMD_GETK = 0x0c,
806  MBCMD_GETKQ = 0x0d,
807  MBCMD_APPEND = 0x0e,
809  MBCMD_STAT = 0x10,
810  MBCMD_SETQ = 0x11,
811  MBCMD_ADDQ = 0x12,
816  MBCMD_QUITQ = 0x17,
817  MBCMD_FLUSHQ = 0x18,
820 };
821 
822 static grn_critical_section cache_lock;
823 static grn_obj *cache_table = NULL;
824 static grn_obj *cache_value = NULL;
825 static grn_obj *cache_flags = NULL;
826 static grn_obj *cache_expire = NULL;
827 static grn_obj *cache_cas = NULL;
828 
829 #define CTX_GET(name) (grn_ctx_get(ctx, (name), strlen(name)))
830 
831 static grn_obj *
832 cache_init(grn_ctx *ctx)
833 {
834  if (cache_cas) { return cache_cas; }
835  CRITICAL_SECTION_ENTER(cache_lock);
836  if (!cache_cas) {
837  if ((cache_table = CTX_GET("Memcache"))) {
838  cache_value = CTX_GET("Memcache.value");
839  cache_flags = CTX_GET("Memcache.flags");
840  cache_expire = CTX_GET("Memcache.expire");
841  cache_cas = CTX_GET("Memcache.cas");
842  } else {
843  if (!cache_table) {
844  grn_obj *uint32_type = grn_ctx_at(ctx, GRN_DB_UINT32);
845  grn_obj *uint64_type = grn_ctx_at(ctx, GRN_DB_UINT64);
846  grn_obj *shorttext_type = grn_ctx_at(ctx, GRN_DB_SHORT_TEXT);
847  if ((cache_table = grn_table_create(ctx, "Memcache", 8, NULL,
849  shorttext_type, NULL))) {
850  cache_value = grn_column_create(ctx, cache_table, "value", 5, NULL,
851  GRN_OBJ_PERSISTENT, shorttext_type);
852  cache_flags = grn_column_create(ctx, cache_table, "flags", 5, NULL,
853  GRN_OBJ_PERSISTENT, uint32_type);
854  cache_expire = grn_column_create(ctx, cache_table, "expire", 6, NULL,
855  GRN_OBJ_PERSISTENT, uint32_type);
856  cache_cas = grn_column_create(ctx, cache_table, "cas", 3, NULL,
857  GRN_OBJ_PERSISTENT, uint64_type);
858  }
859  }
860  }
861  }
862  CRITICAL_SECTION_LEAVE(cache_lock);
863  return cache_cas;
864 }
865 
866 #define RELATIVE_TIME_THRESH 1000000000
867 
868 #define MBRES(ctx,re,status,key_len,extra_len,flags) do {\
869  grn_msg_set_property((ctx), (re), (status), (key_len), (extra_len));\
870  grn_msg_send((ctx), (re), (flags));\
871 } while (0)
872 
873 #define GRN_MSG_MBRES(block) do {\
874  if (!quiet) {\
875  grn_obj *re = grn_msg_open_for_reply(ctx, (grn_obj *)msg, &edge->send_old);\
876  ((grn_msg *)re)->header.qtype = header->qtype;\
877  block\
878  }\
879 } while (0)
880 
881 static uint64_t
882 get_mbreq_cas_id()
883 {
884  static uint64_t cas_id = 0;
885  /* FIXME: use GRN_ATOMIC_ADD_EX_64, but it is not implemented */
886  return ++cas_id;
887 }
888 
889 static void
890 do_mbreq(grn_ctx *ctx, grn_edge *edge)
891 {
892  int quiet = 0;
893  int flags = 0;
894  grn_msg *msg = edge->msg;
895  grn_com_header *header = &msg->header;
896 
897  switch (header->qtype) {
898  case MBCMD_GETQ :
899  flags = GRN_CTX_MORE;
900  /* fallthru */
901  case MBCMD_GET :
902  {
903  grn_id rid;
904  uint16_t keylen = ntohs(header->keylen);
905  char *key = GRN_BULK_HEAD((grn_obj *)msg);
906  cache_init(ctx);
907  rid = grn_table_get(ctx, cache_table, key, keylen);
908  if (!rid) {
909  GRN_MSG_MBRES({
910  MBRES(ctx, re, MBRES_KEY_ENOENT, 0, 0, 0);
911  });
912  } else {
913  grn_timeval tv;
914  uint32_t expire;
915  {
916  grn_obj expire_buf;
917  GRN_UINT32_INIT(&expire_buf, 0);
918  grn_obj_get_value(ctx, cache_expire, rid, &expire_buf);
919  expire = GRN_UINT32_VALUE(&expire_buf);
920  grn_obj_close(ctx, &expire_buf);
921  }
922  grn_timeval_now(ctx, &tv);
923  if (expire && expire < tv.tv_sec) {
924  grn_table_delete_by_id(ctx, cache_table, rid);
925  GRN_MSG_MBRES({
926  MBRES(ctx, re, MBRES_KEY_ENOENT, 0, 0, 0);
927  });
928  } else {
929  grn_obj cas_buf;
930  GRN_UINT64_INIT(&cas_buf, 0);
931  grn_obj_get_value(ctx, cache_cas, rid, &cas_buf);
932  GRN_MSG_MBRES({
933  grn_obj_get_value(ctx, cache_flags, rid, re);
934  grn_obj_get_value(ctx, cache_value, rid, re);
935  ((grn_msg *)re)->header.cas = GRN_UINT64_VALUE(&cas_buf);
936  MBRES(ctx, re, MBRES_SUCCESS, 0, 4, flags);
937  });
938  grn_obj_close(ctx, &cas_buf);
939  }
940  }
941  }
942  break;
943  case MBCMD_SETQ :
944  case MBCMD_ADDQ :
945  case MBCMD_REPLACEQ :
946  quiet = 1;
947  /* fallthru */
948  case MBCMD_SET :
949  case MBCMD_ADD :
950  case MBCMD_REPLACE :
951  {
952  grn_id rid;
953  uint32_t size = ntohl(header->size);
954  uint16_t keylen = ntohs(header->keylen);
955  uint8_t extralen = header->level;
956  char *body = GRN_BULK_HEAD((grn_obj *)msg);
957  uint32_t flags = *((uint32_t *)body);
958  uint32_t expire = ntohl(*((uint32_t *)(body + 4)));
959  uint32_t valuelen = size - keylen - extralen;
960  char *key = body + 8;
961  char *value = key + keylen;
962  int added = 0;
963  int f = (header->qtype == MBCMD_REPLACE ||
964  header->qtype == MBCMD_REPLACEQ) ? 0 : GRN_TABLE_ADD;
965  GRN_ASSERT(extralen == 8);
966  cache_init(ctx);
967  if (header->qtype == MBCMD_REPLACE || header->qtype == MBCMD_REPLACEQ) {
968  rid = grn_table_get(ctx, cache_table, key, keylen);
969  } else {
970  rid = grn_table_add(ctx, cache_table, key, keylen, &added);
971  }
972  if (!rid) {
973  GRN_MSG_MBRES({
974  MBRES(ctx, re, (f & GRN_TABLE_ADD) ? MBRES_ENOMEM : MBRES_NOT_STORED, 0, 0, 0);
975  });
976  } else {
977  if (added) {
978  if (header->cas) {
979  GRN_MSG_MBRES({
980  MBRES(ctx, re, MBRES_EINVAL, 0, 0, 0);
981  });
982  } else {
983  grn_obj text_buf, uint32_buf;
985  GRN_TEXT_SET_REF(&text_buf, value, valuelen);
986  grn_obj_set_value(ctx, cache_value, rid, &text_buf, GRN_OBJ_SET);
987  GRN_UINT32_INIT(&uint32_buf, 0);
988  GRN_UINT32_SET(ctx, &uint32_buf, flags);
989  grn_obj_set_value(ctx, cache_flags, rid, &uint32_buf, GRN_OBJ_SET);
990  if (expire && expire < RELATIVE_TIME_THRESH) {
991  grn_timeval tv;
992  grn_timeval_now(ctx, &tv);
993  expire += tv.tv_sec;
994  }
995  GRN_UINT32_SET(ctx, &uint32_buf, expire);
996  grn_obj_set_value(ctx, cache_expire, rid, &uint32_buf, GRN_OBJ_SET);
997  grn_obj_close(ctx, &uint32_buf);
998  {
999  grn_obj cas_buf;
1000  uint64_t cas_id = get_mbreq_cas_id();
1001  GRN_UINT64_INIT(&cas_buf, 0);
1002  GRN_UINT64_SET(ctx, &cas_buf, cas_id);
1003  grn_obj_set_value(ctx, cache_cas, rid, &cas_buf, GRN_OBJ_SET);
1004  grn_obj_close(ctx, &cas_buf);
1005  GRN_MSG_MBRES({
1006  ((grn_msg *)re)->header.cas = cas_id;
1007  MBRES(ctx, re, MBRES_SUCCESS, 0, 0, 0);
1008  });
1009  }
1010  }
1011  } else {
1012  if (header->qtype != MBCMD_SET && header->qtype != MBCMD_SETQ) {
1013  grn_obj uint32_buf;
1014  grn_timeval tv;
1015  uint32_t oexpire;
1016 
1017  GRN_UINT32_INIT(&uint32_buf, 0);
1018  grn_obj_get_value(ctx, cache_expire, rid, &uint32_buf);
1019  oexpire = GRN_UINT32_VALUE(&uint32_buf);
1020  grn_timeval_now(ctx, &tv);
1021 
1022  if (oexpire && oexpire < tv.tv_sec) {
1023  if (header->qtype == MBCMD_REPLACE ||
1024  header->qtype == MBCMD_REPLACEQ) {
1025  grn_table_delete_by_id(ctx, cache_table, rid);
1026  GRN_MSG_MBRES({
1027  MBRES(ctx, re, MBRES_NOT_STORED, 0, 0, 0);
1028  });
1029  break;
1030  }
1031  } else if (header->qtype == MBCMD_ADD ||
1032  header->qtype == MBCMD_ADDQ) {
1033  GRN_MSG_MBRES({
1034  MBRES(ctx, re, MBRES_NOT_STORED, 0, 0, 0);
1035  });
1036  break;
1037  }
1038  }
1039  {
1040  if (header->cas) {
1041  grn_obj cas_buf;
1042  GRN_UINT64_INIT(&cas_buf, 0);
1043  grn_obj_get_value(ctx, cache_cas, rid, &cas_buf);
1044  if (header->cas != GRN_UINT64_VALUE(&cas_buf)) {
1045  GRN_MSG_MBRES({
1046  MBRES(ctx, re, MBRES_NOT_STORED, 0, 0, 0);
1047  });
1048  }
1049  }
1050  {
1051  grn_obj text_buf, uint32_buf;
1053  GRN_TEXT_SET_REF(&text_buf, value, valuelen);
1054  grn_obj_set_value(ctx, cache_value, rid, &text_buf, GRN_OBJ_SET);
1055  GRN_UINT32_INIT(&uint32_buf, 0);
1056  GRN_UINT32_SET(ctx, &uint32_buf, flags);
1057  grn_obj_set_value(ctx, cache_flags, rid, &uint32_buf, GRN_OBJ_SET);
1058  if (expire && expire < RELATIVE_TIME_THRESH) {
1059  grn_timeval tv;
1060  grn_timeval_now(ctx, &tv);
1061  expire += tv.tv_sec;
1062  }
1063  GRN_UINT32_SET(ctx, &uint32_buf, expire);
1064  grn_obj_set_value(ctx, cache_expire, rid, &uint32_buf, GRN_OBJ_SET);
1065  {
1066  grn_obj cas_buf;
1067  uint64_t cas_id = get_mbreq_cas_id();
1068  GRN_UINT64_INIT(&cas_buf, 0);
1069  GRN_UINT64_SET(ctx, &cas_buf, cas_id);
1070  grn_obj_set_value(ctx, cache_cas, rid, &cas_buf, GRN_OBJ_SET);
1071  GRN_MSG_MBRES({
1072  ((grn_msg *)re)->header.cas = cas_id;
1073  MBRES(ctx, re, MBRES_SUCCESS, 0, 0, 0);
1074  });
1075  }
1076  }
1077  }
1078  }
1079  }
1080  }
1081  break;
1082  case MBCMD_DELETEQ :
1083  quiet = 1;
1084  /* fallthru */
1085  case MBCMD_DELETE :
1086  {
1087  grn_id rid;
1088  uint16_t keylen = ntohs(header->keylen);
1089  char *key = GRN_BULK_HEAD((grn_obj *)msg);
1090  cache_init(ctx);
1091  rid = grn_table_get(ctx, cache_table, key, keylen);
1092  if (!rid) {
1093  /* GRN_LOG(ctx, GRN_LOG_NOTICE, "GET k=%d not found", keylen); */
1094  GRN_MSG_MBRES({
1095  MBRES(ctx, re, MBRES_KEY_ENOENT, 0, 0, 0);
1096  });
1097  } else {
1098  grn_table_delete_by_id(ctx, cache_table, rid);
1099  GRN_MSG_MBRES({
1100  MBRES(ctx, re, MBRES_SUCCESS, 0, 4, 0);
1101  });
1102  }
1103  }
1104  break;
1105  case MBCMD_INCREMENTQ :
1106  case MBCMD_DECREMENTQ :
1107  quiet = 1;
1108  /* fallthru */
1109  case MBCMD_INCREMENT :
1110  case MBCMD_DECREMENT :
1111  {
1112  grn_id rid;
1113  int added = 0;
1114  uint64_t delta, init;
1115  uint16_t keylen = ntohs(header->keylen);
1116  char *body = GRN_BULK_HEAD((grn_obj *)msg);
1117  char *key = body + 20;
1118  uint32_t expire = ntohl(*((uint32_t *)(body + 16)));
1119  grn_ntoh(&delta, body, 8);
1120  grn_ntoh(&init, body + 8, 8);
1121  GRN_ASSERT(header->level == 20); /* extralen */
1122  cache_init(ctx);
1123  if (expire == 0xffffffff) {
1124  rid = grn_table_get(ctx, cache_table, key, keylen);
1125  } else {
1126  rid = grn_table_add(ctx, cache_table, key, keylen, &added);
1127  }
1128  if (!rid) {
1129  GRN_MSG_MBRES({
1130  MBRES(ctx, re, MBRES_KEY_ENOENT, 0, 0, 0);
1131  });
1132  } else {
1133  grn_obj uint32_buf, text_buf;
1134  GRN_UINT32_INIT(&uint32_buf, 0);
1136  if (added) {
1137  GRN_TEXT_SET_REF(&text_buf, &init, 8);
1138  grn_obj_set_value(ctx, cache_value, rid, &text_buf, GRN_OBJ_SET);
1139  GRN_UINT32_SET(ctx, &uint32_buf, 0);
1140  grn_obj_set_value(ctx, cache_flags, rid, &uint32_buf, GRN_OBJ_SET);
1141  } else {
1142  grn_timeval tv;
1143  uint32_t oexpire;
1144 
1145  grn_obj_get_value(ctx, cache_expire, rid, &uint32_buf);
1146  oexpire = GRN_UINT32_VALUE(&uint32_buf);
1147  grn_timeval_now(ctx, &tv);
1148 
1149  if (oexpire && oexpire < tv.tv_sec) {
1150  if (expire == 0xffffffffU) {
1151  GRN_MSG_MBRES({
1152  MBRES(ctx, re, MBRES_KEY_ENOENT, 0, 0, 0);
1153  });
1154  break;
1155  } else {
1156  GRN_TEXT_SET_REF(&text_buf, &init, 8);
1157  grn_obj_set_value(ctx, cache_value, rid, &text_buf, GRN_OBJ_SET);
1158  GRN_UINT32_SET(ctx, &uint32_buf, 0);
1159  grn_obj_set_value(ctx, cache_flags, rid, &uint32_buf, GRN_OBJ_SET);
1160  }
1161  } else {
1162  grn_obj uint64_buf;
1163  GRN_UINT64_INIT(&uint64_buf, 0);
1164  GRN_UINT64_SET(ctx, &uint64_buf, delta);
1165  grn_obj_set_value(ctx, cache_value, rid, &uint64_buf,
1166  header->qtype == MBCMD_INCREMENT ||
1167  header->qtype == MBCMD_INCREMENTQ
1168  ? GRN_OBJ_INCR
1169  : GRN_OBJ_DECR);
1170  }
1171  }
1172  if (expire && expire < RELATIVE_TIME_THRESH) {
1173  grn_timeval tv;
1174  grn_timeval_now(ctx, &tv);
1175  expire += tv.tv_sec;
1176  }
1177  GRN_UINT32_SET(ctx, &uint32_buf, expire);
1178  grn_obj_set_value(ctx, cache_expire, rid, &uint32_buf, GRN_OBJ_SET);
1179  GRN_MSG_MBRES({
1180  /* TODO: get_mbreq_cas_id() */
1181  grn_obj_get_value(ctx, cache_value, rid, re);
1182  grn_hton(&delta, (uint64_t *)GRN_BULK_HEAD(re), 8);
1183  GRN_TEXT_SET(ctx, re, &delta, sizeof(uint64_t));
1184  MBRES(ctx, re, MBRES_SUCCESS, 0, sizeof(uint64_t), 0);
1185  });
1186  }
1187  }
1188  break;
1189  case MBCMD_FLUSHQ :
1190  quiet = 1;
1191  /* fallthru */
1192  case MBCMD_FLUSH :
1193  {
1194  uint32_t expire;
1195  uint8_t extralen = header->level;
1196  if (extralen) {
1197  char *body = GRN_BULK_HEAD((grn_obj *)msg);
1198  GRN_ASSERT(extralen == 4);
1199  expire = ntohl(*((uint32_t *)(body)));
1200  if (expire < RELATIVE_TIME_THRESH) {
1201  grn_timeval tv;
1202  grn_timeval_now(ctx, &tv);
1203  if (expire) {
1204  expire += tv.tv_sec;
1205  } else {
1206  expire = tv.tv_sec - 1;
1207  }
1208  }
1209  } else {
1210  grn_timeval tv;
1211  grn_timeval_now(ctx, &tv);
1212  expire = tv.tv_sec - 1;
1213  }
1214  {
1215  grn_obj exp_buf;
1216  GRN_UINT32_INIT(&exp_buf, 0);
1217  GRN_UINT32_SET(ctx, &exp_buf, expire);
1218  GRN_TABLE_EACH(ctx, cache_table, 0, 0, rid, NULL, NULL, NULL, {
1219  grn_obj_set_value(ctx, cache_expire, rid, &exp_buf, GRN_OBJ_SET);
1220  });
1221  GRN_MSG_MBRES({
1222  MBRES(ctx, re, MBRES_SUCCESS, 0, 4, 0);
1223  });
1224  grn_obj_close(ctx, &exp_buf);
1225  }
1226  }
1227  break;
1228  case MBCMD_NOOP :
1229  break;
1230  case MBCMD_VERSION :
1231  GRN_MSG_MBRES({
1232  grn_bulk_write(ctx, re, PACKAGE_VERSION, strlen(PACKAGE_VERSION));
1233  MBRES(ctx, re, MBRES_SUCCESS, 0, 0, 0);
1234  });
1235  break;
1236  case MBCMD_GETKQ :
1237  flags = GRN_CTX_MORE;
1238  /* fallthru */
1239  case MBCMD_GETK :
1240  {
1241  grn_id rid;
1242  uint16_t keylen = ntohs(header->keylen);
1243  char *key = GRN_BULK_HEAD((grn_obj *)msg);
1244  cache_init(ctx);
1245  rid = grn_table_get(ctx, cache_table, key, keylen);
1246  if (!rid) {
1247  GRN_MSG_MBRES({
1248  MBRES(ctx, re, MBRES_KEY_ENOENT, 0, 0, 0);
1249  });
1250  } else {
1251  grn_obj uint32_buf;
1252  grn_timeval tv;
1253  uint32_t expire;
1254  GRN_UINT32_INIT(&uint32_buf, 0);
1255  grn_obj_get_value(ctx, cache_expire, rid, &uint32_buf);
1256  expire = GRN_UINT32_VALUE(&uint32_buf);
1257  grn_timeval_now(ctx, &tv);
1258  if (expire && expire < tv.tv_sec) {
1259  grn_table_delete_by_id(ctx, cache_table, rid);
1260  GRN_MSG_MBRES({
1261  MBRES(ctx, re, MBRES_KEY_ENOENT, 0, 0, 0);
1262  });
1263  } else {
1264  grn_obj uint64_buf;
1265  GRN_UINT64_INIT(&uint64_buf, 0);
1266  grn_obj_get_value(ctx, cache_cas, rid, &uint64_buf);
1267  GRN_MSG_MBRES({
1268  grn_obj_get_value(ctx, cache_flags, rid, re);
1269  grn_bulk_write(ctx, re, key, keylen);
1270  grn_obj_get_value(ctx, cache_value, rid, re);
1271  ((grn_msg *)re)->header.cas = GRN_UINT64_VALUE(&uint64_buf);
1272  MBRES(ctx, re, MBRES_SUCCESS, keylen, 4, flags);
1273  });
1274  }
1275  }
1276  }
1277  break;
1278  case MBCMD_APPENDQ :
1279  case MBCMD_PREPENDQ :
1280  quiet = 1;
1281  /* fallthru */
1282  case MBCMD_APPEND :
1283  case MBCMD_PREPEND :
1284  {
1285  grn_id rid;
1286  uint32_t size = ntohl(header->size);
1287  uint16_t keylen = ntohs(header->keylen);
1288  char *key = GRN_BULK_HEAD((grn_obj *)msg);
1289  char *value = key + keylen;
1290  uint32_t valuelen = size - keylen;
1291  cache_init(ctx);
1292  rid = grn_table_add(ctx, cache_table, key, keylen, NULL);
1293  if (!rid) {
1294  GRN_MSG_MBRES({
1295  MBRES(ctx, re, MBRES_ENOMEM, 0, 0, 0);
1296  });
1297  } else {
1298  /* FIXME: check expire */
1299  grn_obj buf;
1300  int flags = header->qtype == MBCMD_APPEND ? GRN_OBJ_APPEND : GRN_OBJ_PREPEND;
1302  GRN_TEXT_SET_REF(&buf, value, valuelen);
1303  grn_obj_set_value(ctx, cache_value, rid, &buf, flags);
1304  GRN_MSG_MBRES({
1305  MBRES(ctx, re, MBRES_SUCCESS, 0, 0, 0);
1306  });
1307  }
1308  }
1309  break;
1310  case MBCMD_STAT :
1311  {
1312  pid_t pid = getpid();
1313  GRN_MSG_MBRES({
1314  grn_bulk_write(ctx, re, "pid", 3);
1315  grn_text_itoa(ctx, re, pid);
1316  MBRES(ctx, re, MBRES_SUCCESS, 3, 0, 0);
1317  });
1318  }
1319  break;
1320  case MBCMD_QUITQ :
1321  quiet = 1;
1322  /* fallthru */
1323  case MBCMD_QUIT :
1324  GRN_MSG_MBRES({
1325  MBRES(ctx, re, MBRES_SUCCESS, 0, 0, 0);
1326  });
1327  /* fallthru */
1328  default :
1329  ctx->stat = GRN_CTX_QUIT;
1330  break;
1331  }
1332 }
1333 
1334 /* worker thread */
1335 
1336 enum {
1337  EDGE_IDLE = 0x00,
1338  EDGE_WAIT = 0x01,
1339  EDGE_DOING = 0x02,
1340  EDGE_ABORT = 0x03,
1341 };
1342 
1343 static void
1344 check_rlimit_nofile(grn_ctx *ctx)
1345 {
1346 #ifndef WIN32
1347  struct rlimit limit;
1348  limit.rlim_cur = 0;
1349  limit.rlim_max = 0;
1350  getrlimit(RLIMIT_NOFILE, &limit);
1351  if (limit.rlim_cur < RLIMIT_NOFILE_MINIMUM) {
1352  limit.rlim_cur = RLIMIT_NOFILE_MINIMUM;
1353  limit.rlim_max = RLIMIT_NOFILE_MINIMUM;
1354  setrlimit(RLIMIT_NOFILE, &limit);
1355  limit.rlim_cur = 0;
1356  limit.rlim_max = 0;
1357  getrlimit(RLIMIT_NOFILE, &limit);
1358  }
1359  GRN_LOG(ctx, GRN_LOG_NOTICE,
1360  "RLIMIT_NOFILE(%" GRN_FMT_LLD ",%" GRN_FMT_LLD ")",
1361  (long long int)limit.rlim_cur, (long long int)limit.rlim_max);
1362 #endif /* WIN32 */
1363 }
1364 
1365 static void * CALLBACK
1366 h_worker(void *arg)
1367 {
1368  ht_context hc;
1369  grn_ctx ctx_, *ctx = &ctx_;
1370  grn_ctx_init(ctx, 0);
1371  grn_ctx_use(ctx, (grn_obj *)arg);
1372  grn_ctx_recv_handler_set(ctx, h_output, &hc);
1373  GRN_LOG(&grn_gctx, GRN_LOG_NOTICE, "thread start (%d/%d)", nfthreads, nthreads + 1);
1374  MUTEX_LOCK(q_mutex);
1375  do {
1376  grn_obj *msg;
1377  nfthreads++;
1378  while (!(msg = (grn_obj *)grn_com_queue_deque(&grn_gctx, &ctx_new))) {
1379  COND_WAIT(q_cond, q_mutex);
1380  if (grn_gctx.stat == GRN_CTX_QUIT) {
1381  nfthreads--;
1382  goto exit;
1383  }
1384  }
1385  nfthreads--;
1386  MUTEX_UNLOCK(q_mutex);
1387  hc.msg = (grn_msg *)msg;
1388  do_htreq(ctx, (grn_msg *)msg);
1389  MUTEX_LOCK(q_mutex);
1390  } while (nfthreads < max_nfthreads && grn_gctx.stat != GRN_CTX_QUIT);
1391 exit :
1392  nthreads--;
1393  MUTEX_UNLOCK(q_mutex);
1394  GRN_LOG(&grn_gctx, GRN_LOG_NOTICE, "thread end (%d/%d)", nfthreads, nthreads);
1395  grn_ctx_fin(ctx);
1396  return NULL;
1397 }
1398 
1399 static void
1400 h_handler(grn_ctx *ctx, grn_obj *msg)
1401 {
1402  grn_com *com = ((grn_msg *)msg)->u.peer;
1403  if (ctx->rc) {
1404  grn_com_close(ctx, com);
1405  grn_msg_close(ctx, msg);
1406  } else {
1407  grn_sock fd = com->fd;
1408  void *arg = com->ev->opaque;
1409  /* if not keep alive connection */
1410  grn_com_event_del(ctx, com->ev, fd);
1411  ((grn_msg *)msg)->u.fd = fd;
1412  MUTEX_LOCK(q_mutex);
1413  grn_com_queue_enque(ctx, &ctx_new, (grn_com_queue_entry *)msg);
1414  if (!nfthreads && nthreads < max_nfthreads) {
1415  grn_thread thread;
1416  nthreads++;
1417  if (THREAD_CREATE(thread, h_worker, arg)) { SERR("pthread_create"); }
1418  }
1419  COND_SIGNAL(q_cond);
1420  MUTEX_UNLOCK(q_mutex);
1421  }
1422 }
1423 
1424 static int
1425 h_server(char *path)
1426 {
1427  int exit_code = EXIT_FAILURE;
1428  grn_ctx ctx_, *ctx = &ctx_;
1429  grn_ctx_init(ctx, 0);
1430  MUTEX_INIT(q_mutex);
1431  COND_INIT(q_cond);
1432  CRITICAL_SECTION_INIT(cache_lock);
1433  GRN_COM_QUEUE_INIT(&ctx_new);
1434  GRN_COM_QUEUE_INIT(&ctx_old);
1435  check_rlimit_nofile(ctx);
1436  exit_code = start_service(ctx, path, NULL, h_handler);
1437  grn_ctx_fin(ctx);
1438  return exit_code;
1439 }
1440 
1441 static void * CALLBACK
1442 g_worker(void *arg)
1443 {
1444  GRN_LOG(&grn_gctx, GRN_LOG_NOTICE, "thread start (%d/%d)", nfthreads, nthreads + 1);
1445  MUTEX_LOCK(q_mutex);
1446  do {
1447  grn_ctx *ctx;
1448  grn_edge *edge;
1449  nfthreads++;
1450  while (!(edge = (grn_edge *)grn_com_queue_deque(&grn_gctx, &ctx_new))) {
1451  COND_WAIT(q_cond, q_mutex);
1452  if (grn_gctx.stat == GRN_CTX_QUIT) {
1453  nfthreads--;
1454  goto exit;
1455  }
1456  }
1457  ctx = &edge->ctx;
1458  nfthreads--;
1459  if (edge->stat == EDGE_DOING) { continue; }
1460  if (edge->stat == EDGE_WAIT) {
1461  edge->stat = EDGE_DOING;
1462  while (!GRN_COM_QUEUE_EMPTYP(&edge->recv_new)) {
1463  grn_obj *msg;
1464  MUTEX_UNLOCK(q_mutex);
1465  /* if (edge->flags == GRN_EDGE_WORKER) */
1466  while (ctx->stat != GRN_CTX_QUIT &&
1467  (edge->msg = (grn_msg *)grn_com_queue_deque(ctx, &edge->recv_new))) {
1468  grn_com_header *header = &edge->msg->header;
1469  msg = (grn_obj *)edge->msg;
1470  switch (header->proto) {
1471  case GRN_COM_PROTO_MBREQ :
1472  do_mbreq(ctx, edge);
1473  break;
1474  case GRN_COM_PROTO_GQTP :
1475  grn_ctx_send(ctx, GRN_BULK_HEAD(msg), GRN_BULK_VSIZE(msg), header->flags);
1476  ERRCLR(ctx);
1477  break;
1478  default :
1479  ctx->stat = GRN_CTX_QUIT;
1480  break;
1481  }
1482  grn_msg_close(ctx, msg);
1483  }
1484  while ((msg = (grn_obj *)grn_com_queue_deque(ctx, &edge->send_old))) {
1485  grn_msg_close(ctx, msg);
1486  }
1487  MUTEX_LOCK(q_mutex);
1488  if (ctx->stat == GRN_CTX_QUIT || edge->stat == EDGE_ABORT) { break; }
1489  }
1490  }
1491  if (ctx->stat == GRN_CTX_QUIT || edge->stat == EDGE_ABORT) {
1492  grn_com_queue_enque(&grn_gctx, &ctx_old, (grn_com_queue_entry *)edge);
1493  edge->stat = EDGE_ABORT;
1494  } else {
1495  edge->stat = EDGE_IDLE;
1496  }
1497  } while (nfthreads < max_nfthreads && grn_gctx.stat != GRN_CTX_QUIT);
1498 exit :
1499  nthreads--;
1500  MUTEX_UNLOCK(q_mutex);
1501  GRN_LOG(&grn_gctx, GRN_LOG_NOTICE, "thread end (%d/%d)", nfthreads, nthreads);
1502  return NULL;
1503 }
1504 
1505 static void
1506 g_dispatcher(grn_ctx *ctx, grn_edge *edge)
1507 {
1508  MUTEX_LOCK(q_mutex);
1509  if (edge->stat == EDGE_IDLE) {
1510  grn_com_queue_enque(ctx, &ctx_new, (grn_com_queue_entry *)edge);
1511  edge->stat = EDGE_WAIT;
1512  if (!nfthreads && nthreads < max_nfthreads) {
1513  grn_thread thread;
1514  nthreads++;
1515  if (THREAD_CREATE(thread, g_worker, NULL)) { SERR("pthread_create"); }
1516  }
1517  COND_SIGNAL(q_cond);
1518  }
1519  MUTEX_UNLOCK(q_mutex);
1520 }
1521 
1522 static void
1523 g_output(grn_ctx *ctx, int flags, void *arg)
1524 {
1525  grn_edge *edge = arg;
1526  grn_com *com = edge->com;
1527  grn_msg *req = edge->msg, *msg = (grn_msg *)ctx->impl->outbuf;
1528  msg->edge_id = req->edge_id;
1529  msg->header.proto = req->header.proto == GRN_COM_PROTO_MBREQ
1530  ? GRN_COM_PROTO_MBRES : req->header.proto;
1531  if (ctx->rc != GRN_SUCCESS && GRN_BULK_VSIZE(ctx->impl->outbuf) == 0) {
1532  GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, ctx->errbuf);
1533  }
1534  if (grn_msg_send(ctx, (grn_obj *)msg,
1535  (flags & GRN_CTX_MORE) ? GRN_CTX_MORE : GRN_CTX_TAIL)) {
1536  edge->stat = EDGE_ABORT;
1537  }
1538  ctx->impl->outbuf = grn_msg_open(ctx, com, &edge->send_old);
1539 }
1540 
1541 static void
1542 g_handler(grn_ctx *ctx, grn_obj *msg)
1543 {
1544  grn_edge *edge;
1545  grn_com *com = ((grn_msg *)msg)->u.peer;
1546  if (ctx->rc) {
1547  if (com->has_sid) {
1548  if ((edge = com->opaque)) {
1549  MUTEX_LOCK(q_mutex);
1550  if (edge->stat == EDGE_IDLE) {
1551  grn_com_queue_enque(ctx, &ctx_old, (grn_com_queue_entry *)edge);
1552  }
1553  edge->stat = EDGE_ABORT;
1554  MUTEX_UNLOCK(q_mutex);
1555  } else {
1556  grn_com_close(ctx, com);
1557  }
1558  }
1559  grn_msg_close(ctx, msg);
1560  } else {
1561  int added;
1562  edge = grn_edges_add(ctx, &((grn_msg *)msg)->edge_id, &added);
1563  if (added) {
1564  grn_ctx_init(&edge->ctx, (useql ? GRN_CTX_USE_QL : 0));
1565  GRN_COM_QUEUE_INIT(&edge->recv_new);
1566  GRN_COM_QUEUE_INIT(&edge->send_old);
1567  grn_ctx_use(&edge->ctx, (grn_obj *)com->ev->opaque);
1568  grn_ctx_recv_handler_set(&edge->ctx, g_output, edge);
1569  com->opaque = edge;
1570  grn_obj_close(&edge->ctx, edge->ctx.impl->outbuf);
1571  edge->ctx.impl->outbuf = grn_msg_open(&edge->ctx, com, &edge->send_old);
1572  edge->com = com;
1573  edge->stat = EDGE_IDLE;
1574  edge->flags = GRN_EDGE_WORKER;
1575  }
1576  if (edge->ctx.stat == GRN_CTX_QUIT || edge->stat == EDGE_ABORT) {
1577  grn_msg_close(ctx, msg);
1578  } else {
1579  grn_com_queue_enque(ctx, &edge->recv_new, (grn_com_queue_entry *)msg);
1580  g_dispatcher(ctx, edge);
1581  }
1582  }
1583 }
1584 
1585 static int
1586 g_server(char *path)
1587 {
1588  int exit_code = EXIT_FAILURE;
1589  grn_ctx ctx_, *ctx = &ctx_;
1590  grn_ctx_init(ctx, 0);
1591  MUTEX_INIT(q_mutex);
1592  COND_INIT(q_cond);
1593  CRITICAL_SECTION_INIT(cache_lock);
1594  GRN_COM_QUEUE_INIT(&ctx_new);
1595  GRN_COM_QUEUE_INIT(&ctx_old);
1596  check_rlimit_nofile(ctx);
1597  exit_code = start_service(ctx, path, g_dispatcher, g_handler);
1598  grn_ctx_fin(ctx);
1599  return exit_code;
1600 }
1601 
1602 enum {
1611 };
1612 
1613 #define MODE_MASK 0x007f
1614 #define MODE_USE_QL 0x0080
1615 #define MODE_NEW_DB 0x0100
1616 
1617 static uint32_t
1618 get_core_number(void)
1619 {
1620 #ifdef WIN32
1621  SYSTEM_INFO sinfo;
1622  GetSystemInfo(&sinfo);
1623  return sinfo.dwNumberOfProcessors;
1624 #else /* WIN32 */
1625 # ifdef _SC_NPROCESSORS_CONF
1626  return sysconf(_SC_NPROCESSORS_CONF);
1627 # else
1628  int n_processors;
1629  size_t length = sizeof(n_processors);
1630  int mib[] = {CTL_HW, HW_NCPU};
1631  if (sysctl(mib, sizeof(mib) / sizeof(mib[0]),
1632  &n_processors, &length, NULL, 0) == 0 &&
1633  length == sizeof(n_processors) &&
1634  0 < n_processors) {
1635  return n_processors;
1636  } else {
1637  return 1;
1638  }
1639 # endif /* _SC_NPROCESSORS_CONF */
1640 #endif /* WIN32 */
1641 }
1642 
1643 /*
1644  * The length of each line, including an end-of-line, in config file should be
1645  * shorter than (CONFIG_FILE_BUF_SIZE - 1) bytes. Too long lines are ignored.
1646  * Note that both '\r' and '\n' are handled as end-of-lines.
1647  *
1648  * '#' and ';' are special symbols to start comments. A comment ends with an
1649  * end-of-line.
1650  *
1651  * Format: name[=value]
1652  * - Preceding/trailing white-spaces of each line are removed.
1653  * - White-spaces aroung '=' are removed.
1654  * - name does not allow white-spaces.
1655  */
1656 #define CONFIG_FILE_BUF_SIZE 4096
1657 #define CONFIG_FILE_MAX_NAME_LENGTH 128
1658 #define CONFIG_FILE_MAX_VALUE_LENGTH 2048
1659 
1660 typedef enum {
1667 
1668 /*
1669  * The node type of a linked list for storing values. Note that a value is
1670  * stored in the extra space of an object.
1671  */
1672 typedef struct _config_file_entry {
1675 
1676 static config_file_entry *config_file_entry_head = NULL;
1677 
1678 static void
1679 config_file_clear(void) {
1680  while (config_file_entry_head) {
1681  config_file_entry *next = config_file_entry_head->next;
1682  free(config_file_entry_head);
1683  config_file_entry_head = next;
1684  }
1685 }
1686 
1687 static config_file_status
1688 config_file_register(const char *path, const grn_str_getopt_opt *opts,
1689  int *flags, const char *name, size_t name_length,
1690  const char *value, size_t value_length)
1691 {
1692  char name_buf[CONFIG_FILE_MAX_NAME_LENGTH + 3];
1693  config_file_entry *entry = NULL;
1694  char *args[4];
1695 
1696  name_buf[0] = name_buf[1] = '-';
1697  strcpy(name_buf + 2, name);
1698 
1699  if (value) {
1700  const size_t entry_size = sizeof(config_file_entry) + value_length + 1;
1701  entry = (config_file_entry *)malloc(entry_size);
1702  if (!entry) {
1703  fprintf(stderr, "memory allocation failed: %u bytes\n",
1704  (unsigned int)entry_size);
1705  return CONFIG_FILE_MALLOC_ERROR;
1706  }
1707  strcpy((char *)(entry + 1), value);
1708  entry->next = config_file_entry_head;
1709  if (!config_file_entry_head) {
1710  if (atexit(config_file_clear)) {
1711  free(entry);
1712  return CONFIG_FILE_ATEXIT_ERROR;
1713  }
1714  }
1715  config_file_entry_head = entry;
1716  }
1717 
1718  args[0] = (char *)path;
1719  args[1] = name_buf;
1720  args[2] = entry ? (char *)(entry + 1) : NULL;
1721  args[3] = NULL;
1722  grn_str_getopt(entry ? 3 : 2, args, opts, flags);
1723  return CONFIG_FILE_SUCCESS;
1724 }
1725 
1726 static config_file_status
1727 config_file_parse(const char *path, const grn_str_getopt_opt *opts,
1728  int *flags, char *buf) {
1729  char *ptr, *name, *value;
1730  size_t name_length, value_length;
1731 
1732  while (isspace(*buf)) {
1733  buf++;
1734  }
1735 
1736  ptr = buf;
1737  while (*ptr && *ptr != '#' && *ptr != ';') {
1738  ptr++;
1739  }
1740 
1741  do {
1742  *ptr-- = '\0';
1743  } while (ptr >= buf && isspace(*ptr));
1744 
1745  if (!*buf) {
1746  return CONFIG_FILE_SUCCESS;
1747  }
1748 
1749  name = ptr = buf;
1750  while (*ptr && !isspace(*ptr) && *ptr != '=') {
1751  ptr++;
1752  }
1753  while (isspace(*ptr)) {
1754  *ptr++ = '\0';
1755  }
1756 
1757  name_length = strlen(name);
1758  if (name_length == 0) {
1759  return CONFIG_FILE_SUCCESS;
1760  } else if (name_length > CONFIG_FILE_MAX_NAME_LENGTH) {
1761  fprintf(stderr, "too long name in config file: %u bytes\n",
1762  (unsigned int)name_length);
1763  return CONFIG_FILE_FORMAT_ERROR;
1764  }
1765 
1766  if (*ptr == '=') {
1767  *ptr++ = '\0';
1768  while (isspace(*ptr)) {
1769  ptr++;
1770  }
1771  value = ptr;
1772  } else if (*ptr) {
1773  fprintf(stderr, "invalid name in config file\n");
1774  return CONFIG_FILE_FORMAT_ERROR;
1775  } else {
1776  value = NULL;
1777  }
1778 
1779  value_length = value ? strlen(value) : 0;
1780  if (value_length > CONFIG_FILE_MAX_VALUE_LENGTH) {
1781  fprintf(stderr, "too long value in config file: %u bytes\n",
1782  (unsigned int)value_length);
1783  return CONFIG_FILE_FORMAT_ERROR;
1784  }
1785 
1786  return config_file_register(path, opts, flags,
1787  name, name_length, value, value_length);
1788 }
1789 
1790 static config_file_status
1791 config_file_load(const char *path, const grn_str_getopt_opt *opts, int *flags)
1792 {
1794  char buf[CONFIG_FILE_BUF_SIZE];
1795  size_t length = 0;
1796  FILE * const file = fopen(path, "rb");
1797  if (!file) {
1798  return CONFIG_FILE_FOPEN_ERROR;
1799  }
1800 
1801  for ( ; ; ) {
1802  int c = fgetc(file);
1803  if (c == '\r' || c == '\n' || c == EOF) {
1804  if (length < sizeof(buf) - 1) {
1805  buf[length] = '\0';
1806  status = config_file_parse(path, opts, flags, buf);
1807  if (status != CONFIG_FILE_SUCCESS) {
1808  break;
1809  }
1810  }
1811  length = 0;
1812  } else if (c == '\0') {
1813  fprintf(stderr, "prohibited '\\0' in config file: %s\n", path);
1814  status = CONFIG_FILE_FORMAT_ERROR;
1815  break;
1816  } else {
1817  if (length < sizeof(buf) - 1) {
1818  buf[length] = (char)c;
1819  }
1820  length++;
1821  }
1822 
1823  if (c == EOF) {
1824  break;
1825  }
1826  }
1827 
1828  fclose(file);
1829  return status;
1830 }
1831 
1832 static const int default_port = DEFAULT_PORT;
1833 static grn_encoding default_encoding = GRN_ENC_DEFAULT;
1834 static uint32_t default_max_num_threads = DEFAULT_MAX_NFTHREADS;
1835 static const int default_mode = mode_alone;
1836 static const int default_log_level = GRN_LOG_DEFAULT_LEVEL;
1837 static const char * const default_protocol = "gqtp";
1838 static const char *default_hostname = "localhost";
1839 static const char * const default_dest = "localhost";
1840 static const char *default_log_path = "";
1841 static const char *default_query_log_path = "";
1842 static const char *default_config_path = "";
1843 static const char *default_document_root = "";
1844 static grn_command_version default_default_command_version =
1846 static int64_t default_default_match_escalation_threshold = 0;
1847 static const char * const default_bind_address = "0.0.0.0";
1848 
1849 static void
1850 init_default_hostname(void)
1851 {
1852  static char hostname[HOST_NAME_MAX + 1];
1853  struct addrinfo hints, *result;
1854 
1855  hostname[HOST_NAME_MAX] = '\0';
1856  if (gethostname(hostname, HOST_NAME_MAX) == -1)
1857  return;
1858 
1859  memset(&hints, 0, sizeof(hints));
1860  hints.ai_family = AF_UNSPEC;
1861  hints.ai_socktype = SOCK_STREAM;
1862  hints.ai_addr = NULL;
1863  hints.ai_canonname = NULL;
1864  hints.ai_next = NULL;
1865  if (getaddrinfo(hostname, NULL, &hints, &result) != 0)
1866  return;
1867  freeaddrinfo(result);
1868 
1869  default_hostname = hostname;
1870 }
1871 
1872 static void
1873 init_default_settings(void)
1874 {
1875  output = stdout;
1876 
1877  default_encoding = grn_encoding_parse(GRN_DEFAULT_ENCODING);
1878 
1879  {
1880  const uint32_t num_cores = get_core_number();
1881  if (num_cores != 0) {
1882  default_max_num_threads = num_cores;
1883  }
1884  }
1885 
1886  init_default_hostname();
1887 
1888  default_log_path = grn_default_logger_get_path();
1889  default_query_log_path = grn_default_query_logger_get_path();
1890 
1891  default_config_path = getenv("GRN_CONFIG_PATH");
1892  if (!default_config_path) {
1893  default_config_path = GRN_CONFIG_PATH;
1894  if (!default_config_path) {
1895  default_config_path = "";
1896  }
1897  }
1898 
1899 #ifdef WIN32
1900  {
1901  static char win32_default_document_root[PATH_MAX];
1902  size_t document_root_length = strlen(grn_win32_base_dir()) + 1 +
1903  strlen(GRN_DEFAULT_RELATIVE_DOCUMENT_ROOT) + 1;
1904  if (document_root_length >= PATH_MAX) {
1905  fprintf(stderr, "can't use default root: too long path\n");
1906  } else {
1907  strcpy(win32_default_document_root, grn_win32_base_dir());
1908  strcat(win32_default_document_root, "/");
1909  strcat(win32_default_document_root, GRN_DEFAULT_RELATIVE_DOCUMENT_ROOT);
1910  default_document_root = win32_default_document_root;
1911  }
1912  }
1913 #else
1914  default_document_root = GRN_DEFAULT_DOCUMENT_ROOT;
1915 #endif
1916 
1917  default_default_command_version = grn_get_default_command_version();
1918  default_default_match_escalation_threshold =
1920 }
1921 
1922 static void
1923 show_config(FILE *out, const grn_str_getopt_opt *opts, int flags)
1924 {
1925  const grn_str_getopt_opt *o;
1926 
1927  for (o = opts; o->opt || o->longopt; o++) {
1928  switch (o->op) {
1929  case GETOPT_OP_NONE:
1930  if (o->arg && *o->arg) {
1931  if (o->longopt && strcmp(o->longopt, "config-path")) {
1932  fprintf(out, "%s=%s\n", o->longopt, *o->arg);
1933  }
1934  }
1935  break;
1936  case GETOPT_OP_ON:
1937  if (flags & o->flag) {
1938  goto no_arg;
1939  }
1940  break;
1941  case GETOPT_OP_OFF:
1942  if (!(flags & o->flag)) {
1943  goto no_arg;
1944  }
1945  break;
1946  case GETOPT_OP_UPDATE:
1947  if (flags == o->flag) {
1948  no_arg:
1949  if (o->longopt) {
1950  fprintf(out, "%s\n", o->longopt);
1951  }
1952  }
1953  break;
1954  }
1955  }
1956 }
1957 
1958 static void
1959 show_version(void)
1960 {
1961  printf("%s %s [",
1962  grn_get_package(),
1963  grn_get_version());
1964 
1965  /* FIXME: Should we detect host information dynamically on Windows? */
1966 #ifdef HOST_OS
1967  printf("%s,", HOST_OS);
1968 #endif
1969 #ifdef HOST_CPU
1970  printf("%s,", HOST_CPU);
1971 #endif
1972  printf("%s", GRN_DEFAULT_ENCODING);
1973 
1974  printf(",match-escalation-threshold=%" GRN_FMT_LLD,
1976 
1977 #ifndef NO_NFKC
1978  printf(",nfkc");
1979 #endif
1980 #ifdef GRN_WITH_MECAB
1981  printf(",mecab");
1982 #endif
1983 #ifdef GRN_WITH_MESSAGE_PACK
1984  printf(",msgpack");
1985 #endif
1986 #ifndef NO_ZLIB
1987  printf(",zlib");
1988 #endif
1989 #ifndef NO_LZO
1990  printf(",lzo");
1991 #endif
1992 #ifdef USE_KQUEUE
1993  printf(",kqueue");
1994 #endif
1995 #ifdef USE_EPOLL
1996  printf(",epoll");
1997 #endif
1998 #ifdef USE_POLL
1999  printf(",poll");
2000 #endif
2001  printf("]\n");
2002 
2003 #ifdef CONFIGURE_OPTIONS
2004  printf("\n");
2005  printf("configure options: <%s>\n", CONFIGURE_OPTIONS);
2006 #endif
2007 }
2008 
2009 static void
2010 show_usage(FILE *output)
2011 {
2012  uint32_t default_cache_limit = GRN_CACHE_DEFAULT_MAX_N_ENTRIES;
2013 
2014  fprintf(output,
2015  "Usage: groonga [options...] [dest]\n"
2016  "\n"
2017  "Mode options: (default: standalone)\n"
2018  " By default, groonga runs in standalone mode.\n"
2019  " -c: run in client mode\n"
2020  " -s: run in server mode\n"
2021  " -d: run in daemon mode\n"
2022  "\n"
2023  "Database creation options:\n"
2024  " -n: create new database (except client mode)\n"
2025  " -e, --encoding <encoding>:\n"
2026  " specify encoding for new database\n"
2027  " [none|euc|utf8|sjis|latin1|koi8r] (default: %s)\n"
2028  "\n"
2029  "Standalone/client options:\n"
2030  " --file <path>: read commands from specified file\n"
2031  " --input-fd <FD>: read commands from specified file descriptor\n"
2032  " --file has a prioriry over --input-fd\n"
2033  " --output-fd <FD>: output response to specifid file descriptor\n"
2034  " -p, --port <port number>: specify server port number (client mode only)\n"
2035  " (default: %d)\n"
2036  "\n"
2037  "Server/daemon options:\n"
2038  " --bind-address <ip/hostname>:\n"
2039  " specify server address to bind\n"
2040  " (default: %s)\n"
2041  " -p, --port <port number>: specify server port number (default: %d)\n"
2042  " -i, --server-id <ip/hostname>:\n"
2043  " specify server ID address (default: %s)\n"
2044  " --protocol <protocol>: specify server protocol to listen\n"
2045  " [gqtp|http|memcached] (default: %s)\n"
2046  " --document-root <path>: specify document root path (http only)\n"
2047  " (default: %s)\n"
2048  " --cache-limit <limit>: specify max number of cache data (default: %u)\n"
2049  " -t, --max-threads <max threads>:\n"
2050  " specify max number of threads (default: %u)\n"
2051  " --pid-path <path>: specify file to write process ID to\n"
2052  " (daemon mode only)\n"
2053  "\n"
2054  "Logging options:\n"
2055  " -l, --log-level <log level>:\n"
2056  " specify log level (default: %d)\n"
2057  " --log-path <path>: specify log path\n"
2058  " (default: %s)\n"
2059  " --query-log-path <path>:\n"
2060  " specify query log path\n"
2061  " (default: %s)\n"
2062  "\n"
2063  "Common options:\n"
2064  " --working-directory <path>:\n"
2065  " specify working directory path\n"
2066  " (none)\n"
2067  " --config-path <path>:\n"
2068  " specify config file path\n"
2069  " (default: %s)\n"
2070  " --default-command-version <version>:\n"
2071  " specify default command version (default: %d)\n"
2072  " --default-match-escalation-threshold <threshold>:\n"
2073  " specify default match escalation threshold"
2074  " (default: %" GRN_FMT_LLD ")\n"
2075  "\n"
2076  " --show-config: show config\n"
2077  " -h, --help: show usage\n"
2078  " --version: show groonga version\n"
2079  "\n"
2080  "dest:\n"
2081  " <db pathname> [<commands>]: in standalone mode\n"
2082  " <db pathname>: in server/daemon mode\n"
2083  " <dest hostname> [<commands>]: in client mode (default: %s)\n",
2084  grn_encoding_to_string(default_encoding),
2085  default_port, default_bind_address,
2086  default_port, default_hostname, default_protocol,
2087  default_document_root, default_cache_limit, default_max_num_threads,
2088  default_log_level, default_log_path, default_query_log_path,
2089  default_config_path, default_default_command_version,
2090  (long long int)default_default_match_escalation_threshold,
2091  default_dest);
2092 }
2093 
2094 int
2095 main(int argc, char **argv)
2096 {
2097  const char *port_arg = NULL, *encoding_arg = NULL,
2098  *max_num_threads_arg = NULL, *log_level_arg = NULL,
2099  *bind_address_arg = NULL, *hostname_arg = NULL, *protocol_arg = NULL,
2100  *log_path_arg = NULL, *query_log_path_arg = NULL,
2101  *cache_limit_arg = NULL, *document_root_arg = NULL,
2102  *default_command_version_arg = NULL,
2103  *default_match_escalation_threshold_arg = NULL,
2104  *input_fd_arg = NULL, *output_fd_arg = NULL,
2105  *working_directory_arg = NULL;
2106  const char *config_path = NULL;
2107  int exit_code = EXIT_SUCCESS;
2108  int i, mode = mode_alone;
2109  uint32_t cache_limit = 0;
2110  static grn_str_getopt_opt opts[] = {
2111  {'p', "port", NULL, 0, GETOPT_OP_NONE},
2112  {'e', "encoding", NULL, 0, GETOPT_OP_NONE},
2113  {'t', "max-threads", NULL, 0, GETOPT_OP_NONE},
2114  {'h', "help", NULL, mode_usage, GETOPT_OP_UPDATE},
2115  {'c', NULL, NULL, mode_client, GETOPT_OP_UPDATE},
2116  {'d', NULL, NULL, mode_daemon, GETOPT_OP_UPDATE},
2117  {'s', NULL, NULL, mode_server, GETOPT_OP_UPDATE},
2118  {'l', "log-level", NULL, 0, GETOPT_OP_NONE},
2119  {'i', "server-id", NULL, 0, GETOPT_OP_NONE},
2120  {'q', NULL, NULL, MODE_USE_QL, GETOPT_OP_ON},
2121  {'n', NULL, NULL, MODE_NEW_DB, GETOPT_OP_ON},
2122  {'\0', "protocol", NULL, 0, GETOPT_OP_NONE},
2123  {'\0', "version", NULL, mode_version, GETOPT_OP_UPDATE},
2124  {'\0', "log-path", NULL, 0, GETOPT_OP_NONE},
2125  {'\0', "query-log-path", NULL, 0, GETOPT_OP_NONE},
2126  {'\0', "pid-path", NULL, 0, GETOPT_OP_NONE},
2127  {'\0', "config-path", NULL, 0, GETOPT_OP_NONE},
2128  {'\0', "show-config", NULL, mode_config, GETOPT_OP_UPDATE},
2129  {'\0', "cache-limit", NULL, 0, GETOPT_OP_NONE},
2130  {'\0', "file", NULL, 0, GETOPT_OP_NONE},
2131  {'\0', "document-root", NULL, 0, GETOPT_OP_NONE},
2132  {'\0', "default-command-version", NULL, 0, GETOPT_OP_NONE},
2133  {'\0', "default-match-escalation-threshold", NULL, 0, GETOPT_OP_NONE},
2134  {'\0', "bind-address", NULL, 0, GETOPT_OP_NONE},
2135  {'\0', "input-fd", NULL, 0, GETOPT_OP_NONE},
2136  {'\0', "output-fd", NULL, 0, GETOPT_OP_NONE},
2137  {'\0', "working-directory", NULL, 0, GETOPT_OP_NONE},
2138  {'\0', NULL, NULL, 0, 0}
2139  };
2140  opts[0].arg = &port_arg;
2141  opts[1].arg = &encoding_arg;
2142  opts[2].arg = &max_num_threads_arg;
2143  opts[7].arg = &log_level_arg;
2144  opts[8].arg = &hostname_arg;
2145  opts[11].arg = &protocol_arg;
2146  opts[13].arg = &log_path_arg;
2147  opts[14].arg = &query_log_path_arg;
2148  opts[15].arg = &pid_file_path;
2149  opts[16].arg = &config_path;
2150  opts[18].arg = &cache_limit_arg;
2151  opts[19].arg = &input_path;
2152  opts[20].arg = &document_root_arg;
2153  opts[21].arg = &default_command_version_arg;
2154  opts[22].arg = &default_match_escalation_threshold_arg;
2155  opts[23].arg = &bind_address_arg;
2156  opts[24].arg = &input_fd_arg;
2157  opts[25].arg = &output_fd_arg;
2158  opts[26].arg = &working_directory_arg;
2159 
2160  reset_ready_notify_pipe();
2161 
2162  init_default_settings();
2163 
2164  /* only for parsing --config-path. */
2165  i = grn_str_getopt(argc, argv, opts, &mode);
2166  if (i < 0) {
2167  show_usage(stderr);
2168  return EXIT_FAILURE;
2169  }
2170 
2171  if (config_path) {
2172  const config_file_status status = config_file_load(config_path, opts, &mode);
2173  if (status == CONFIG_FILE_FOPEN_ERROR) {
2174  fprintf(stderr, "%s: can't open config file: %s (%s)\n",
2175  argv[0], config_path, strerror(errno));
2176  return EXIT_FAILURE;
2177  } else if (status != CONFIG_FILE_SUCCESS) {
2178  fprintf(stderr, "%s: failed to parse config file: %s (%s)\n",
2179  argv[0], config_path,
2180  (status == CONFIG_FILE_FORMAT_ERROR) ? "Invalid format" : strerror(errno));
2181  return EXIT_FAILURE;
2182  }
2183  } else if (*default_config_path) {
2184  const config_file_status status =
2185  config_file_load(default_config_path, opts, &mode);
2186  if (status != CONFIG_FILE_SUCCESS && status != CONFIG_FILE_FOPEN_ERROR) {
2187  fprintf(stderr, "%s: failed to parse config file: %s (%s)\n",
2188  argv[0], default_config_path,
2189  (status == CONFIG_FILE_FORMAT_ERROR) ? "Invalid format" : strerror(errno));
2190  return EXIT_FAILURE;
2191  }
2192  }
2193 
2194  if (working_directory_arg) {
2195  if (chdir(working_directory_arg) == -1) {
2196  fprintf(stderr, "%s: failed to change directory: %s: %s\n",
2197  argv[0], working_directory_arg, strerror(errno));
2198  return EXIT_FAILURE;
2199  }
2200  }
2201 
2202  /* ignore mode option in config file */
2203  mode = (mode == mode_error) ? default_mode :
2204  ((mode & ~MODE_MASK) | default_mode);
2205 
2206  i = grn_str_getopt(argc, argv, opts, &mode);
2207  if (i < 0) { mode = mode_error; }
2208  switch (mode & MODE_MASK) {
2209  case mode_version :
2210  show_version();
2211  return EXIT_SUCCESS;
2212  case mode_usage :
2213  show_usage(output);
2214  return EXIT_SUCCESS;
2215  case mode_config :
2216  show_config(output, opts, mode & ~MODE_MASK);
2217  return EXIT_SUCCESS;
2218  case mode_error :
2219  show_usage(stderr);
2220  return EXIT_FAILURE;
2221  }
2222 
2223  if (port_arg) {
2224  const char * const end = port_arg + strlen(port_arg);
2225  const char *rest = NULL;
2226  const int value = grn_atoi(port_arg, end, &rest);
2227  if (rest != end || value <= 0 || value > 65535) {
2228  fprintf(stderr, "invalid port number: <%s>\n", port_arg);
2229  return EXIT_FAILURE;
2230  }
2231  port = value;
2232  } else {
2233  port = default_port;
2234  }
2235 
2236  if (encoding_arg) {
2237  switch (*encoding_arg) {
2238  case 'n' :
2239  case 'N' :
2240  encoding = GRN_ENC_NONE;
2241  break;
2242  case 'e' :
2243  case 'E' :
2244  encoding = GRN_ENC_EUC_JP;
2245  break;
2246  case 'u' :
2247  case 'U' :
2248  encoding = GRN_ENC_UTF8;
2249  break;
2250  case 's' :
2251  case 'S' :
2252  encoding = GRN_ENC_SJIS;
2253  break;
2254  case 'l' :
2255  case 'L' :
2256  encoding = GRN_ENC_LATIN1;
2257  break;
2258  case 'k' :
2259  case 'K' :
2260  encoding = GRN_ENC_KOI8R;
2261  break;
2262  default:
2263  encoding = GRN_ENC_DEFAULT;
2264  break;
2265  }
2266  } else {
2267  encoding = GRN_ENC_DEFAULT;
2268  }
2269 
2270  if (!grn_document_root) {
2271  grn_document_root = default_document_root;
2272  }
2273 
2274  if (protocol_arg) {
2275  switch (*protocol_arg) {
2276  case 'g' :
2277  case 'G' :
2278  do_client = g_client;
2279  do_server = g_server;
2280  break;
2281  case 'h' :
2282  case 'H' :
2283  do_client = g_client;
2284  do_server = h_server;
2285  break;
2286  case 'm' :
2287  case 'M' :
2288  do_client = g_client;
2289  do_server = g_server;
2290  break;
2291  default :
2292  do_client = g_client;
2293  do_server = g_server;
2294  break;
2295  }
2296  } else {
2297  do_client = g_client;
2298  do_server = g_server;
2299  }
2300 
2301  if (log_path_arg) {
2302  grn_default_logger_set_path(log_path_arg);
2303  }
2304 
2305  if (query_log_path_arg) {
2306  grn_default_query_logger_set_path(query_log_path_arg);
2307  }
2308 
2309  if (log_level_arg) {
2310  const char * const end = log_level_arg + strlen(log_level_arg);
2311  const char *rest = NULL;
2312  const int value = grn_atoi(log_level_arg, end, &rest);
2313  if (end != rest || value < 0 || value > 9) {
2314  fprintf(stderr, "invalid log level: <%s>\n", log_level_arg);
2315  return EXIT_FAILURE;
2316  }
2317  log_level = value;
2318  } else {
2319  log_level = default_log_level;
2320  }
2322 
2323  if (max_num_threads_arg) {
2324  const char * const end = max_num_threads_arg + strlen(max_num_threads_arg);
2325  const char *rest = NULL;
2326  const uint32_t value = grn_atoui(max_num_threads_arg, end, &rest);
2327  if (end != rest || value < 1 || value > 100) {
2328  fprintf(stderr, "invalid max number of threads: <%s>\n",
2329  max_num_threads_arg);
2330  return EXIT_FAILURE;
2331  }
2332  max_nfthreads = value;
2333  } else {
2334  max_nfthreads = default_max_num_threads;
2335  }
2336 
2337  if (input_path) {
2338  if (!freopen(input_path, "r", stdin)) {
2339  fprintf(stderr, "can't open input file: %s (%s)\n",
2340  input_path, strerror(errno));
2341  return EXIT_FAILURE;
2342  }
2343  batchmode = GRN_TRUE;
2344  } else {
2345  if (input_fd_arg) {
2346  const char * const end = input_fd_arg + strlen(input_fd_arg);
2347  const char *rest = NULL;
2348  const int input_fd = grn_atoi(input_fd_arg, end, &rest);
2349  if (rest != end || input_fd == 0) {
2350  fprintf(stderr, "invalid input FD: <%s>\n", input_fd_arg);
2351  return EXIT_FAILURE;
2352  }
2353  if (dup2(input_fd, STDIN_FILENO) == -1) {
2354  fprintf(stderr, "can't open input FD: %d (%s)\n",
2355  input_fd, strerror(errno));
2356  return EXIT_FAILURE;
2357  }
2358  batchmode = GRN_TRUE;
2359  } else {
2360  if (argc - i > 1) {
2361  batchmode = GRN_TRUE;
2362  } else {
2363  batchmode = !isatty(0);
2364  }
2365  }
2366  }
2367 
2368  if (output_fd_arg) {
2369  const char * const end = output_fd_arg + strlen(output_fd_arg);
2370  const char *rest = NULL;
2371  const int output_fd = grn_atoi(output_fd_arg, end, &rest);
2372  if (rest != end || output_fd == 0) {
2373  fprintf(stderr, "invalid output FD: <%s>\n", output_fd_arg);
2374  return EXIT_FAILURE;
2375  }
2376  output = fdopen(output_fd, "w");
2377  if (!output) {
2378  fprintf(stderr, "can't open output FD: %d (%s)\n",
2379  output_fd, strerror(errno));
2380  return EXIT_FAILURE;
2381  }
2382  }
2383 
2384 
2385  if (bind_address_arg) {
2386  const size_t bind_address_length = strlen(bind_address_arg);
2387  if (bind_address_length > HOST_NAME_MAX) {
2388  fprintf(stderr, "too long bind address: %s (%u bytes):"
2389  " must not be longer than %u bytes\n",
2390  bind_address_arg, (unsigned int)bind_address_length, HOST_NAME_MAX);
2391  return EXIT_FAILURE;
2392  }
2393  strcpy(bind_address, bind_address_arg);
2394  } else {
2395  strcpy(bind_address, default_bind_address);
2396  }
2397 
2398  if (hostname_arg) {
2399  const size_t hostname_length = strlen(hostname_arg);
2400  if (hostname_length > HOST_NAME_MAX) {
2401  fprintf(stderr, "too long hostname: %s (%u bytes):"
2402  " must not be longer than %u bytes\n",
2403  hostname_arg, (unsigned int)hostname_length, HOST_NAME_MAX);
2404  return EXIT_FAILURE;
2405  }
2406  strcpy(hostname, hostname_arg);
2407  } else {
2408  strcpy(hostname, default_hostname);
2409  }
2410 
2411  if (document_root_arg) {
2412  grn_document_root = document_root_arg;
2413  }
2414 
2415  if (default_command_version_arg) {
2416  const char * const end = default_command_version_arg
2417  + strlen(default_command_version_arg);
2418  const char *rest = NULL;
2419  const int value = grn_atoi(default_command_version_arg, end, &rest);
2420  if (end != rest || value < GRN_COMMAND_VERSION_MIN ||
2421  value > GRN_COMMAND_VERSION_MAX) {
2422  fprintf(stderr, "invalid command version: <%s>\n",
2423  default_command_version_arg);
2424  return EXIT_FAILURE;
2425  }
2426  switch (value) {
2427  case 1 :
2428  default_command_version = GRN_COMMAND_VERSION_1;
2429  break;
2430  case 2 :
2431  default_command_version = GRN_COMMAND_VERSION_2;
2432  break;
2433  default :
2434  fprintf(stderr, "invalid command version: <%s>\n",
2435  default_command_version_arg);
2436  return EXIT_FAILURE;
2437  }
2438  } else {
2439  default_command_version = default_default_command_version;
2440  }
2441 
2442  if (default_match_escalation_threshold_arg) {
2443  const char * const end = default_match_escalation_threshold_arg
2444  + strlen(default_match_escalation_threshold_arg);
2445  const char *rest = NULL;
2446  const int64_t value = grn_atoll(default_match_escalation_threshold_arg, end, &rest);
2447  if (end != rest) {
2448  fprintf(stderr, "invalid match escalation threshold: <%s>\n",
2449  default_match_escalation_threshold_arg);
2450  return EXIT_FAILURE;
2451  }
2452  default_match_escalation_threshold = value;
2453  } else {
2454  default_match_escalation_threshold = default_default_match_escalation_threshold;
2455  }
2456 
2457  if (cache_limit_arg) {
2458  const char * const end = cache_limit_arg + strlen(cache_limit_arg);
2459  const char *rest = NULL;
2460  const uint32_t value = grn_atoui(cache_limit_arg, end, &rest);
2461  if (end != rest) {
2462  fprintf(stderr, "invalid --cache-limit value: <%s>\n", cache_limit_arg);
2463  return EXIT_FAILURE;
2464  }
2465  cache_limit = value;
2466  }
2467 
2468 #ifdef GRN_WITH_LIBEDIT
2469  if (!batchmode) {
2470  line_editor_init(argc, argv);
2471  }
2472 #endif
2473  if (grn_init()) { return EXIT_FAILURE; }
2474 
2475  grn_set_default_encoding(encoding);
2476 
2477  if (default_command_version_arg) {
2478  grn_set_default_command_version(default_command_version);
2479  }
2480 
2481  if (default_match_escalation_threshold_arg) {
2482  grn_set_default_match_escalation_threshold(default_match_escalation_threshold);
2483  }
2484 
2488 
2489  if (cache_limit_arg) {
2490  grn_cache *cache;
2491  cache = grn_cache_current_get(&grn_gctx);
2492  grn_cache_set_max_n_entries(&grn_gctx, cache, cache_limit);
2493  }
2494 
2495  newdb = (mode & MODE_NEW_DB);
2496  useql = (mode & MODE_USE_QL);
2497  switch (mode & MODE_MASK) {
2498  case mode_alone :
2499  exit_code = do_alone(argc - i, argv + i);
2500  break;
2501  case mode_client :
2502  exit_code = do_client(argc - i, argv + i);
2503  break;
2504  case mode_daemon :
2505  is_daemon_mode = GRN_TRUE;
2506  /* fallthru */
2507  case mode_server :
2508  exit_code = do_server(argc > i ? argv[i] : NULL);
2509  break;
2510  default:
2511  exit_code = EXIT_FAILURE;
2512  break;
2513  }
2514 
2515 #ifdef GRN_WITH_LIBEDIT
2516  if (!batchmode) {
2517  line_editor_fin();
2518  }
2519 #endif
2520  if (output != stdout) {
2521  fclose(output);
2522  }
2523  grn_fin();
2524  return exit_code;
2525 }