1 /*************************************************************************************************
2 * Implementation of mastermod
3 * Copyright (C) 2004-2007 Mikio Hirabayashi
4 * This file is part of Hyper Estraier.
5 * Hyper Estraier is free software; you can redistribute it and/or modify it under the terms of
6 * the GNU Lesser General Public License as published by the Free Software Foundation; either
7 * version 2.1 of the License or any later version. Hyper Estraier is distributed in the hope
8 * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10 * License for more details.
11 * You should have received a copy of the GNU Lesser General Public License along with Hyper
12 * Estraier; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
13 * Boston, MA 02111-1307 USA.
14 *************************************************************************************************/
15
16
17 #include "mastermod.h"
18
19
20 /* private function prototypes */
21 static void log_close(void);
22 static void db_informer(const char *message, void *opaque);
23 static int resdoc_compare_by_score(const void *ap, const void *bp);
24 static int resdoc_compare_by_str_asc(const void *ap, const void *bp);
25 static int resdoc_compare_by_str_desc(const void *ap, const void *bp);
26 static int resdoc_compare_by_num_asc(const void *ap, const void *bp);
27 static int resdoc_compare_by_num_desc(const void *ap, const void *bp);
28
29
30
31 /*************************************************************************************************
32 * pseudo API
33 *************************************************************************************************/
34
35
36 /* The handles of the log file. */
37 FILE *log_fp = NULL;
38
39
40 /* Level of logging. */
41 int log_level = LL_INFO;
42
43
44 /* Open the log file. */
log_open(const char * rootdir,const char * path,int level,int trunc)45 int log_open(const char *rootdir, const char *path, int level, int trunc){
46 char mypath[URIBUFSIZ];
47 assert(rootdir && path);
48 log_level = level;
49 if(log_fp) return TRUE;
50 if((ESTPATHCHR == '/' && path[0] == ESTPATHCHR) ||
51 (ESTPATHCHR == '\\' && ((path[0] >= 'A' && path[0] <= 'Z') ||
52 (path[0] >= 'a' && path[0] <= 'z')) && path[1] == ':' &&
53 path[2] == '\\')){
54 sprintf(mypath, "%s", path);
55 } else {
56 sprintf(mypath, "%s%c%s", rootdir, ESTPATHCHR, path);
57 }
58 if(!(log_fp = fopen(mypath, trunc ? "wb" : "ab"))) return FALSE;
59 if(level == LL_CHECK){
60 fclose(log_fp);
61 log_fp = NULL;
62 return TRUE;
63 }
64 atexit(log_close);
65 return TRUE;
66 }
67
68
69 /* Print formatted string into the log file. */
log_print(int level,const char * format,...)70 void log_print(int level, const char *format, ...){
71 static pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
72 va_list ap, aq;
73 const char *lvstr;
74 char *date;
75 if(level < log_level) return;
76 if(pthread_mutex_lock(&mymutex) != 0) return;
77 va_start(ap, format);
78 est_va_copy(aq, ap);
79 switch(level){
80 case LL_DEBUG: lvstr = "DEBUG"; break;
81 case LL_INFO: lvstr = "INFO"; break;
82 case LL_WARN: lvstr = "WARN"; break;
83 default: lvstr = "ERROR"; break;
84 }
85 date = cbdatestrwww(time(NULL), 0);
86 printf("%s\t%s\t", date, lvstr);
87 vprintf(format, ap);
88 putchar('\n');
89 fflush(stdout);
90 if(log_fp){
91 fprintf(log_fp, "%s\t%s\t", date, lvstr);
92 vfprintf(log_fp, format, aq);
93 fputc('\n', log_fp);
94 fflush(log_fp);
95 }
96 free(date);
97 va_end(aq);
98 va_end(ap);
99 pthread_mutex_unlock(&mymutex);
100 }
101
102
103 /* Rotete the log file. */
log_rotate(const char * rootdir,const char * path)104 int log_rotate(const char *rootdir, const char *path){
105 FILE *ifp, *ofp;
106 char mypath[URIBUFSIZ], *wp, iobuf[IOBUFSIZ];
107 int err, year, month, day, hour, minute, second, len;
108 assert(rootdir && path);
109 if(!log_fp || fflush(log_fp) == -1) return FALSE;
110 err = FALSE;
111 wp = mypath;
112 if((ESTPATHCHR == '/' && path[0] == ESTPATHCHR) ||
113 (ESTPATHCHR == '\\' && ((path[0] >= 'A' && path[0] <= 'Z') ||
114 (path[0] >= 'a' && path[0] <= 'z')) && path[1] == ':' &&
115 path[2] == '\\')){
116 wp += sprintf(wp, "%s", path);
117 } else {
118 wp += sprintf(wp, "%s%c%s", rootdir, ESTPATHCHR, path);
119 }
120 if(!(ifp = fopen(mypath, "rb"))) return FALSE;
121 cbcalendar(-1, 0, &year, &month, &day, &hour, &minute, &second);
122 sprintf(wp, "-%04d%02d%02d%02d%02d%02d", year, month, day, hour, minute, second);
123 if(!(ofp = fopen(mypath, "wb"))){
124 fclose(ifp);
125 return FALSE;
126 }
127 while((len = fread(iobuf, 1, IOBUFSIZ, ifp)) > 0){
128 fwrite(iobuf, 1, len, ofp);
129 }
130 if(fclose(ofp) == -1) err = TRUE;
131 if(fclose(ifp) == -1) err = TRUE;
132 if(fseek(log_fp, 0, SEEK_SET) == -1 || fflush(log_fp) == -1) err = TRUE;
133 if(ftruncate(fileno(log_fp), 0) == -1) err = TRUE;
134 return err ? FALSE : TRUE;
135 }
136
137
138 /* Initialize the root directory. */
master_init(const char * rootdir)139 int master_init(const char *rootdir){
140 DEPOT *depot;
141 FILE *ofp;
142 char path[URIBUFSIZ];
143 int err;
144 assert(rootdir);
145 if(est_mkdir(rootdir) == -1 && errno != EEXIST) return FALSE;
146 err = FALSE;
147 sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, METAFILE);
148 if((depot = dpopen(path, DP_OWRITER | DP_OCREAT | DP_OTRUNC, MINIBNUM))){
149 if(!dpput(depot, MMKMAGIC, -1, MMKMAGVAL, -1, DP_DKEEP)) err = TRUE;
150 if(!dpclose(depot)) err = TRUE;
151 } else {
152 err = TRUE;
153 }
154 sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, CONFFILE);
155 if((ofp = fopen(path, "wb")) != NULL){
156 fprintf(ofp, "# binding address of TCP (0.0.0.0 means every address)\n");
157 fprintf(ofp, "bindaddr: 0.0.0.0\n");
158 fprintf(ofp, "\n");
159 fprintf(ofp, "# port number of TCP\n");
160 fprintf(ofp, "portnum: 1978\n");
161 fprintf(ofp, "\n");
162 fprintf(ofp, "# public URL (absolute URL)\n");
163 fprintf(ofp, "publicurl:\n");
164 fprintf(ofp, "\n");
165 fprintf(ofp, "# running mode (1:normal, 2:readonly)\n");
166 fprintf(ofp, "runmode: 1\n");
167 fprintf(ofp, "\n");
168 fprintf(ofp, "# authorization mode (1:none, 2:admin, 3:all)\n");
169 fprintf(ofp, "authmode: 2\n");
170 fprintf(ofp, "\n");
171 fprintf(ofp, "# maximum length of data to receive (in kilobytes)\n");
172 fprintf(ofp, "recvmax: 1024\n");
173 fprintf(ofp, "\n");
174 fprintf(ofp, "# maximum number of connections at the same time\n");
175 fprintf(ofp, "maxconn: 30\n");
176 fprintf(ofp, "\n");
177 fprintf(ofp, "# idle time to start flushing (in seconds)\n");
178 fprintf(ofp, "idleflush: 20\n");
179 fprintf(ofp, "\n");
180 fprintf(ofp, "# idle time to start synchronizing (in seconds)\n");
181 fprintf(ofp, "idlesync: 300\n");
182 fprintf(ofp, "\n");
183 fprintf(ofp, "# timeout of a session (in seconds)\n");
184 fprintf(ofp, "sessiontimeout: 600\n");
185 fprintf(ofp, "\n");
186 fprintf(ofp, "# timeout of search (in seconds)\n");
187 fprintf(ofp, "searchtimeout: 15\n");
188 fprintf(ofp, "\n");
189 fprintf(ofp, "# maximum number of documents to send\n");
190 fprintf(ofp, "searchmax: 1000\n");
191 fprintf(ofp, "\n");
192 fprintf(ofp, "# maximum depth of meta search\n");
193 fprintf(ofp, "searchdepth: 5\n");
194 fprintf(ofp, "\n");
195 fprintf(ofp, "# whether to rate URI for scoring (0:no, 1:yes)\n");
196 fprintf(ofp, "rateuri: 1\n");
197 fprintf(ofp, "\n");
198 fprintf(ofp, "# merge method of meta search (1:score, 2:score and rank, 3:rank)\n");
199 fprintf(ofp, "mergemethod: 2\n");
200 fprintf(ofp, "\n");
201 fprintf(ofp, "# host name of the proxy\n");
202 fprintf(ofp, "proxyhost:\n");
203 fprintf(ofp, "\n");
204 fprintf(ofp, "# port number of the proxy\n");
205 fprintf(ofp, "proxyport:\n");
206 fprintf(ofp, "\n");
207 fprintf(ofp, "# path of the log file (relative path or absolute path)\n");
208 fprintf(ofp, "logfile: %s\n", LOGFILE);
209 fprintf(ofp, "\n");
210 fprintf(ofp, "# logging level (1:debug, 2:information, 3:warning, 4:error, 5:none)\n");
211 fprintf(ofp, "loglevel: 2\n");
212 fprintf(ofp, "\n");
213 fprintf(ofp, "# command for backup (absolute path of a command)\n");
214 fprintf(ofp, "backupcmd:\n");
215 fprintf(ofp, "\n");
216 fprintf(ofp, "# scale prediction (1:small, 2:medium, 3:large, 4:huge)\n");
217 fprintf(ofp, "scalepred: 2\n");
218 fprintf(ofp, "\n");
219 fprintf(ofp, "# score expression (1:void, 2:char, 3:int, 4:asis)\n");
220 fprintf(ofp, "scoreexpr: 2\n");
221 fprintf(ofp, "\n");
222 fprintf(ofp, "# attribute indexes (attribute name and data type)\n");
223 fprintf(ofp, "attrindex: @mdate{{!}}seq\n");
224 fprintf(ofp, "attrindex: @title{{!}}str\n");
225 fprintf(ofp, "\n");
226 fprintf(ofp, "# document root directory (absolute path of a directory to be public)\n");
227 fprintf(ofp, "docroot:\n");
228 fprintf(ofp, "\n");
229 fprintf(ofp, "# index file (name of directory index files)\n");
230 fprintf(ofp, "indexfile:\n");
231 fprintf(ofp, "\n");
232 fprintf(ofp, "# decimal IP addresses of trusted nodes\n");
233 fprintf(ofp, "trustednode:\n");
234 fprintf(ofp, "\n");
235 fprintf(ofp, "# whether to deny all nodes except for trusted nodes (0:no, 1:yes)\n");
236 fprintf(ofp, "denyuntrusted: 0\n");
237 fprintf(ofp, "\n");
238 fprintf(ofp, "# maximum size of the index cache (in megabytes)\n");
239 fprintf(ofp, "cachesize: 64\n");
240 fprintf(ofp, "\n");
241 fprintf(ofp, "# maximum number of cached records for document attributes\n");
242 fprintf(ofp, "cacheanum: 8192\n");
243 fprintf(ofp, "\n");
244 fprintf(ofp, "# maximum number of cached records for document texts\n");
245 fprintf(ofp, "cachetnum: 1024\n");
246 fprintf(ofp, "\n");
247 fprintf(ofp, "# maximum number of cached records for occurrence results\n");
248 fprintf(ofp, "cachernum: 256\n");
249 fprintf(ofp, "\n");
250 fprintf(ofp, "# name of the attribute of the special cache\n");
251 fprintf(ofp, "specialcache:\n");
252 fprintf(ofp, "\n");
253 fprintf(ofp, "# lower limit of cache usage to use the helper\n");
254 fprintf(ofp, "helpershift: 0.9\n");
255 fprintf(ofp, "\n");
256 fprintf(ofp, "# maximum number of expansion of wild cards\n");
257 fprintf(ofp, "wildmax: 256\n");
258 fprintf(ofp, "\n");
259 fprintf(ofp, "# text size limitation of indexing documents (in kilobytes)\n");
260 fprintf(ofp, "limittextsize: 128\n");
261 fprintf(ofp, "\n");
262 fprintf(ofp, "# whole width of the snippet of each shown document\n");
263 fprintf(ofp, "snipwwidth: 480\n");
264 fprintf(ofp, "\n");
265 fprintf(ofp, "# width of strings picked up from the beginning of the text\n");
266 fprintf(ofp, "sniphwidth: 96\n");
267 fprintf(ofp, "\n");
268 fprintf(ofp, "# width of strings picked up around each highlighted word\n");
269 fprintf(ofp, "snipawidth: 96\n");
270 fprintf(ofp, "\n");
271 fprintf(ofp, "# whether to check documents by scanning (0:no, 1:yes)\n");
272 fprintf(ofp, "scancheck: 1\n");
273 fprintf(ofp, "\n");
274 fprintf(ofp, "# number of keywords for similarity search (0 means disabled)\n");
275 fprintf(ofp, "smlrvnum: 32\n");
276 fprintf(ofp, "\n");
277 fprintf(ofp, "# number of documents for delay of keyword extraction\n");
278 fprintf(ofp, "extdelay: 4096\n");
279 fprintf(ofp, "\n");
280 fprintf(ofp, "# e-mail address of the administrator\n");
281 fprintf(ofp, "adminemail: magnus@hyperestraier.gov\n");
282 fprintf(ofp, "\n");
283 fprintf(ofp, "# expressions to replace the URI of each document\n");
284 fprintf(ofp, "uireplace: ^file:///home/mikio/public_html/{{!}}http://localhost/\n");
285 fprintf(ofp, "uireplace: /index\\.html?${{!}}/\n");
286 fprintf(ofp, "\n");
287 fprintf(ofp, "# extra attributes to be shown\n");
288 fprintf(ofp, "uiextattr: @author|Author\n");
289 fprintf(ofp, "uiextattr: @mdate|Modification Date\n");
290 fprintf(ofp, "\n");
291 fprintf(ofp, "# mode of phrase form"
292 " (1:usual, 2:simplified, 3:rough, 4:union: 5:intersection)\n");
293 fprintf(ofp, "uiphraseform: 2\n");
294 fprintf(ofp, "\n");
295 fprintf(ofp, "# tuning parameters for similarity search\n");
296 fprintf(ofp, "uismlrtune: 16 1024 4096\n");
297 fprintf(ofp, "\n");
298 if(fclose(ofp) == EOF) err = TRUE;
299 } else {
300 err = TRUE;
301 }
302 sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, PIDFILE);
303 unlink(path);
304 sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, STOPFILE);
305 unlink(path);
306 sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, USERFILE);
307 if((ofp = fopen(path, "wb")) != NULL){
308 if(fclose(ofp) == EOF) err = TRUE;
309 } else {
310 err = TRUE;
311 }
312 sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, LOGFILE);
313 if((ofp = fopen(path, "wb")) != NULL){
314 if(fclose(ofp) == EOF) err = TRUE;
315 } else {
316 err = TRUE;
317 }
318 sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, NODEDIR);
319 est_rmdir_rec(path);
320 if(est_mkdir(path) == -1) err = TRUE;
321 sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, SESSDIR);
322 est_rmdir_rec(path);
323 if(est_mkdir(path) == -1) err = TRUE;
324 return err ? FALSE : TRUE;
325 }
326
327
328 /* Get the PID of the process locking the root directory. */
lockerpid(const char * rootdir)329 int lockerpid(const char *rootdir){
330 char path[URIBUFSIZ], *vbuf;
331 int pid;
332 pid = -1;
333 sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, PIDFILE);
334 if((vbuf = cbreadfile(path, NULL)) != NULL){
335 pid = atoi(vbuf);
336 free(vbuf);
337 }
338 return pid;
339 }
340
341
342 /* Check whether a name includes alpha numeric characters only. */
check_alnum_name(const char * name)343 int check_alnum_name(const char *name){
344 while(*name != '\0'){
345 if(!(*name >= 'a' && *name <= 'z') && !(*name >= '0' && *name <= '9') &&
346 *name != '-' && *name != '_' && *name != '.'){
347 return FALSE;
348 }
349 name++;
350 }
351 return TRUE;
352 }
353
354
355 /* Create a user manager object. */
umgr_new(const char * rootdir)356 UMGR *umgr_new(const char *rootdir){
357 UMGR *umgr;
358 assert(rootdir);
359 log_print(LL_INFO, "starting the user manager");
360 umgr = cbmalloc(sizeof(UMGR));
361 umgr->rootdir = cbmemdup(rootdir, -1);
362 umgr->users = cbmapopen();
363 return umgr;
364 }
365
366
367 /* Destroy a user manager object. */
umgr_delete(UMGR * umgr)368 int umgr_delete(UMGR *umgr){
369 USER *user;
370 const char *kbuf, *vbuf;
371 int err;
372 assert(umgr);
373 log_print(LL_INFO, "finishing the user manager");
374 err = FALSE;
375 if(!umgr_sync(umgr)) err = TRUE;
376 cbmapiterinit(umgr->users);
377 while((kbuf = cbmapiternext(umgr->users, NULL)) != NULL){
378 vbuf = cbmapiterval(kbuf, NULL);
379 user = (USER *)vbuf;
380 pthread_mutex_destroy(&(user->mutex));
381 if(user->sess) cbmapclose(user->sess);
382 free(user->misc);
383 free(user->fname);
384 free(user->flags);
385 free(user->passwd);
386 free(user->name);
387 }
388 cbmapclose(umgr->users);
389 free(umgr->rootdir);
390 free(umgr);
391 return err ? FALSE : TRUE;
392 }
393
394
395 /* Load all users from the user file. */
umgr_load(UMGR * umgr)396 int umgr_load(UMGR *umgr){
397 CBLIST *lines, *elems;
398 const char *line;
399 char path[URIBUFSIZ];
400 int i, size;
401 assert(umgr);
402 log_print(LL_INFO, "loading the user list");
403 sprintf(path, "%s%c%s", umgr->rootdir, ESTPATHCHR, USERFILE);
404 if(!(lines = cbreadlines(path))){
405 log_print(LL_ERROR, "loading the user list failed");
406 return FALSE;
407 }
408 for(i = 0; i < cblistnum(lines); i++){
409 line = cblistval(lines, i, &size);
410 if(size < 1) continue;
411 elems = cbsplit(line, size, "\t");
412 if(cblistnum(elems) >= 5){
413 umgr_put(umgr, cblistval(elems, 0, NULL), cblistval(elems, 1, NULL),
414 cblistval(elems, 2, NULL), cblistval(elems, 3, NULL), cblistval(elems, 4, NULL));
415 } else {
416 log_print(LL_WARN, "invalid line: %d", i + 1);
417 }
418 cblistclose(elems);
419 }
420 cblistclose(lines);
421 return TRUE;
422 }
423
424
425 /* Synchronize all users into the user file. */
umgr_sync(UMGR * umgr)426 int umgr_sync(UMGR *umgr){
427 FILE *ofp;
428 USER *user;
429 const char *kbuf, *vbuf;
430 char path[URIBUFSIZ];
431 int err;
432 assert(umgr);
433 log_print(LL_INFO, "saving the user list");
434 sprintf(path, "%s%c%s", umgr->rootdir, ESTPATHCHR, USERFILE);
435 if(!(ofp = fopen(path, "wb"))){
436 log_print(LL_ERROR, "synchronizing the user list failed");
437 return FALSE;
438 }
439 err = FALSE;
440 cbmapiterinit(umgr->users);
441 while((kbuf = cbmapiternext(umgr->users, NULL)) != NULL){
442 vbuf = cbmapiterval(kbuf, NULL);
443 user = (USER *)vbuf;
444 fprintf(ofp, "%s\t%s\t%s\t%s\t%s\n",
445 user->name, user->passwd, user->flags, user->fname, user->misc);
446 }
447 if(fclose(ofp) == EOF){
448 log_print(LL_ERROR, "saving the user list failed");
449 err = TRUE;
450 }
451 return err ? FALSE : TRUE;
452 }
453
454
455 /* Add a user to a user manager object. */
umgr_put(UMGR * umgr,const char * name,const char * passwd,const char * flags,const char * fname,const char * misc)456 int umgr_put(UMGR *umgr, const char *name, const char *passwd, const char *flags,
457 const char *fname, const char *misc){
458 USER user;
459 assert(umgr && name && passwd && flags && fname && misc);
460 log_print(LL_DEBUG, "umgr_put: %s:%s:%s:%s:%s", name, passwd, flags, fname, misc);
461 if(name[0] == '\0' || cbmapget(umgr->users, name, -1, NULL)){
462 log_print(LL_WARN, "duplicated or empty user name: %s", name);
463 return FALSE;
464 }
465 if(!check_alnum_name(name)){
466 log_print(LL_WARN, "invalid user name: %s", name);
467 return FALSE;
468 }
469 user.name = cbmemdup(name, -1);
470 user.passwd = cbmemdup(passwd, -1);
471 user.flags = cbmemdup(flags, -1);
472 user.fname = cbmemdup(fname, -1);
473 user.misc = cbmemdup(misc, -1);
474 user.atime = 0;
475 user.sess = NULL;
476 pthread_mutex_init(&(user.mutex), NULL);
477 cbmapput(umgr->users, name, -1, (char *)&user, sizeof(USER), FALSE);
478 return TRUE;
479 }
480
481
482 /* Remove a user from a user manager object. */
umgr_out(UMGR * umgr,const char * name)483 int umgr_out(UMGR *umgr, const char *name){
484 USER *user;
485 const char *vbuf;
486 assert(umgr && name);
487 log_print(LL_DEBUG, "umgr_out: %s", name);
488 if(!(vbuf = cbmapget(umgr->users, name, -1, NULL))) return FALSE;
489 user = (USER *)vbuf;
490 pthread_mutex_destroy(&(user->mutex));
491 if(user->sess) cbmapclose(user->sess);
492 free(user->misc);
493 free(user->fname);
494 free(user->flags);
495 free(user->passwd);
496 free(user->name);
497 cbmapout(umgr->users, name, -1);
498 return TRUE;
499 }
500
501
502 /* Get a list of names of users in a user manager object. */
umgr_names(UMGR * umgr)503 CBLIST *umgr_names(UMGR *umgr){
504 CBLIST *names;
505 assert(umgr);
506 names = cbmapkeys(umgr->users);
507 cblistsort(names);
508 return names;
509 }
510
511
512 /* Get a user object in a user manager object. */
umgr_get(UMGR * umgr,const char * name)513 USER *umgr_get(UMGR *umgr, const char *name){
514 const char *vbuf;
515 assert(umgr && name);
516 if(!(vbuf = cbmapget(umgr->users, name, -1, NULL))) return NULL;
517 return (USER *)vbuf;
518 }
519
520
521 /* Make the session of a user object. */
user_make_sess(USER * user)522 void user_make_sess(USER *user){
523 assert(user);
524 if(pthread_mutex_lock(&(user->mutex)) != 0) return;
525 if(user->sess) cbmapclose(user->sess);
526 user->sess = cbmapopenex(MINIBNUM);
527 pthread_mutex_unlock(&(user->mutex));
528 }
529
530
531 /* Clear the session of a user object. */
user_clear_sess(USER * user)532 void user_clear_sess(USER *user){
533 assert(user);
534 if(pthread_mutex_lock(&(user->mutex)) != 0) return;
535 if(user->sess) cbmapclose(user->sess);
536 user->sess = NULL;
537 pthread_mutex_unlock(&(user->mutex));
538 }
539
540
541 /* Set a session variable of a user object. */
user_set_sess_val(USER * user,const char * name,const char * value)542 void user_set_sess_val(USER *user, const char *name, const char *value){
543 assert(user && name);
544 if(pthread_mutex_lock(&(user->mutex)) != 0) return;
545 if(user->sess){
546 if(value){
547 cbmapput(user->sess, name, -1, value, -1, TRUE);
548 } else {
549 cbmapout(user->sess, name, -1);
550 }
551 }
552 pthread_mutex_unlock(&(user->mutex));
553 }
554
555
556 /* Get the value of a session variable of a user object. */
user_sess_val(USER * user,const char * name)557 char *user_sess_val(USER *user, const char *name){
558 const char *value;
559 char *rv;
560 assert(user && name);
561 if(pthread_mutex_lock(&(user->mutex)) != 0) return NULL;
562 value = user->sess ? cbmapget(user->sess, name, -1, NULL) : NULL;
563 rv = value ? cbmemdup(value, -1) : NULL;
564 pthread_mutex_unlock(&(user->mutex));
565 return rv;
566 }
567
568
569 /* Create a node manager object. */
nmgr_new(const char * rootdir)570 NMGR *nmgr_new(const char *rootdir){
571 NMGR *nmgr;
572 assert(rootdir);
573 log_print(LL_INFO, "starting the node manager");
574 nmgr = cbmalloc(sizeof(NMGR));
575 nmgr->rootdir = cbmemdup(rootdir, -1);
576 nmgr->nodes = cbmapopenex(MINIBNUM);
577 nmgr->aidxs = cbmapopenex(MINIBNUM);
578 return nmgr;
579 }
580
581
582 /* Destroy a node manager object. */
nmgr_delete(NMGR * nmgr)583 int nmgr_delete(NMGR *nmgr){
584 NODE *node;
585 const char *kbuf, *vbuf;
586 int err, ecode;
587 assert(nmgr);
588 log_print(LL_INFO, "finishing the node manager");
589 err = FALSE;
590 if(!nmgr_sync(nmgr, FALSE)) err = TRUE;
591 cbmapclose(nmgr->aidxs);
592 cbmapiterinit(nmgr->nodes);
593 while((kbuf = cbmapiternext(nmgr->nodes, NULL)) != NULL){
594 vbuf = cbmapiterval(kbuf, NULL);
595 node = (NODE *)vbuf;
596 pthread_mutex_destroy(&(node->mutex));
597 cbmapclose(node->links);
598 cbmapclose(node->users);
599 cbmapclose(node->admins);
600 free(node->label);
601 free(node->name);
602 est_mtdb_close(node->db, &ecode);
603 }
604 cbmapclose(nmgr->nodes);
605 free(nmgr->rootdir);
606 free(nmgr);
607 return err ? FALSE : TRUE;
608 }
609
610
611 /* Load all nodes from the node directory. */
nmgr_load(NMGR * nmgr,int wmode)612 int nmgr_load(NMGR *nmgr, int wmode){
613 CBLIST *list;
614 const char *file;
615 char path[URIBUFSIZ];
616 int i, err;
617 assert(nmgr);
618 sprintf(path, "%s%c%s", nmgr->rootdir, ESTPATHCHR, NODEDIR);
619 if(!(list = cbdirlist(path))){
620 log_print(LL_ERROR, "loading the node directory failed");
621 return FALSE;
622 }
623 err = FALSE;
624 for(i = 0; i < cblistnum(list); i++){
625 file = cblistval(list, i, NULL);
626 if(!strcmp(file, ESTCDIRSTR) || !strcmp(file, ESTPDIRSTR)) continue;
627 if(!nmgr_put(nmgr, file, wmode, 0)) err = TRUE;
628 }
629 cblistclose(list);
630 return err ? FALSE : TRUE;
631 }
632
633
634 /* Synchronize all nodes into the node directory. */
nmgr_sync(NMGR * nmgr,int phis)635 int nmgr_sync(NMGR *nmgr, int phis){
636 NODE *node;
637 CBDATUM *datum;
638 const char *kbuf, *vbuf;
639 int err, ksiz, vsiz;
640 assert(nmgr);
641 log_print(LL_INFO, "synchronizing the node manager");
642 err = FALSE;
643 cbmapiterinit(nmgr->nodes);
644 while((kbuf = cbmapiternext(nmgr->nodes, NULL)) != NULL){
645 vbuf = cbmapiterval(kbuf, NULL);
646 node = (NODE *)vbuf;
647 est_mtdb_add_meta(node->db, NMKNAME, node->name);
648 est_mtdb_add_meta(node->db, NMKLABEL, node->label);
649 datum = cbdatumopen(NULL, -1);
650 cbmapiterinit(node->admins);
651 while((kbuf = cbmapiternext(node->admins, &ksiz)) != NULL){
652 cbdatumcat(datum, kbuf, ksiz);
653 cbdatumcat(datum, "\n", 1);
654 }
655 est_mtdb_add_meta(node->db, NMKADMINS, cbdatumptr(datum));
656 cbdatumclose(datum);
657 datum = cbdatumopen(NULL, -1);
658 cbmapiterinit(node->users);
659 while((kbuf = cbmapiternext(node->users, &ksiz)) != NULL){
660 cbdatumcat(datum, kbuf, ksiz);
661 cbdatumcat(datum, "\n", 1);
662 }
663 est_mtdb_add_meta(node->db, NMKUSERS, cbdatumptr(datum));
664 cbdatumclose(datum);
665 datum = cbdatumopen(NULL, -1);
666 cbmapiterinit(node->links);
667 while((kbuf = cbmapiternext(node->links, &ksiz)) != NULL){
668 vbuf = cbmapiterval(kbuf, &vsiz);
669 cbdatumcat(datum, kbuf, ksiz);
670 cbdatumcat(datum, "\t", 1);
671 cbdatumcat(datum, vbuf, vsiz);
672 cbdatumcat(datum, "\n", 1);
673 }
674 est_mtdb_add_meta(node->db, NMKLINKS, cbdatumptr(datum));
675 cbdatumclose(datum);
676 if(phis && !est_mtdb_sync(node->db)){
677 log_print(LL_ERROR, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db)));
678 err = TRUE;
679 }
680 }
681 return err ? FALSE : TRUE;
682 }
683
684
685 /* Add an attribute index to a node manager object. */
nmgr_add_aidx(NMGR * nmgr,const char * name,const char * type)686 void nmgr_add_aidx(NMGR *nmgr, const char *name, const char *type){
687 int tnum;
688 assert(nmgr && name && type);
689 if(!cbstricmp(type, "str")){
690 tnum = ESTIDXATTRSTR;
691 } else if(!cbstricmp(type, "num")){
692 tnum = ESTIDXATTRNUM;
693 } else {
694 tnum = ESTIDXATTRSEQ;
695 }
696 cbmapput(nmgr->aidxs, name, -1, (char *)&tnum, sizeof(int), TRUE);
697 }
698
699
700 /* Add a node to a node manager object. */
nmgr_put(NMGR * nmgr,const char * name,int wmode,int options)701 int nmgr_put(NMGR *nmgr, const char *name, int wmode, int options){
702 NODE node;
703 ESTMTDB *db;
704 CBLIST *list;
705 const char *cbuf, *pv;
706 char pbuf[URIBUFSIZ], *vbuf;
707 int i, ecode, csiz;
708 assert(nmgr && name);
709 log_print(LL_DEBUG, "nmgr_put: %s", name);
710 if(name[0] == '\0' || cbmapget(nmgr->nodes, name, -1, NULL)){
711 log_print(LL_WARN, "duplicated or empty node name: %s", name);
712 return FALSE;
713 }
714 if(strlen(name) >= NODENAMEMAX || !check_alnum_name(name)){
715 log_print(LL_WARN, "invalid node name: %s", name);
716 return FALSE;
717 }
718 log_print(LL_INFO, "opening a node (%s): %s", wmode ? "WRITER" : "READER", name);
719 sprintf(pbuf, "%s%c%s%c%s", nmgr->rootdir, ESTPATHCHR, NODEDIR, ESTPATHCHR, name);
720 if(!(db = est_mtdb_open(pbuf, wmode ? ESTDBWRITER | ESTDBCREAT | options : ESTDBREADER,
721 &ecode))){
722 log_print(LL_ERROR, "DB-ERROR: %s", est_err_msg(ecode));
723 return FALSE;
724 }
725 est_mtdb_set_informer(db, db_informer, NULL);
726 cbmapiterinit(nmgr->aidxs);
727 while((cbuf = cbmapiternext(nmgr->aidxs, NULL)) != NULL){
728 est_mtdb_add_attr_index(db, cbuf, *(int *)cbmapiterval(cbuf, NULL));
729 }
730 node.db = db;
731 est_mtdb_add_meta(db, NMKNAME, name);
732 node.name = cbmemdup(name, -1);
733 vbuf = est_mtdb_meta(db, NMKLABEL);
734 node.label = vbuf ? vbuf : cbmemdup(name, -1);
735 if((vbuf = est_mtdb_meta(db, NMKADMINS)) != NULL){
736 list = cbsplit(vbuf, -1, "\n");
737 node.admins = cbmapopenex(cblistnum(list) + MINIBNUM);
738 for(i = 0; i < cblistnum(list); i++){
739 cbuf = cblistval(list, i, &csiz);
740 if(csiz < 1) continue;
741 cbmapput(node.admins, cbuf, csiz, "", 0, FALSE);
742 }
743 cblistclose(list);
744 free(vbuf);
745 } else {
746 node.admins = cbmapopenex(MINIBNUM);
747 }
748 if((vbuf = est_mtdb_meta(db, NMKUSERS)) != NULL){
749 list = cbsplit(vbuf, -1, "\n");
750 node.users = cbmapopenex(cblistnum(list) + MINIBNUM);
751 for(i = 0; i < cblistnum(list); i++){
752 cbuf = cblistval(list, i, &csiz);
753 if(csiz < 1) continue;
754 cbmapput(node.users, cbuf, csiz, "", 0, FALSE);
755 }
756 cblistclose(list);
757 free(vbuf);
758 } else {
759 node.users = cbmapopenex(MINIBNUM);
760 }
761 if((vbuf = est_mtdb_meta(db, NMKLINKS)) != NULL){
762 list = cbsplit(vbuf, -1, "\n");
763 node.links = cbmapopenex(cblistnum(list) + MINIBNUM);
764 for(i = 0; i < cblistnum(list); i++){
765 cbuf = cblistval(list, i, NULL);
766 if(!(pv = strchr(cbuf, '\t'))) continue;
767 cbmapput(node.links, cbuf, pv - cbuf, pv + 1, -1, FALSE);
768 }
769 cblistclose(list);
770 free(vbuf);
771 } else {
772 node.links = cbmapopenex(MINIBNUM);
773 }
774 node.mtime = time(NULL);
775 node.dirty = FALSE;
776 pthread_mutex_init(&(node.mutex), NULL);
777 cbmapput(nmgr->nodes, name, -1, (char *)&node, sizeof(NODE), FALSE);
778 return TRUE;
779 }
780
781
782 /* Remove a node from a node manager object. */
nmgr_out(NMGR * nmgr,const char * name)783 int nmgr_out(NMGR *nmgr, const char *name){
784 NODE *node;
785 const char *vbuf;
786 char pbuf[URIBUFSIZ];
787 int err, ecode;
788 assert(nmgr && name);
789 log_print(LL_DEBUG, "nmgr_out: %s", name);
790 if(!(vbuf = cbmapget(nmgr->nodes, name, -1, NULL))) return FALSE;
791 err = FALSE;
792 node = (NODE *)vbuf;
793 pthread_mutex_destroy(&(node->mutex));
794 cbmapclose(node->links);
795 cbmapclose(node->users);
796 cbmapclose(node->admins);
797 free(node->label);
798 free(node->name);
799 if(!est_mtdb_close(node->db, &ecode)){
800 log_print(LL_ERROR, "DB-ERROR: %s", est_err_msg(ecode));
801 err = TRUE;
802 }
803 sprintf(pbuf, "%s%c%s%c%s", nmgr->rootdir, ESTPATHCHR, NODEDIR, ESTPATHCHR, name);
804 if(!est_rmdir_rec(pbuf)){
805 log_print(LL_ERROR, "could not remove a directory");
806 err = TRUE;
807 }
808 cbmapout(nmgr->nodes, name, -1);
809 return TRUE;
810 }
811
812
813 /* Clear registered documents in a node in a node manager object. */
nmgr_clear(NMGR * nmgr,const char * name,int options)814 int nmgr_clear(NMGR *nmgr, const char *name, int options){
815 NODE *node;
816 CBMAP *admins, *users, *links;
817 const char *vbuf;
818 char *label;
819 if(!(vbuf = cbmapget(nmgr->nodes, name, -1, NULL))) return FALSE;
820 node = (NODE *)vbuf;
821 label = cbmemdup(node->label, -1);
822 admins = cbmapdup(node->admins);
823 users = cbmapdup(node->users);
824 links = cbmapdup(node->links);
825 if(!nmgr_out(nmgr, name) || !nmgr_put(nmgr, name, TRUE, options)){
826 cbmapclose(links);
827 cbmapclose(users);
828 cbmapclose(admins);
829 free(label);
830 return FALSE;
831 }
832 if(!(vbuf = cbmapget(nmgr->nodes, name, -1, NULL))) return FALSE;
833 node = (NODE *)vbuf;
834 cbmapclose(node->links);
835 cbmapclose(node->users);
836 cbmapclose(node->admins);
837 free(node->label);
838 node->label = label;
839 node->admins = admins;
840 node->users = users;
841 node->links = links;
842 return TRUE;
843 }
844
845
846 /* Get a list of names of nodes in a noder manager object. */
nmgr_names(NMGR * nmgr)847 CBLIST *nmgr_names(NMGR *nmgr){
848 CBLIST *names;
849 assert(nmgr);
850 names = cbmapkeys(nmgr->nodes);
851 cblistsort(names);
852 return names;
853 }
854
855
856 /* Get a node object in a node manager object. */
nmgr_get(NMGR * nmgr,const char * name)857 NODE *nmgr_get(NMGR *nmgr, const char *name){
858 const char *vbuf;
859 assert(nmgr && name);
860 if(!(vbuf = cbmapget(nmgr->nodes, name, -1, NULL))) return NULL;
861 return (NODE *)vbuf;
862 }
863
864
865 /* Set a link object of a node. */
node_set_link(NODE * node,const char * url,const char * label,int credit)866 void node_set_link(NODE *node, const char *url, const char *label, int credit){
867 char *vbuf;
868 assert(node && url);
869 if(!label || credit < 0){
870 cbmapout(node->links, url, -1);
871 return;
872 }
873 vbuf = cbsprintf("%s\t%d", label, credit);
874 cbmapput(node->links, url, -1, vbuf, -1, TRUE);
875 free(vbuf);
876 }
877
878
879 /* Create a read-write lock object. */
rwlock_new(void)880 RWLOCK *rwlock_new(void){
881 RWLOCK *rwlock;
882 rwlock = cbmalloc(sizeof(RWLOCK));
883 rwlock->readers = 0;
884 rwlock->writers = 0;
885 pthread_mutex_init(&(rwlock->mutex), NULL);
886 pthread_cond_init(&(rwlock->cond), NULL);
887 return rwlock;
888 }
889
890
891 /* Destroy a read-write lock object. */
rwlock_delete(RWLOCK * rwlock)892 void rwlock_delete(RWLOCK *rwlock){
893 assert(rwlock);
894 pthread_cond_destroy(&(rwlock->cond));
895 pthread_mutex_destroy(&(rwlock->mutex));
896 free(rwlock);
897 }
898
899
900 /* Lock a read-write lock object. */
rwlock_lock(RWLOCK * rwlock,int wmode)901 int rwlock_lock(RWLOCK *rwlock, int wmode){
902 assert(rwlock);
903 if(pthread_mutex_lock(&(rwlock->mutex)) != 0) return FALSE;
904 if(wmode){
905 while(rwlock->writers > 0 || rwlock->readers > 0){
906 pthread_cond_wait(&(rwlock->cond), &(rwlock->mutex));
907 }
908 rwlock->writers++;
909 } else {
910 while(rwlock->writers > 0){
911 pthread_cond_wait(&(rwlock->cond), &(rwlock->mutex));
912 }
913 rwlock->readers++;
914 }
915 pthread_mutex_unlock(&(rwlock->mutex));
916 return TRUE;
917 }
918
919
920 /* Unlock a read-write lock object. */
rwlock_unlock(RWLOCK * rwlock)921 int rwlock_unlock(RWLOCK *rwlock){
922 assert(rwlock);
923 if(pthread_mutex_lock(&(rwlock->mutex)) != 0) return FALSE;
924 if(rwlock->writers > 0){
925 rwlock->writers--;
926 pthread_cond_broadcast(&(rwlock->cond));
927 pthread_mutex_unlock(&(rwlock->mutex));
928 } else {
929 rwlock->readers--;
930 if(rwlock->readers < 1) pthread_cond_signal(&(rwlock->cond));
931 pthread_mutex_unlock(&(rwlock->mutex));
932 }
933 return TRUE;
934 }
935
936
937 /* Get the number of readers locking a read-write lock object. */
rwlock_rnum(RWLOCK * rwlock)938 int rwlock_rnum(RWLOCK *rwlock){
939 assert(rwlock);
940 return rwlock->readers;
941 }
942
943
944 /* Create a result map object. */
resmap_new(void)945 RESMAP *resmap_new(void){
946 RESMAP *resmap;
947 resmap = cbmalloc(sizeof(RESMAP));
948 resmap->uris = cbmapopen();
949 pthread_mutex_init(&(resmap->mutex), NULL);
950 return resmap;
951 }
952
953
954 /* Destroy a result map object. */
resmap_delete(RESMAP * resmap)955 void resmap_delete(RESMAP *resmap){
956 RESDOC *resdoc;
957 const char *kbuf, *vbuf;
958 assert(resmap);
959 cbmapiterinit(resmap->uris);
960 while((kbuf = cbmapiternext(resmap->uris, NULL)) != NULL){
961 vbuf = cbmapiterval(kbuf, NULL);
962 resdoc = (RESDOC *)vbuf;
963 if(resdoc->doc) est_doc_delete(resdoc->doc);
964 if(resdoc->attrs) cbmapclose(resdoc->attrs);
965 if(resdoc->body) free(resdoc->body);
966 }
967 pthread_mutex_destroy(&(resmap->mutex));
968 cbmapclose(resmap->uris);
969 free(resmap);
970 }
971
972
973 /* Add a result document data to a result map object. */
resmap_put(RESMAP * resmap,int score,ESTDOC * doc,CBMAP * attrs,char * body)974 void resmap_put(RESMAP *resmap, int score, ESTDOC *doc, CBMAP *attrs, char *body){
975 RESDOC resdoc;
976 const char *uri, *vbuf;
977 assert(resmap);
978 uri = NULL;
979 if(doc) uri = est_doc_attr(doc, ESTDATTRURI);
980 if(attrs) uri = cbmapget(attrs, ESTDATTRURI, -1, NULL);
981 if(!uri || pthread_mutex_lock(&(resmap->mutex)) != 0){
982 if(doc) est_doc_delete(doc);
983 if(attrs) cbmapclose(attrs);
984 if(body) free(body);
985 return;
986 }
987 if((vbuf = cbmapget(resmap->uris, uri, -1, NULL)) != NULL){
988 if(((RESDOC *)vbuf)->score >= score){
989 if(doc) est_doc_delete(doc);
990 if(attrs) cbmapclose(attrs);
991 if(body) free(body);
992 } else {
993 if(((RESDOC *)vbuf)->doc) est_doc_delete(((RESDOC *)vbuf)->doc);
994 if(((RESDOC *)vbuf)->attrs) cbmapclose(((RESDOC *)vbuf)->attrs);
995 if(((RESDOC *)vbuf)->body) free(((RESDOC *)vbuf)->body);
996 resdoc.score = score;
997 resdoc.doc = doc;
998 resdoc.attrs = attrs;
999 resdoc.body = body;
1000 resdoc.value = NULL;
1001 cbmapput(resmap->uris, uri, -1, (char *)&resdoc, sizeof(RESDOC), TRUE);
1002 }
1003 } else {
1004 resdoc.score = score;
1005 resdoc.doc = doc;
1006 resdoc.attrs = attrs;
1007 resdoc.body = body;
1008 cbmapput(resmap->uris, uri, -1, (char *)&resdoc, sizeof(RESDOC), FALSE);
1009 }
1010 pthread_mutex_unlock(&(resmap->mutex));
1011 }
1012
1013
1014 /* Get a list object of result objects in a result map objects. */
resmap_list(RESMAP * resmap,int * nump,const char * order,const char * distinct)1015 RESDOC **resmap_list(RESMAP *resmap, int *nump, const char *order, const char *distinct){
1016 RESDOC **resdocs, *resdoc;
1017 CBMAP *umap;
1018 const char *kbuf, *vbuf, *otype, *rp;
1019 char *oname, *wp;
1020 int i, onlen, dnlen, nnum;
1021 time_t tval;
1022 assert(resmap && nump);
1023 if(pthread_mutex_lock(&(resmap->mutex)) != 0){
1024 *nump = 0;
1025 return cbmalloc(1);
1026 }
1027 *nump = cbmaprnum(resmap->uris);
1028 resdocs = cbmalloc(*nump * sizeof(RESDOC) + 1);
1029 cbmapiterinit(resmap->uris);
1030 for(i = 0; i < *nump; i++){
1031 kbuf = cbmapiternext(resmap->uris, NULL);
1032 vbuf = cbmapiterval(kbuf, NULL);
1033 resdocs[i] = (RESDOC *)vbuf;
1034 }
1035 if(order){
1036 oname = cbmemdup(order, -1);
1037 cbstrtrim(oname);
1038 otype = ESTORDSTRA;
1039 if((wp = strchr(oname, ' ')) != NULL){
1040 *wp = '\0';
1041 rp = wp + 1;
1042 while(*rp == ' '){
1043 rp++;
1044 }
1045 otype = rp;
1046 }
1047 onlen = strlen(oname);
1048 for(i = 0; i < *nump; i++){
1049 if(resdocs[i]->doc){
1050 resdocs[i]->value = est_doc_attr(resdocs[i]->doc, oname);
1051 } else {
1052 resdocs[i]->value = cbmapget(resdocs[i]->attrs, oname, onlen, NULL);
1053 }
1054 if(!resdocs[i]->value) resdocs[i]->value = "";
1055 }
1056 if(!cbstricmp(otype, ESTORDSTRA)){
1057 qsort(resdocs, *nump, sizeof(RESDOC *), resdoc_compare_by_str_asc);
1058 } else if(!cbstricmp(otype, ESTORDSTRD)){
1059 qsort(resdocs, *nump, sizeof(RESDOC *), resdoc_compare_by_str_desc);
1060 } else if(!cbstricmp(otype, ESTORDNUMA)){
1061 for(i = 0; i < *nump; i++){
1062 tval = cbstrmktime(resdocs[i]->value);
1063 resdocs[i]->value = (void *)tval;
1064 }
1065 qsort(resdocs, *nump, sizeof(RESDOC *), resdoc_compare_by_num_asc);
1066 } else if(!cbstricmp(otype, ESTORDNUMD)){
1067 for(i = 0; i < *nump; i++){
1068 tval = cbstrmktime(resdocs[i]->value);
1069 resdocs[i]->value = (void *)tval;
1070 }
1071 qsort(resdocs, *nump, sizeof(RESDOC *), resdoc_compare_by_num_desc);
1072 }
1073 free(oname);
1074 } else {
1075 qsort(resdocs, *nump, sizeof(RESDOC *), resdoc_compare_by_score);
1076 }
1077 if(distinct){
1078 dnlen = strlen(distinct);
1079 umap = cbmapopenex(*nump + 1);
1080 nnum = 0;
1081 for(i = 0; i < *nump; i++){
1082 resdoc = resdocs[i];
1083 if(resdoc->doc){
1084 vbuf = est_doc_attr(resdoc->doc, distinct);
1085 printf("*");
1086 } else {
1087 vbuf = cbmapget(resdoc->attrs, distinct, dnlen, NULL);
1088 }
1089 if(!vbuf) vbuf = "";
1090 if(cbmapput(umap, vbuf, -1, "", 0, FALSE)) resdocs[nnum++] = resdoc;
1091 }
1092 *nump = nnum;
1093 cbmapclose(umap);
1094 }
1095 pthread_mutex_unlock(&(resmap->mutex));
1096 return resdocs;
1097 }
1098
1099
1100 /* Be a daemon process. */
be_daemon(const char * curdir)1101 int be_daemon(const char *curdir){
1102 #if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
1103 PROCESS_INFORMATION pi;
1104 STARTUPINFO si;
1105 assert(curdir);
1106 FreeConsole();
1107 if(getenv("ESTDAEMON")){
1108 Sleep(1000);
1109 if(chdir(curdir) == -1) return FALSE;
1110 } else {
1111 putenv("ESTDAEMON=1");
1112 memset(&si, 0, sizeof(si));
1113 si.cb = sizeof(si);
1114 if(!CreateProcess(NULL, GetCommandLine(), NULL, NULL, FALSE,
1115 BELOW_NORMAL_PRIORITY_CLASS | CREATE_NEW_PROCESS_GROUP |
1116 CREATE_NO_WINDOW | DETACHED_PROCESS, NULL, NULL, &si, &pi))
1117 return FALSE;
1118 CloseHandle(pi.hProcess);
1119 exit(0);
1120 }
1121 return TRUE;
1122 #else
1123 int fd;
1124 assert(curdir);
1125 switch(fork()){
1126 case -1:
1127 return FALSE;
1128 case 0:
1129 break;
1130 default:
1131 exit(0);
1132 }
1133 if(setsid() == -1) return FALSE;
1134 switch(fork()){
1135 case -1:
1136 return FALSE;
1137 case 0:
1138 break;
1139 default:
1140 exit(0);
1141 }
1142 umask(0);
1143 if(chdir(curdir) == -1) return FALSE;
1144 close(0);
1145 close(1);
1146 close(2);
1147 if((fd = open(NULLDEV, O_RDWR, 0)) != -1){
1148 dup2(fd, 0);
1149 dup2(fd, 1);
1150 dup2(fd, 2);
1151 if(fd > 2) close(fd);
1152 }
1153 nice(5);
1154 return TRUE;
1155 #endif
1156 }
1157
1158
1159
1160 /*************************************************************************************************
1161 * private objects
1162 *************************************************************************************************/
1163
1164
1165 /* Close the log file. */
log_close(void)1166 static void log_close(void){
1167 if(log_fp) fclose(log_fp);
1168 }
1169
1170
1171 /* Output the log message of a DB event.
1172 `msg' specifies the log message of a DB event.
1173 `opaque' is simply ignored. */
db_informer(const char * message,void * opaque)1174 static void db_informer(const char *message, void *opaque){
1175 assert(message);
1176 log_print(LL_INFO, "DB-EVENT: %s", message);
1177 }
1178
1179
1180 /* Compare two result document objects by score.
1181 `ap' specifies the pointer to one object.
1182 `ap' specifies the pointer to the other object.
1183 The return value is negative if one is small, positive if one is big, 0 if both are equal. */
resdoc_compare_by_score(const void * ap,const void * bp)1184 static int resdoc_compare_by_score(const void *ap, const void *bp){
1185 assert(ap && bp);
1186 return (*(RESDOC **)bp)->score - (*(RESDOC **)ap)->score;
1187 }
1188
1189
1190 /* Compare two result document objects by attributes of strings for ascending order.
1191 `ap' specifies the pointer to one score.
1192 `bp' specifies the pointer to the other score.
1193 The return value is negative if one is small, positive if one is big, 0 if both are equal. */
resdoc_compare_by_str_asc(const void * ap,const void * bp)1194 static int resdoc_compare_by_str_asc(const void *ap, const void *bp){
1195 assert(ap && bp);
1196 return strcmp((*(RESDOC **)ap)->value, (*(RESDOC **)bp)->value);
1197 }
1198
1199
1200 /* Compare two result document objects by attributes of strings for descending order.
1201 `ap' specifies the pointer to one score.
1202 `bp' specifies the pointer to the other score.
1203 The return value is negative if one is small, positive if one is big, 0 if both are equal. */
resdoc_compare_by_str_desc(const void * ap,const void * bp)1204 static int resdoc_compare_by_str_desc(const void *ap, const void *bp){
1205 assert(ap && bp);
1206 return strcmp((*(RESDOC **)bp)->value, (*(RESDOC **)ap)->value);
1207 }
1208
1209
1210 /* Compare two result document objects by attributes of numbers for ascending order.
1211 `ap' specifies the pointer to one score.
1212 `bp' specifies the pointer to the other score.
1213 The return value is negative if one is small, positive if one is big, 0 if both are equal. */
resdoc_compare_by_num_asc(const void * ap,const void * bp)1214 static int resdoc_compare_by_num_asc(const void *ap, const void *bp){
1215 assert(ap && bp);
1216 return (time_t)(*(RESDOC **)ap)->value - (time_t)(*(RESDOC **)bp)->value;
1217 }
1218
1219
1220 /* Compare two result document objects by attributes of numbers for descending order.
1221 `ap' specifies the pointer to one score.
1222 `bp' specifies the pointer to the other score.
1223 The return value is negative if one is small, positive if one is big, 0 if both are equal. */
resdoc_compare_by_num_desc(const void * ap,const void * bp)1224 static int resdoc_compare_by_num_desc(const void *ap, const void *bp){
1225 assert(ap && bp);
1226 return (time_t)(*(RESDOC **)bp)->value - (time_t)(*(RESDOC **)ap)->value;
1227 }
1228
1229
1230
1231 /* END OF FILE */
1232