00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <config.h>
00021
00022 #include <stdio.h>
00023 #include <stdarg.h>
00024 #include <errno.h>
00025 #include <ctype.h>
00026 #include <signal.h>
00027 #include <time.h>
00028 #include <stdlib.h>
00029 #include <sys/socket.h>
00030 #include <fcntl.h>
00031
00032 #ifdef HAVE_UNISTD_H
00033 #include <unistd.h>
00034 #endif
00035
00036 #ifdef HAVE_STRING_H
00037 #include <string.h>
00038 #endif
00039
00040 #ifdef HAVE_STRINGS_H
00041 #include <strings.h>
00042 #endif
00043
00044 #ifdef HAVE_SYS_TYPES_H
00045 #include <sys/types.h>
00046 #endif
00047
00048 #ifdef HAVE_SYS_STAT_H
00049 #include <sys/stat.h>
00050 #endif
00051
00052 #include "monitor.h"
00053 #include "engine.h"
00054 #include "md5.h"
00055 #include "base64.h"
00056 #include "alert.h"
00057 #include "monit_process.h"
00058
00059
00060 static int is_unsafe(unsigned char *c);
00061 static char *is_str_defined(char *);
00062 static char *is_str_defined_default(char *);
00063
00078
00079
00080
00086 void error(const char *format, ...) {
00087
00088 char msg[1024];
00089 va_list ap;
00090
00091 ASSERT(format);
00092
00093 va_start(ap,format);
00094 vsnprintf(msg, 1024, format, ap);
00095 va_end(ap);
00096
00097 if(Run.have_tty) {
00098
00099 fprintf(stderr,"%s", msg);
00100 fflush(stderr);
00101
00102 } else {
00103
00104 log("%s", msg);
00105
00106 }
00107
00108 }
00109
00110
00114 int is_strdefined(char *p) {
00115
00116 return(p && *p);
00117
00118 }
00119
00120
00126 char *stripfilename(char* path) {
00127
00128 char *fname;
00129
00130 ASSERT(path);
00131
00132 fname= strrchr(path, '/');
00133
00134 return(fname ? ++fname : path);
00135
00136 }
00137
00138
00143 void chomp(char *string) {
00144
00145 char *p;
00146
00147 ASSERT(string);
00148
00149 if((p= strchr(string, '\r')) || (p= strchr(string, '\n'))) *p= 0;
00150
00151 }
00152
00153
00159 char *trim(char *s) {
00160
00161 ASSERT(s);
00162
00163 ltrim(s);
00164 rtrim(s);
00165
00166 return s;
00167
00168 }
00169
00170
00176 char *ltrim(char *s) {
00177
00178 char *t= s;
00179
00180 ASSERT(s);
00181
00182 while(*t==' ' || *t=='\t' || *t=='\r' || *t=='\n') t++;
00183
00184 return strcpy(s, t);
00185
00186 }
00187
00188
00194 char *rtrim(char *s) {
00195
00196 char *t= s;
00197
00198 ASSERT(s);
00199
00200 while(*s) s++;
00201 while(*--s==' ' || *s=='\t' || *s=='\r' || *s=='\n') *s= '\0';
00202
00203 return t;
00204
00205 }
00206
00207
00214 char *trim_quotes(char *s) {
00215
00216 char *t= s;
00217 char tmp=0;
00218
00219 ASSERT(s);
00220
00221 while(*t==39 || *t==34) {
00222
00223 tmp=*t;
00224 t++;
00225 break;
00226
00227 }
00228
00229 if(t[strlen(t)-1]==tmp) t[strlen(t)-1]= '\0';
00230
00231 return strcpy(s, t);
00232
00233 }
00234
00235
00242 int starts_with(char *a, char *b) {
00243
00244 if((!a || !b) || *a!=*b) return FALSE;
00245
00246 while(*a && *b) {
00247
00248 if(*a++ != *b++) return FALSE;
00249
00250 }
00251
00252 return TRUE;
00253
00254 }
00255
00256
00262 void handle_string_escapes(char *buf) {
00263
00264 int editpos;
00265 int insertpos;
00266
00267 ASSERT(buf);
00268
00269 for(editpos=insertpos=0; *(buf+editpos)!='\0'; editpos++, insertpos++) {
00270
00271 if(*(buf+editpos) == '\\' ) {
00272
00273 switch(*(buf+editpos+1)) {
00274
00275 case 'n':
00276 *(buf+insertpos)='\n';
00277 editpos++;
00278 break;
00279
00280 case 't':
00281 *(buf+insertpos)='\t';
00282 editpos++;
00283 break;
00284
00285 case 'r':
00286 *(buf+insertpos)='\r';
00287 editpos++;
00288 break;
00289
00290 case ' ':
00291 *(buf+insertpos)=' ';
00292 editpos++;
00293 break;
00294
00295 case '\\':
00296 *(buf+insertpos)='\\';
00297 editpos++;
00298 break;
00299
00300 default:
00301 *(buf+insertpos)=*(buf+editpos);
00302
00303 }
00304
00305 } else {
00306
00307 *(buf+insertpos)=*(buf+editpos);
00308
00309 }
00310
00311 }
00312 *(buf+insertpos)='\0';
00313
00314 }
00315
00316
00321 Process_T get_process(char *name) {
00322
00323 Process_T p;
00324
00325 ASSERT(name);
00326
00327 for(p= processlist; p; p= p->next) {
00328 if(is(p->name, name)) {
00329 return p;
00330 }
00331 }
00332
00333 return NULL;
00334
00335 }
00336
00337
00343 int exist_process(char *name) {
00344
00345 Process_T p;
00346
00347 ASSERT(name);
00348
00349 for(p= processlist; p; p= p->next)
00350 if(is(p->name, name))
00351 return TRUE;
00352
00353 return FALSE;
00354
00355 }
00356
00357
00361 void printrunlist() {
00362
00363 printf("Runtime constants:\n");
00364 printf(" %-18s = %s\n", "Control file", is_str_defined(Run.controlfile));
00365 printf(" %-18s = %s\n", "Log file", is_str_defined(Run.logfile));
00366 printf(" %-18s = %s\n", "Pid file", is_str_defined(Run.pidfile));
00367 printf(" %-18s = %s\n", "Debug", Run.debug?"True":"False");
00368 printf(" %-18s = %s\n", "Log", Run.dolog?"True":"False");
00369 printf(" %-18s = %s\n", "Use syslog", Run.use_syslog?"True":"False");
00370 printf(" %-18s = %s\n", "Is Daemon", Run.isdaemon?"True":"False");
00371 printf(" %-18s = %s\n", "Use process engine", Run.doprocess?"True":"False");
00372 printf(" %-18s = %d seconds\n", "Poll time", Run.polltime);
00373 printf(" %-18s = %s\n", "Mail server", is_str_defined(Run.mailserver));
00374 printf(" %-18s = %s\n", "Mail from", is_str_defined(Run.MailFormat.from));
00375 printf(" %-18s = %s\n", "Mail subject",
00376 is_str_defined(Run.MailFormat.subject));
00377 printf(" %-18s = %-.20s%s\n", "Mail message",
00378 Run.MailFormat.message?
00379 Run.MailFormat.message:"(not defined)",
00380 Run.MailFormat.message?"..(truncated)":"");
00381
00382 printf(" %-18s = %s\n", "Start monit httpd", Run.dohttpd?"True":"False");
00383
00384
00385
00386 if(Run.dohttpd) {
00387
00388 printf(" %-18s = %s\n", "httpd bind address",
00389 Run.bind_addr?Run.bind_addr:"Any/All");
00390 printf(" %-18s = %d\n", "httpd portnumber", Run.httpdport);
00391 printf(" %-18s = %s\n", "Use ssl encryption", Run.httpdssl?"True":"False");
00392
00393 if(Run.httpdssl) {
00394
00395 printf(" %-18s = %s\n", "PEM key/cert file", Run.httpsslpem);
00396
00397 if(Run.httpsslclientpem!=NULL) {
00398 printf(" %-18s = %s\n", "Client cert file", Run.httpsslclientpem);
00399 } else {
00400 printf(" %-18s = %s\n", "Client cert file", "None");
00401 }
00402
00403 printf(" %-18s = %s\n", "Allow self certs",
00404 Run.allowselfcert?"True":"False");
00405
00406 }
00407
00408 printf(" %-18s = %s\n", "httpd auth. style",
00409 Run.Auth.defined&&has_hosts_allow()?
00410 "Basic Authentication and Host allow list":
00411 Run.Auth.defined?"Basic Authentication":
00412 has_hosts_allow()?"Host allow list":
00413 "No authentication!");
00414
00415 }
00416
00417 printf("\n");
00418
00419 }
00420
00421
00426 void printprocess(Process_T p) {
00427
00428 Port_T n;
00429 Mail_T r;
00430 Resource_T q;
00431 Checksum_T c;
00432 Timestamp_T t;
00433 Dependant_T d;
00434
00435 ASSERT(p);
00436
00437 printf("%-21s = %s\n", "Process Name", p->name);
00438 printf(" %-20s = %s\n", "Group", is_str_defined(p->group));
00439 printf(" %-20s = %s\n", "Pid file", p->pidfile);
00440 printf(" %-20s = %s\n", "Monitoring mode", modenames[p->mode]);
00441 if(p->start)
00442 printf(" %-20s = %s\n", "Start program", is_str_defined(p->start->arg[0]));
00443 if(p->stop)
00444 printf(" %-20s = %s\n", "Stop program", is_str_defined(p->stop->arg[0]));
00445
00446 for(c= p->checksumlist; c; c= c->next) {
00447
00448 printf(" %-20s = %s %s\n", "Checksum", c->md5, c->file);
00449
00450 }
00451
00452 for(d= p->dependantlist; d; d= d->next)
00453 if(d->dependant != NULL)
00454 printf(" %-20s = %s\n", "Depends on Process", d->dependant);
00455
00456 if(! p->portlist) {
00457
00458 printf(" %-20s = (not defined)\n", "Host:Port");
00459
00460 } else {
00461
00462 for(n= p->portlist; n; n= n->next) {
00463
00464 if(n->family == AF_INET) {
00465
00466 if(n->ssl != NULL) {
00467
00468 printf(" %-20s = %s:%d%s [protocol %s via SSL]\n", "Host:Port",
00469 n->hostname, n->port, n->request?n->request:"",
00470 n->protocol->name);
00471
00472 if(n->certmd5 != NULL) {
00473
00474 printf(" %-20s = %s\n",
00475 "Server cert md5 sum",
00476 n->certmd5);
00477
00478 }
00479
00480 } else {
00481
00482 printf(" %-20s = %s:%d%s [protocol %s]\n", "Host:Port",
00483 n->hostname, n->port, n->request?n->request:"",
00484 n->protocol->name);
00485
00486 }
00487
00488 } else if(n->family == AF_UNIX) {
00489
00490 printf(" %-20s = %s [protocol %s]\n", "Unix Socket",
00491 n->pathname, n->protocol->name);
00492
00493 }
00494
00495 }
00496
00497 }
00498
00499 for(t= p->timestamplist; t; t= t->next) {
00500
00501 printf(" %-20s = if %s %s %d second(s) then %s\n",
00502 "Timestamp",
00503 t->pathname,
00504 operatornames[t->operator],
00505 t->time,
00506 actionnames[t->action]);
00507
00508 }
00509
00510 if(! p->resourcelist) {
00511
00512 printf(" %-20s = (not defined)\n", "Resource Limits");
00513
00514 }
00515
00516 for(q= p->resourcelist; q; q= q->next) {
00517
00518 switch(q->resource_id) {
00519
00520 case RESOURCE_ID_CPU_PERCENT:
00521
00522 printf(" %-20s = if %s %.1f%% for %d cycle(s) then %s\n",
00523 "CPU usage limit",
00524 operatornames[q->operator],
00525 q->limit/10.0, q->max_cycle, actionnames[q->action]);
00526 break;
00527
00528 case RESOURCE_ID_MEM_PERCENT:
00529
00530 printf(" %-20s = if %s %.1f%% for %d cycle(s) then %s\n",
00531 "Memory usage limit",
00532 operatornames[q->operator], q->limit/10.0, q->max_cycle,
00533 actionnames[q->action]);
00534 break;
00535
00536 case RESOURCE_ID_MEM_KBYTE:
00537
00538 printf(" %-20s = if %s %ldkB for %d cycle(s) then %s\n",
00539 "Memory amount limit",
00540 operatornames[q->operator], q->limit, q->max_cycle,
00541 actionnames[q->action]);
00542 break;
00543
00544 case RESOURCE_ID_LOAD1:
00545
00546 printf(" %-20s = if %s %.1f for %d cycle(s) then %s\n",
00547 "Load avg. (1min)",
00548 operatornames[q->operator], q->limit/10.0, q->max_cycle,
00549 actionnames[q->action]);
00550 break;
00551
00552 case RESOURCE_ID_LOAD5:
00553
00554 printf(" %-20s = if %s %.1f for %d cycle(s) then %s\n",
00555 "Load avg. (5min)",
00556 operatornames[q->operator], q->limit/10.0, q->max_cycle,
00557 actionnames[q->action]);
00558 break;
00559
00560 case RESOURCE_ID_LOAD15:
00561
00562 printf(" %-20s = if %s %.1f for %d cycle(s) then %s\n",
00563 "Load avg. (15min)",
00564 operatornames[q->operator], q->limit/10.0, q->max_cycle,
00565 actionnames[q->action]);
00566 break;
00567
00568 }
00569 }
00570
00571 if(p->def_every)
00572
00573 printf(" %-20s = Check process every %d cycles\n", "Every", p->every);
00574
00575 else {
00576
00577 printf(" %-20s = (not defined)\n", "Every");
00578
00579 }
00580
00581 if(p->def_timeout) {
00582
00583 printf(" %-20s = Do timeout if %d restart within %d cycles\n",
00584 "Timeout", p->to_start, p->to_cycle);
00585
00586 } else {
00587
00588 printf(" %-20s = (not defined)\n", "Timeout");
00589
00590 }
00591
00592 for(r= p->maillist; r; r= r->next) {
00593
00594 printf(" %-20s = %s\n", "Alert mail to", is_str_defined(r->to));
00595 printf(" %-18s = %s\n", "alert from", is_str_defined_default(r->from));
00596 printf(" %-18s = %s\n", "alert subject",
00597 is_str_defined_default(r->subject));
00598 printf(" %-18s = %-.20s%s\n", "alert message",
00599 is_str_defined_default(r->message),
00600 r->message?"..":"");
00601 printf(" %-18s = %s\n", "alert on timeout",
00602 r->alert_on_timeout?"yes":"no");
00603 printf(" %-18s = %s\n", "alert on restart",
00604 r->alert_on_restart?"yes":"no");
00605 printf(" %-18s = %s\n", "alert on checksum",
00606 r->alert_on_checksum?"yes":"no");
00607 printf(" %-18s = %s\n", "alert on resource",
00608 r->alert_on_resource?"yes":"no");
00609 printf(" %-18s = %s\n", "alert on stop",
00610 r->alert_on_stop?"yes":"no");
00611 printf(" %-18s = %s\n", "alert on timestamp",
00612 r->alert_on_timestamp?"yes":"no");
00613
00614 }
00615
00616 printf("\n");
00617
00618 }
00619
00620
00624 void printprocesslist() {
00625
00626 Process_T p;
00627 char ruler[STRLEN];
00628
00629 printf("The process list contains the following entries:\n\n");
00630
00631 for(p= processlist; p; p= p->next) {
00632
00633 printprocess(p);
00634
00635 }
00636
00637 memset(ruler, '-', STRLEN);
00638 printf("%-.79s\n", ruler);
00639
00640 }
00641
00642
00649 pid_t get_pid(char *pidfile) {
00650
00651 FILE *file= NULL;
00652 int pid= -1;
00653
00654 ASSERT(pidfile);
00655
00656 if(! exist_file(pidfile)) {
00657
00658 return(FALSE);
00659
00660 }
00661
00662 if(! isreg_file(pidfile)) {
00663
00664 log("%s: pidfile '%s' is not a regular file\n",prog, pidfile);
00665 return(FALSE);
00666
00667 }
00668
00669 if((file= fopen(pidfile,"r")) == (FILE *)NULL) {
00670
00671 log("%s: Error opening the pidfile '%s' -- %s\n",
00672 prog, pidfile, STRERROR);
00673 return(FALSE);
00674
00675 }
00676
00677 fscanf(file, "%d", &pid);
00678 fclose(file);
00679
00680 if(pid == -1) {
00681
00682 log("%s: pidfile `%s' does not contain a valid pidnumber\n",
00683 prog, pidfile);
00684
00685 return (FALSE);
00686
00687 }
00688
00689 return (pid_t)pid;
00690
00691 }
00692
00693
00698 int is_process_running(Process_T p) {
00699
00700 pid_t pid;
00701 int kill_return= 0;
00702
00703 ASSERT(p);
00704
00705 errno= 0;
00706
00707 if((pid= get_pid(p->pidfile))) {
00708
00709 if((kill_return= getpgid(pid)) > 0 || errno == EPERM)
00710
00711 return pid;
00712
00713 }
00714
00715 memset(p->procinfo, 0, sizeof *(p->procinfo));
00716
00717 return FALSE;
00718
00719 }
00720
00721
00728 char *get_RFC1123date(long *date) {
00729
00730 struct tm *tm_now;
00731 char D[STRLEN];
00732 time_t now= (date && (*date>0))?*date:time(&now);
00733 long timezone_h;
00734 long timezone_m;
00735 int datelen;
00736
00737 time(&now);
00738 tzset();
00739 tm_now = localtime(&now);
00740
00741 #if HAVE_STRUCT_TM_TM_GMTOFF
00742 timezone_h = tm_now->tm_gmtoff/3600;
00743 timezone_m = abs(tm_now->tm_gmtoff/60)%60;
00744 #else
00745 timezone_h = -(timezone/3600)+(tm_now->tm_isdst>0);
00746 timezone_m = abs(timezone/60)%60;
00747 #endif
00748
00749 datelen=strftime(D, STRLEN-7, "%a, %d %b %Y %H:%M:%S", tm_now);
00750
00751 if(! datelen) {
00752
00753 memset(D, 0, STRLEN);
00754
00755 } else {
00756
00757 snprintf(D+datelen, 7, " %+03ld%02ld", timezone_h, timezone_m);
00758
00759 }
00760
00761 return xstrdup(D);
00762
00763 }
00764
00765
00770 char *get_ctime() {
00771
00772 time_t now;
00773 char *buf = (char*)xmalloc(STRLEN);
00774
00775 time(&now);
00776 snprintf(buf, STRLEN, "%s", ctime(&now));
00777
00778 chomp(buf);
00779
00780 return buf;
00781
00782 }
00783
00790 char *get_process_uptime(char *pidfile) {
00791
00792 time_t ctime;
00793
00794 ASSERT(pidfile);
00795
00796 if( (ctime= get_timestamp(pidfile, S_IFREG)) ) {
00797
00798 time_t now= time(&now);
00799 time_t since= now-ctime;
00800
00801 return get_uptime(since);
00802
00803 }
00804
00805 return xstrdup("");
00806
00807 }
00808
00809
00816 char *get_uptime(time_t delta) {
00817
00818 static int min= 60;
00819 static int hour= 3600;
00820 static int day= 86400;
00821 long rest_d;
00822 long rest_h;
00823 long rest_m;
00824 char buf[STRLEN];
00825 char *p= buf;
00826
00827 *buf= 0;
00828
00829 if((rest_d= delta/day)>0) {
00830 p+= sprintf(p, "%ldd ", rest_d);
00831 delta-= rest_d*day;
00832 }
00833 if((rest_h= delta/hour)>0 || (rest_d > 0)) {
00834 p+= sprintf(p, "%ldh ", rest_h);
00835 delta-= rest_h*hour;
00836 }
00837
00838 rest_m= delta/min;
00839 p+= sprintf(p, "%ldm ", rest_m);
00840 delta-= rest_m*min;
00841
00842 return xstrdup(buf);
00843
00844 }
00845
00846
00855 int set_md5sum(char **dest, char *file) {
00856
00857 ASSERT(dest);
00858 ASSERT(file);
00859
00860 if(! (*dest= get_md5sum(file)))
00861 return FALSE;
00862
00863 return TRUE;
00864
00865 }
00866
00867
00871 char *get_md5sum(char *file) {
00872
00873 ASSERT(file);
00874
00875 if(isreg_file(file)) {
00876
00877 FILE *f= fopen(file, "r");
00878
00879 if(f) {
00880
00881 int i;
00882 unsigned char md5buf[16];
00883 char result[STRLEN];
00884 char *r= result;
00885
00886 if(md5_stream(f, md5buf)) {
00887
00888 fclose(f);
00889
00890 return NULL;
00891
00892 }
00893
00894 fclose(f);
00895
00896 for(i= 0; i < 16; ++i) {
00897
00898 r+= sprintf(r, "%02x", md5buf[i]);
00899
00900 }
00901
00902 return (xstrdup(result));
00903
00904 }
00905
00906 }
00907
00908 return NULL;
00909
00910 }
00911
00912
00920 int check_md5(char *file, char *sum) {
00921
00922 char *newSum;
00923
00924 ASSERT(file);
00925 ASSERT(sum);
00926
00927 newSum= get_md5sum(file);
00928
00929 if(newSum) {
00930
00931 int rv;
00932
00933 rv= (!strncmp(sum, newSum, 31));
00934 free(newSum);
00935
00936 return (rv);
00937
00938 }
00939
00940 return FALSE;
00941
00942 }
00943
00944
00951 char *url_encode(char *uri) {
00952
00953 static unsigned char hexchars[]= "0123456789ABCDEF";
00954 register int x, y;
00955 unsigned char *str;
00956
00957 ASSERT(uri);
00958
00959 str= (unsigned char *)xmalloc(3 * strlen(uri) + 1);
00960
00961 for(x = 0, y = 0; uri[x]; x++, y++) {
00962
00963 if(is_unsafe(&uri[x])) {
00964 str[y++] = '%';
00965 str[y++] = hexchars[(unsigned char) uri[x] >> 4];
00966 str[y] = hexchars[(unsigned char) uri[x] & 0xf];
00967 } else str[y]= (unsigned char)uri[x];
00968
00969 }
00970
00971 str[y] = '\0';
00972
00973 return ((char *) str);
00974
00975 }
00976
00977
00983 char *get_basic_authentication_header() {
00984
00985 if(Run.Auth.defined) {
00986
00987 char *b64;
00988 char buf[STRLEN];
00989 char *auth= xmalloc(STRLEN+1);
00990
00991 snprintf(buf, STRLEN, "%s:%s", Run.Auth.uname, Run.Auth.passwd);
00992 encode_base64(&b64, strlen(buf), buf);
00993 snprintf(auth, STRLEN, "Authorization: Basic %s\r\n", b64);
00994 free(b64);
00995
00996 return auth;
00997
00998 }
00999
01000 return xstrdup("\r\n");
01001
01002 }
01003
01004
01011 char *format(const char *s, va_list ap) {
01012
01013 int n;
01014 int size= STRLEN;
01015 char *buf= xmalloc(size);
01016
01017 ASSERT(s);
01018
01019 while(TRUE) {
01020
01021 n= vsnprintf(buf, size, s, ap);
01022
01023 if(n > -1 && n < size)
01024 break;
01025
01026 if(n > -1)
01027 size= n+1;
01028 else
01029 size*= 2;
01030
01031 buf= xresize(buf, size);
01032
01033 }
01034
01035 return buf;
01036
01037 }
01038
01039
01044 void redirect_stdfd() {
01045
01046 int i;
01047
01048 Run.have_tty= FALSE;
01049 for(i= 0; i < 3; i++)
01050 if(close(i) == -1 || open("/dev/null", O_RDWR) != i)
01051 error("Cannot reopen standard file descriptor (%d) -- %s\n", i, STRERROR);
01052
01053 }
01054
01055
01056
01057
01058
01063 static char *is_str_defined(char *var) {
01064
01065 return (is_strdefined(var)?var:"(not defined)");
01066
01067 }
01068
01069
01074 static char *is_str_defined_default(char *var) {
01075
01076 return (is_strdefined(var)?var:"(default)");
01077
01078 }
01079
01080
01086 static int is_unsafe(unsigned char *c) {
01087
01088 int i;
01089 static unsigned char unsafe[]= "<>\"#{}|\\^~[]`";
01090
01091 ASSERT(c);
01092
01093 if(33>*c || *c>176)
01094 return TRUE;
01095
01096 if(*c=='%') {
01097 if( isxdigit(*(c + 1)) && isxdigit(*(c + 2)) ) return FALSE;
01098 return TRUE;
01099 }
01100
01101 for(i=0; unsafe[i]; i++)
01102 if(*c==unsafe[i]) return TRUE;
01103
01104 return FALSE;
01105
01106 }
01107