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