1 |
/* |
2 |
* Copyright (C), 2000-2003 by Contributors to the monit codebase. |
3 |
* All Rights Reserved. |
4 |
* |
5 |
* This program is free software; you can redistribute it and/or |
6 |
* modify it under the terms of the GNU General Public License as |
7 |
* published by the Free Software Foundation; either version 2 of the |
8 |
* License, or (at your option) any later version. |
9 |
* |
10 |
* This program is distributed in the hope that it will be useful, but |
11 |
* WITHOUT ANY WARRANTY; without even the implied warranty of |
12 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 |
* General Public License for more details. |
14 |
* |
15 |
* You should have received a copy of the GNU General Public License |
16 |
* along with this program; if not, write to the Free Software Foundation, |
17 |
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 |
*/ |
19 |
|
20 |
#include <config.h> |
21 |
|
22 |
#include <stdio.h> |
23 |
#include <stdarg.h> |
24 |
#include <errno.h> |
25 |
#include <ctype.h> |
26 |
#include <signal.h> |
27 |
#include <time.h> |
28 |
#include <stdlib.h> |
29 |
#include <sys/socket.h> |
30 |
#include <fcntl.h> |
31 |
|
32 |
#ifdef HAVE_UNISTD_H |
33 |
#include <unistd.h> |
34 |
#endif |
35 |
|
36 |
#ifdef HAVE_STRING_H |
37 |
#include <string.h> |
38 |
#endif |
39 |
|
40 |
#ifdef HAVE_STRINGS_H |
41 |
#include <strings.h> |
42 |
#endif |
43 |
|
44 |
#ifdef HAVE_SYS_TYPES_H |
45 |
#include <sys/types.h> |
46 |
#endif |
47 |
|
48 |
#ifdef HAVE_SYS_STAT_H |
49 |
#include <sys/stat.h> |
50 |
#endif |
51 |
|
52 |
#include "monitor.h" |
53 |
#include "engine.h" |
54 |
#include "md5.h" |
55 |
#include "base64.h" |
56 |
#include "alert.h" |
57 |
#include "monit_process.h" |
58 |
|
59 |
/* Private prototypes */ |
60 |
static int is_unsafe(unsigned char *c); |
61 |
static char *is_str_defined(char *); |
62 |
static char *is_str_defined_default(char *); |
63 |
|
64 |
/** |
65 |
* General purpose utility methods. |
66 |
* |
67 |
* @author Jan-Henrik Haukeland, <hauk@tildeslash.com> |
68 |
* @author "Martin Pala" <martin.pala@iol.cz> |
69 |
* @author Christian Hopp <chopp@iei.tu-clausthal.de> |
70 |
* @author Michael Amster, <mamster@webeasy.com> |
71 |
* |
72 |
* @version \$Id: util.c,v 1.54 2003/02/11 21:27:39 martinp Exp $ |
73 |
* |
74 |
* @file |
75 |
*/ |
76 |
|
77 |
|
78 |
/* ------------------------------------------------------------------ Public */ |
79 |
|
80 |
|
81 |
/** |
82 |
* Print a formated message to stderr |
83 |
* or to the logfile if no tty exist |
84 |
* @param format A formate (printf-style) string |
85 |
*/ |
86 |
void error(const char *format, ...) { |
87 |
|
88 |
char msg[1024]; |
89 |
va_list ap; |
90 |
|
91 |
ASSERT(format); |
92 |
|
93 |
va_start(ap,format); |
94 |
vsnprintf(msg, 1024, format, ap); |
95 |
va_end(ap); |
96 |
|
97 |
if(Run.have_tty) { |
98 |
|
99 |
fprintf(stderr,"%s", msg); |
100 |
fflush(stderr); |
101 |
|
102 |
} else { |
103 |
|
104 |
log("%s", msg); |
105 |
|
106 |
} |
107 |
|
108 |
} |
109 |
|
110 |
|
111 |
/** |
112 |
* @return TRUE if the string parameter is defined, otherwise FALSE |
113 |
*/ |
114 |
int is_strdefined(char *p) { |
115 |
|
116 |
return(p && *p); |
117 |
|
118 |
} |
119 |
|
120 |
|
121 |
/** |
122 |
* Strip the path and return only the filename |
123 |
* @param path A file path string |
124 |
* @return the basename |
125 |
*/ |
126 |
char *stripfilename(char* path) { |
127 |
|
128 |
char *fname; |
129 |
|
130 |
ASSERT(path); |
131 |
|
132 |
fname= strrchr(path, '/'); |
133 |
|
134 |
return(fname ? ++fname : path); |
135 |
|
136 |
} |
137 |
|
138 |
|
139 |
/** |
140 |
* Removes everything from the first newline (CR|LF) |
141 |
* @param string A string to be chomped |
142 |
*/ |
143 |
void chomp(char *string) { |
144 |
|
145 |
char *p; |
146 |
|
147 |
ASSERT(string); |
148 |
|
149 |
if((p= strchr(string, '\r')) || (p= strchr(string, '\n'))) *p= 0; |
150 |
|
151 |
} |
152 |
|
153 |
|
154 |
/** |
155 |
* Remove leading and trailing space from the string |
156 |
* @param s A string |
157 |
* @return s with leading and trailing spaces removed |
158 |
*/ |
159 |
char *trim(char *s) { |
160 |
|
161 |
ASSERT(s); |
162 |
|
163 |
ltrim(s); |
164 |
rtrim(s); |
165 |
|
166 |
return s; |
167 |
|
168 |
} |
169 |
|
170 |
|
171 |
/** |
172 |
* Remove leading white space [ \t\r\n] from the string. |
173 |
* @param s A string |
174 |
* @return s with leading spaces removed |
175 |
*/ |
176 |
char *ltrim(char *s) { |
177 |
|
178 |
char *t= s; |
179 |
|
180 |
ASSERT(s); |
181 |
|
182 |
while(*t==' ' || *t=='\t' || *t=='\r' || *t=='\n') t++; |
183 |
|
184 |
return strcpy(s, t); |
185 |
|
186 |
} |
187 |
|
188 |
|
189 |
/** |
190 |
* Remove trailing white space [ \t\r\n] from the string |
191 |
* @param s A string |
192 |
* @return s with trailing spaces removed |
193 |
*/ |
194 |
char *rtrim(char *s) { |
195 |
|
196 |
char *t= s; |
197 |
|
198 |
ASSERT(s); |
199 |
|
200 |
while(*s) s++; |
201 |
while(*--s==' ' || *s=='\t' || *s=='\r' || *s=='\n') *s= '\0'; |
202 |
|
203 |
return t; |
204 |
|
205 |
} |
206 |
|
207 |
|
208 |
/** |
209 |
* Remove any enclosing quotes ["'] from the string |
210 |
* @param s A string |
211 |
* @return s with any enclosed quotes removed |
212 |
*/ |
213 |
|
214 |
char *trim_quotes(char *s) { |
215 |
|
216 |
char *t= s; |
217 |
char tmp=0; |
218 |
|
219 |
ASSERT(s); |
220 |
|
221 |
while(*t==39 || *t==34) { |
222 |
|
223 |
tmp=*t; |
224 |
t++; |
225 |
break; |
226 |
|
227 |
} |
228 |
|
229 |
if(t[strlen(t)-1]==tmp) t[strlen(t)-1]= '\0'; |
230 |
|
231 |
return strcpy(s, t); |
232 |
|
233 |
} |
234 |
|
235 |
|
236 |
/** |
237 |
* Return TRUE if the string <i>a</i> starts with the string <i>b</i>. |
238 |
* @param a The string to search for b in |
239 |
* @param b The sub-string to test a against |
240 |
* @return TRUE if a starts with b, otherwise FALSE |
241 |
*/ |
242 |
int starts_with(char *a, char *b) { |
243 |
|
244 |
if((!a || !b) || *a!=*b) return FALSE; |
245 |
|
246 |
while(*a && *b) { |
247 |
|
248 |
if(*a++ != *b++) return FALSE; |
249 |
|
250 |
} |
251 |
|
252 |
return TRUE; |
253 |
|
254 |
} |
255 |
|
256 |
|
257 |
/** |
258 |
* Exchanges \escape sequences in a string |
259 |
* @param buf A string |
260 |
*/ |
261 |
|
262 |
void handle_string_escapes(char *buf) { |
263 |
|
264 |
int editpos; |
265 |
int insertpos; |
266 |
|
267 |
ASSERT(buf); |
268 |
|
269 |
for(editpos=insertpos=0; *(buf+editpos)!='\0'; editpos++, insertpos++) { |
270 |
|
271 |
if(*(buf+editpos) == '\\' ) { |
272 |
|
273 |
switch(*(buf+editpos+1)) { |
274 |
|
275 |
case 'n': |
276 |
*(buf+insertpos)='\n'; |
277 |
editpos++; |
278 |
break; |
279 |
|
280 |
case 't': |
281 |
*(buf+insertpos)='\t'; |
282 |
editpos++; |
283 |
break; |
284 |
|
285 |
case 'r': |
286 |
*(buf+insertpos)='\r'; |
287 |
editpos++; |
288 |
break; |
289 |
|
290 |
case ' ': |
291 |
*(buf+insertpos)=' '; |
292 |
editpos++; |
293 |
break; |
294 |
|
295 |
case '\\': |
296 |
*(buf+insertpos)='\\'; |
297 |
editpos++; |
298 |
break; |
299 |
|
300 |
default: |
301 |
*(buf+insertpos)=*(buf+editpos); |
302 |
|
303 |
} |
304 |
|
305 |
} else { |
306 |
|
307 |
*(buf+insertpos)=*(buf+editpos); |
308 |
|
309 |
} |
310 |
|
311 |
} |
312 |
*(buf+insertpos)='\0'; |
313 |
|
314 |
} |
315 |
|
316 |
|
317 |
/** |
318 |
* @param name A process name as stated in the config file |
319 |
* @return the named process or NULL if not found |
320 |
*/ |
321 |
Process_T get_process(char *name) { |
322 |
|
323 |
Process_T p; |
324 |
|
325 |
ASSERT(name); |
326 |
|
327 |
for(p= processlist; p; p= p->next) { |
328 |
if(is(p->name, name)) { |
329 |
return p; |
330 |
} |
331 |
} |
332 |
|
333 |
return NULL; |
334 |
|
335 |
} |
336 |
|
337 |
|
338 |
/** |
339 |
* @param name A process name as stated in the config file |
340 |
* @return TRUE if the process name exist in the |
341 |
* processlist, otherwise FALSE |
342 |
*/ |
343 |
int exist_process(char *name) { |
344 |
|
345 |
Process_T p; |
346 |
|
347 |
ASSERT(name); |
348 |
|
349 |
for(p= processlist; p; p= p->next) |
350 |
if(is(p->name, name)) |
351 |
return TRUE; |
352 |
|
353 |
return FALSE; |
354 |
|
355 |
} |
356 |
|
357 |
|
358 |
/** |
359 |
* Print the Runtime object |
360 |
*/ |
361 |
void printrunlist() { |
362 |
|
363 |
printf("Runtime constants:\n"); |
364 |
printf(" %-18s = %s\n", "Control file", is_str_defined(Run.controlfile)); |
365 |
printf(" %-18s = %s\n", "Log file", is_str_defined(Run.logfile)); |
366 |
printf(" %-18s = %s\n", "Pid file", is_str_defined(Run.pidfile)); |
367 |
printf(" %-18s = %s\n", "Debug", Run.debug?"True":"False"); |
368 |
printf(" %-18s = %s\n", "Log", Run.dolog?"True":"False"); |
369 |
printf(" %-18s = %s\n", "Use syslog", Run.use_syslog?"True":"False"); |
370 |
printf(" %-18s = %s\n", "Is Daemon", Run.isdaemon?"True":"False"); |
371 |
printf(" %-18s = %s\n", "Use process engine", Run.doprocess?"True":"False"); |
372 |
printf(" %-18s = %d seconds\n", "Poll time", Run.polltime); |
373 |
printf(" %-18s = %s\n", "Mail server", is_str_defined(Run.mailserver)); |
374 |
printf(" %-18s = %s\n", "Mail from", is_str_defined(Run.MailFormat.from)); |
375 |
printf(" %-18s = %s\n", "Mail subject", |
376 |
is_str_defined(Run.MailFormat.subject)); |
377 |
printf(" %-18s = %-.20s%s\n", "Mail message", |
378 |
Run.MailFormat.message? |
379 |
Run.MailFormat.message:"(not defined)", |
380 |
Run.MailFormat.message?"..(truncated)":""); |
381 |
|
382 |
printf(" %-18s = %s\n", "Start monit httpd", Run.dohttpd?"True":"False"); |
383 |
|
384 |
|
385 |
|
386 |
if(Run.dohttpd) { |
387 |
|
388 |
printf(" %-18s = %s\n", "httpd bind address", |
389 |
Run.bind_addr?Run.bind_addr:"Any/All"); |
390 |
printf(" %-18s = %d\n", "httpd portnumber", Run.httpdport); |
391 |
printf(" %-18s = %s\n", "Use ssl encryption", Run.httpdssl?"True":"False"); |
392 |
|
393 |
if(Run.httpdssl) { |
394 |
|
395 |
printf(" %-18s = %s\n", "PEM key/cert file", Run.httpsslpem); |
396 |
|
397 |
if(Run.httpsslclientpem!=NULL) { |
398 |
printf(" %-18s = %s\n", "Client cert file", Run.httpsslclientpem); |
399 |
} else { |
400 |
printf(" %-18s = %s\n", "Client cert file", "None"); |
401 |
} |
402 |
|
403 |
printf(" %-18s = %s\n", "Allow self certs", |
404 |
Run.allowselfcert?"True":"False"); |
405 |
|
406 |
} |
407 |
|
408 |
printf(" %-18s = %s\n", "httpd auth. style", |
409 |
Run.Auth.defined&&has_hosts_allow()? |
410 |
"Basic Authentication and Host allow list": |
411 |
Run.Auth.defined?"Basic Authentication": |
412 |
has_hosts_allow()?"Host allow list": |
413 |
"No authentication!"); |
414 |
|
415 |
} |
416 |
|
417 |
printf("\n"); |
418 |
|
419 |
} |
420 |
|
421 |
|
422 |
/** |
423 |
* Print a process object |
424 |
* @param p A Process_T object |
425 |
*/ |
426 |
void printprocess(Process_T p) { |
427 |
|
428 |
Port_T n; |
429 |
Mail_T r; |
430 |
Resource_T q; |
431 |
Checksum_T c; |
432 |
Timestamp_T t; |
433 |
Dependant_T d; |
434 |
|
435 |
ASSERT(p); |
436 |
|
437 |
printf("%-21s = %s\n", "Process Name", p->name); |
438 |
printf(" %-20s = %s\n", "Group", is_str_defined(p->group)); |
439 |
printf(" %-20s = %s\n", "Pid file", p->pidfile); |
440 |
printf(" %-20s = %s\n", "Monitoring mode", modenames[p->mode]); |
441 |
if(p->start) |
442 |
printf(" %-20s = %s\n", "Start program", is_str_defined(p->start->arg[0])); |
443 |
if(p->stop) |
444 |
printf(" %-20s = %s\n", "Stop program", is_str_defined(p->stop->arg[0])); |
445 |
|
446 |
for(c= p->checksumlist; c; c= c->next) { |
447 |
|
448 |
printf(" %-20s = %s %s\n", "Checksum", c->md5, c->file); |
449 |
|
450 |
} |
451 |
|
452 |
for(d= p->dependantlist; d; d= d->next) |
453 |
if(d->dependant != NULL) |
454 |
printf(" %-20s = %s\n", "Depends on Process", d->dependant); |
455 |
|
456 |
if(! p->portlist) { |
457 |
|
458 |
printf(" %-20s = (not defined)\n", "Host:Port"); |
459 |
|
460 |
} else { |
461 |
|
462 |
for(n= p->portlist; n; n= n->next) { |
463 |
|
464 |
if(n->family == AF_INET) { |
465 |
|
466 |
if(n->ssl != NULL) { |
467 |
|
468 |
printf(" %-20s = %s:%d%s [protocol %s via SSL]\n", "Host:Port", |
469 |
n->hostname, n->port, n->request?n->request:"", |
470 |
n->protocol->name); |
471 |
|
472 |
if(n->certmd5 != NULL) { |
473 |
|
474 |
printf(" %-20s = %s\n", |
475 |
"Server cert md5 sum", |
476 |
n->certmd5); |
477 |
|
478 |
} |
479 |
|
480 |
} else { |
481 |
|
482 |
printf(" %-20s = %s:%d%s [protocol %s]\n", "Host:Port", |
483 |
n->hostname, n->port, n->request?n->request:"", |
484 |
n->protocol->name); |
485 |
|
486 |
} |
487 |
|
488 |
} else if(n->family == AF_UNIX) { |
489 |
|
490 |
printf(" %-20s = %s [protocol %s]\n", "Unix Socket", |
491 |
n->pathname, n->protocol->name); |
492 |
|
493 |
} |
494 |
|
495 |
} |
496 |
|
497 |
} |
498 |
|
499 |
for(t= p->timestamplist; t; t= t->next) { |
500 |
|
501 |
printf(" %-20s = if %s %s %d second(s) then %s\n", |
502 |
"Timestamp", |
503 |
t->pathname, |
504 |
operatornames[t->operator], |
505 |
t->time, |
506 |
actionnames[t->action]); |
507 |
|
508 |
} |
509 |
|
510 |
if(! p->resourcelist) { |
511 |
|
512 |
printf(" %-20s = (not defined)\n", "Resource Limits"); |
513 |
|
514 |
} |
515 |
|
516 |
for(q= p->resourcelist; q; q= q->next) { |
517 |
|
518 |
switch(q->resource_id) { |
519 |
|
520 |
case RESOURCE_ID_CPU_PERCENT: |
521 |
|
522 |
printf(" %-20s = if %s %.1f%% for %d cycle(s) then %s\n", |
523 |
"CPU usage limit", |
524 |
operatornames[q->operator], |
525 |
q->limit/10.0, q->max_cycle, actionnames[q->action]); |
526 |
break; |
527 |
|
528 |
case RESOURCE_ID_MEM_PERCENT: |
529 |
|
530 |
printf(" %-20s = if %s %.1f%% for %d cycle(s) then %s\n", |
531 |
"Memory usage limit", |
532 |
operatornames[q->operator], q->limit/10.0, q->max_cycle, |
533 |
actionnames[q->action]); |
534 |
break; |
535 |
|
536 |
case RESOURCE_ID_MEM_KBYTE: |
537 |
|
538 |
printf(" %-20s = if %s %ldkB for %d cycle(s) then %s\n", |
539 |
"Memory amount limit", |
540 |
operatornames[q->operator], q->limit, q->max_cycle, |
541 |
actionnames[q->action]); |
542 |
break; |
543 |
|
544 |
case RESOURCE_ID_LOAD1: |
545 |
|
546 |
printf(" %-20s = if %s %.1f for %d cycle(s) then %s\n", |
547 |
"Load avg. (1min)", |
548 |
operatornames[q->operator], q->limit/10.0, q->max_cycle, |
549 |
actionnames[q->action]); |
550 |
break; |
551 |
|
552 |
case RESOURCE_ID_LOAD5: |
553 |
|
554 |
printf(" %-20s = if %s %.1f for %d cycle(s) then %s\n", |
555 |
"Load avg. (5min)", |
556 |
operatornames[q->operator], q->limit/10.0, q->max_cycle, |
557 |
actionnames[q->action]); |
558 |
break; |
559 |
|
560 |
case RESOURCE_ID_LOAD15: |
561 |
|
562 |
printf(" %-20s = if %s %.1f for %d cycle(s) then %s\n", |
563 |
"Load avg. (15min)", |
564 |
operatornames[q->operator], q->limit/10.0, q->max_cycle, |
565 |
actionnames[q->action]); |
566 |
break; |
567 |
|
568 |
} |
569 |
} |
570 |
|
571 |
if(p->def_every) |
572 |
|
573 |
printf(" %-20s = Check process every %d cycles\n", "Every", p->every); |
574 |
|
575 |
else { |
576 |
|
577 |
printf(" %-20s = (not defined)\n", "Every"); |
578 |
|
579 |
} |
580 |
|
581 |
if(p->def_timeout) { |
582 |
|
583 |
printf(" %-20s = Do timeout if %d restart within %d cycles\n", |
584 |
"Timeout", p->to_start, p->to_cycle); |
585 |
|
586 |
} else { |
587 |
|
588 |
printf(" %-20s = (not defined)\n", "Timeout"); |
589 |
|
590 |
} |
591 |
|
592 |
for(r= p->maillist; r; r= r->next) { |
593 |
|
594 |
printf(" %-20s = %s\n", "Alert mail to", is_str_defined(r->to)); |
595 |
printf(" %-18s = %s\n", "alert from", is_str_defined_default(r->from)); |
596 |
printf(" %-18s = %s\n", "alert subject", |
597 |
is_str_defined_default(r->subject)); |
598 |
printf(" %-18s = %-.20s%s\n", "alert message", |
599 |
is_str_defined_default(r->message), |
600 |
r->message?"..":""); |
601 |
printf(" %-18s = %s\n", "alert on timeout", |
602 |
r->alert_on_timeout?"yes":"no"); |
603 |
printf(" %-18s = %s\n", "alert on restart", |
604 |
r->alert_on_restart?"yes":"no"); |
605 |
printf(" %-18s = %s\n", "alert on checksum", |
606 |
r->alert_on_checksum?"yes":"no"); |
607 |
printf(" %-18s = %s\n", "alert on resource", |
608 |
r->alert_on_resource?"yes":"no"); |
609 |
printf(" %-18s = %s\n", "alert on stop", |
610 |
r->alert_on_stop?"yes":"no"); |
611 |
printf(" %-18s = %s\n", "alert on timestamp", |
612 |
r->alert_on_timestamp?"yes":"no"); |
613 |
|
614 |
} |
615 |
|
616 |
printf("\n"); |
617 |
|
618 |
} |
619 |
|
620 |
|
621 |
/** |
622 |
* Print all the processes in the processlist |
623 |
*/ |
624 |
void printprocesslist() { |
625 |
|
626 |
Process_T p; |
627 |
char ruler[STRLEN]; |
628 |
|
629 |
printf("The process list contains the following entries:\n\n"); |
630 |
|
631 |
for(p= processlist; p; p= p->next) { |
632 |
|
633 |
printprocess(p); |
634 |
|
635 |
} |
636 |
|
637 |
memset(ruler, '-', STRLEN); |
638 |
printf("%-.79s\n", ruler); |
639 |
|
640 |
} |
641 |
|
642 |
|
643 |
/** |
644 |
* Open and read the pid from the given pidfile. |
645 |
* @param pidfile A pidfile with full path |
646 |
* @return the pid (TRUE) or FALSE if the pid could |
647 |
* not be read from the file |
648 |
*/ |
649 |
pid_t get_pid(char *pidfile) { |
650 |
|
651 |
FILE *file= NULL; |
652 |
int pid= -1; |
653 |
|
654 |
ASSERT(pidfile); |
655 |
|
656 |
if(! exist_file(pidfile)) { |
657 |
|
658 |
return(FALSE); |
659 |
|
660 |
} |
661 |
|
662 |
if(! isreg_file(pidfile)) { |
663 |
|
664 |
log("%s: pidfile '%s' is not a regular file\n",prog, pidfile); |
665 |
return(FALSE); |
666 |
|
667 |
} |
668 |
|
669 |
if((file= fopen(pidfile,"r")) == (FILE *)NULL) { |
670 |
|
671 |
log("%s: Error opening the pidfile '%s' -- %s\n", |
672 |
prog, pidfile, STRERROR); |
673 |
return(FALSE); |
674 |
|
675 |
} |
676 |
|
677 |
fscanf(file, "%d", &pid); |
678 |
fclose(file); |
679 |
|
680 |
if(pid == -1) { |
681 |
|
682 |
log("%s: pidfile `%s' does not contain a valid pidnumber\n", |
683 |
prog, pidfile); |
684 |
|
685 |
return (FALSE); |
686 |
|
687 |
} |
688 |
|
689 |
return (pid_t)pid; |
690 |
|
691 |
} |
692 |
|
693 |
|
694 |
/** |
695 |
* @return TRUE (i.e. the running pid id) if |
696 |
* the process is running, otherwise FALSE |
697 |
*/ |
698 |
int is_process_running(Process_T p) { |
699 |
|
700 |
pid_t pid; |
701 |
int kill_return= 0; |
702 |
|
703 |
ASSERT(p); |
704 |
|
705 |
errno= 0; |
706 |
|
707 |
if((pid= get_pid(p->pidfile))) { |
708 |
|
709 |
if((kill_return= getpgid(pid)) > 0 || errno == EPERM) |
710 |
|
711 |
return pid; |
712 |
|
713 |
} |
714 |
|
715 |
memset(p->procinfo, 0, sizeof *(p->procinfo)); |
716 |
|
717 |
return FALSE; |
718 |
|
719 |
} |
720 |
|
721 |
|
722 |
/** |
723 |
* Returns a (RFC1123) Date string. If the given date |
724 |
* is NULL compute the date now. |
725 |
* @param date |
726 |
* @return a date string alligned with RFC1123 |
727 |
*/ |
728 |
char *get_RFC1123date(long *date) { |
729 |
|
730 |
struct tm *tm_now; |
731 |
char D[STRLEN]; |
732 |
time_t now= (date && (*date>0))?*date:time(&now); |
733 |
long timezone_h; |
734 |
long timezone_m; |
735 |
int datelen; |
736 |
|
737 |
time(&now); |
738 |
tzset(); |
739 |
tm_now = localtime(&now); |
740 |
|
741 |
#if HAVE_STRUCT_TM_TM_GMTOFF |
742 |
timezone_h = tm_now->tm_gmtoff/3600; |
743 |
timezone_m = abs(tm_now->tm_gmtoff/60)%60; |
744 |
#else |
745 |
timezone_h = -(timezone/3600)+(tm_now->tm_isdst>0); |
746 |
timezone_m = abs(timezone/60)%60; |
747 |
#endif |
748 |
|
749 |
datelen=strftime(D, STRLEN-7, "%a, %d %b %Y %H:%M:%S", tm_now); |
750 |
|
751 |
if(! datelen) { |
752 |
|
753 |
memset(D, 0, STRLEN); |
754 |
|
755 |
} else { |
756 |
|
757 |
snprintf(D+datelen, 7, " %+03ld%02ld", timezone_h, timezone_m); |
758 |
|
759 |
} |
760 |
|
761 |
return xstrdup(D); |
762 |
|
763 |
} |
764 |
|
765 |
|
766 |
/** |
767 |
* Get a non \n terminated ctime |
768 |
* @return a ctime string (i.e. date) |
769 |
*/ |
770 |
char *get_ctime() { |
771 |
|
772 |
time_t now; |
773 |
char *buf = (char*)xmalloc(STRLEN); |
774 |
|
775 |
time(&now); |
776 |
snprintf(buf, STRLEN, "%s", ctime(&now)); |
777 |
|
778 |
chomp(buf); |
779 |
|
780 |
return buf; |
781 |
|
782 |
} |
783 |
|
784 |
/** |
785 |
* Compute an uptime string for a process based on the ctime |
786 |
* from the pidfile. The caller must free the returned string. |
787 |
* @param pidfile A process pidfile |
788 |
* @return an uptime string |
789 |
*/ |
790 |
char *get_process_uptime(char *pidfile) { |
791 |
|
792 |
time_t ctime; |
793 |
|
794 |
ASSERT(pidfile); |
795 |
|
796 |
if( (ctime= get_timestamp(pidfile, S_IFREG)) ) { |
797 |
|
798 |
time_t now= time(&now); |
799 |
time_t since= now-ctime; |
800 |
|
801 |
return get_uptime(since); |
802 |
|
803 |
} |
804 |
|
805 |
return xstrdup(""); |
806 |
|
807 |
} |
808 |
|
809 |
|
810 |
/** |
811 |
* Compute an uptime string based on the delta time in seconds. The |
812 |
* caller must free the returned string. |
813 |
* @param delta seconds. |
814 |
* @return an uptime string |
815 |
*/ |
816 |
char *get_uptime(time_t delta) { |
817 |
|
818 |
static int min= 60; |
819 |
static int hour= 3600; |
820 |
static int day= 86400; |
821 |
long rest_d; |
822 |
long rest_h; |
823 |
long rest_m; |
824 |
char buf[STRLEN]; |
825 |
char *p= buf; |
826 |
|
827 |
*buf= 0; |
828 |
|
829 |
if((rest_d= delta/day)>0) { |
830 |
p+= sprintf(p, "%ldd ", rest_d); |
831 |
delta-= rest_d*day; |
832 |
} |
833 |
if((rest_h= delta/hour)>0 || (rest_d > 0)) { |
834 |
p+= sprintf(p, "%ldh ", rest_h); |
835 |
delta-= rest_h*hour; |
836 |
} |
837 |
|
838 |
rest_m= delta/min; |
839 |
p+= sprintf(p, "%ldm ", rest_m); |
840 |
delta-= rest_m*min; |
841 |
|
842 |
return xstrdup(buf); |
843 |
|
844 |
} |
845 |
|
846 |
|
847 |
/** |
848 |
* Compute a md5 checksum for the given file and save the result |
849 |
* in an allocated area pointed to by dest. The caller is responsible |
850 |
* for freeing dest. |
851 |
* @param dest a pointer to an allocated area with the computed md5 sum |
852 |
* @param file The name of file to compute a md5 sum for |
853 |
* @return TRUE if success otherwise FALSE. |
854 |
*/ |
855 |
int set_md5sum(char **dest, char *file) { |
856 |
|
857 |
ASSERT(dest); |
858 |
ASSERT(file); |
859 |
|
860 |
if(! (*dest= get_md5sum(file))) |
861 |
return FALSE; |
862 |
|
863 |
return TRUE; |
864 |
|
865 |
} |
866 |
|
867 |
|
868 |
/** |
869 |
* @return a md5 checksum for the given file, or NULL if error. |
870 |
*/ |
871 |
char *get_md5sum(char *file) { |
872 |
|
873 |
ASSERT(file); |
874 |
|
875 |
if(isreg_file(file)) { |
876 |
|
877 |
FILE *f= fopen(file, "r"); |
878 |
|
879 |
if(f) { |
880 |
|
881 |
int i; |
882 |
unsigned char md5buf[16]; |
883 |
char result[STRLEN]; |
884 |
char *r= result; |
885 |
|
886 |
if(md5_stream(f, md5buf)) { |
887 |
|
888 |
fclose(f); |
889 |
|
890 |
return NULL; |
891 |
|
892 |
} |
893 |
|
894 |
fclose(f); |
895 |
|
896 |
for(i= 0; i < 16; ++i) { |
897 |
|
898 |
r+= sprintf(r, "%02x", md5buf[i]); |
899 |
|
900 |
} |
901 |
|
902 |
return (xstrdup(result)); |
903 |
|
904 |
} |
905 |
|
906 |
} |
907 |
|
908 |
return NULL; |
909 |
|
910 |
} |
911 |
|
912 |
|
913 |
/** |
914 |
* @param file A file to open and compute a md5 checksum for |
915 |
* @param sum A previous checksum computed for this file |
916 |
* @return TRUE if computation of md5sum on the given file, is equal |
917 |
* to the given sum, otherwise return FALSE. FALSE is also returned if |
918 |
* md5_stream fails. |
919 |
*/ |
920 |
int check_md5(char *file, char *sum) { |
921 |
|
922 |
char *newSum; |
923 |
|
924 |
ASSERT(file); |
925 |
ASSERT(sum); |
926 |
|
927 |
newSum= get_md5sum(file); |
928 |
|
929 |
if(newSum) { |
930 |
|
931 |
int rv; |
932 |
|
933 |
rv= (!strncmp(sum, newSum, 31)); |
934 |
free(newSum); |
935 |
|
936 |
return (rv); |
937 |
|
938 |
} |
939 |
|
940 |
return FALSE; |
941 |
|
942 |
} |
943 |
|
944 |
|
945 |
/** |
946 |
* Escape an uri string converting unsafe characters to a hex (%xx) |
947 |
* representation. The caller must free the returned string. |
948 |
* @param uri an uri string |
949 |
* @return the escaped string |
950 |
*/ |
951 |
char *url_encode(char *uri) { |
952 |
|
953 |
static unsigned char hexchars[]= "0123456789ABCDEF"; |
954 |
register int x, y; |
955 |
unsigned char *str; |
956 |
|
957 |
ASSERT(uri); |
958 |
|
959 |
str= (unsigned char *)xmalloc(3 * strlen(uri) + 1); |
960 |
|
961 |
for(x = 0, y = 0; uri[x]; x++, y++) { |
962 |
|
963 |
if(is_unsafe(&uri[x])) { |
964 |
str[y++] = '%'; |
965 |
str[y++] = hexchars[(unsigned char) uri[x] >> 4]; |
966 |
str[y] = hexchars[(unsigned char) uri[x] & 0xf]; |
967 |
} else str[y]= (unsigned char)uri[x]; |
968 |
|
969 |
} |
970 |
|
971 |
str[y] = '\0'; |
972 |
|
973 |
return ((char *) str); |
974 |
|
975 |
} |
976 |
|
977 |
|
978 |
/** |
979 |
* @return a Basic Authentication Authorization string (RFC 2617), |
980 |
* with credentials from the Run object, The string "\r\n" if |
981 |
* credentials are not defined. |
982 |
*/ |
983 |
char *get_basic_authentication_header() { |
984 |
|
985 |
if(Run.Auth.defined) { |
986 |
|
987 |
char *b64; |
988 |
char buf[STRLEN]; |
989 |
char *auth= xmalloc(STRLEN+1); |
990 |
|
991 |
snprintf(buf, STRLEN, "%s:%s", Run.Auth.uname, Run.Auth.passwd); |
992 |
encode_base64(&b64, strlen(buf), buf); |
993 |
snprintf(auth, STRLEN, "Authorization: Basic %s\r\n", b64); |
994 |
free(b64); |
995 |
|
996 |
return auth; |
997 |
|
998 |
} |
999 |
|
1000 |
return xstrdup("\r\n"); |
1001 |
|
1002 |
} |
1003 |
|
1004 |
|
1005 |
/** |
1006 |
* Do printf style format line parsing |
1007 |
* @param s format string |
1008 |
* @param ap variable argument list |
1009 |
* @return buffer with parsed string |
1010 |
*/ |
1011 |
char *format(const char *s, va_list ap) { |
1012 |
|
1013 |
int n; |
1014 |
int size= STRLEN; |
1015 |
char *buf= xmalloc(size); |
1016 |
|
1017 |
ASSERT(s); |
1018 |
|
1019 |
while(TRUE) { |
1020 |
|
1021 |
n= vsnprintf(buf, size, s, ap); |
1022 |
|
1023 |
if(n > -1 && n < size) |
1024 |
break; |
1025 |
|
1026 |
if(n > -1) |
1027 |
size= n+1; |
1028 |
else |
1029 |
size*= 2; |
1030 |
|
1031 |
buf= xresize(buf, size); |
1032 |
|
1033 |
} |
1034 |
|
1035 |
return buf; |
1036 |
|
1037 |
} |
1038 |
|
1039 |
|
1040 |
/** |
1041 |
* Redirect the standard file descriptors to /dev/null and route any |
1042 |
* error messages to the log file. |
1043 |
*/ |
1044 |
void redirect_stdfd() { |
1045 |
|
1046 |
int i; |
1047 |
|
1048 |
Run.have_tty= FALSE; |
1049 |
for(i= 0; i < 3; i++) |
1050 |
if(close(i) == -1 || open("/dev/null", O_RDWR) != i) |
1051 |
error("Cannot reopen standard file descriptor (%d) -- %s\n", i, STRERROR); |
1052 |
|
1053 |
} |
1054 |
|
1055 |
|
1056 |
/* ----------------------------------------------------------------- Private */ |
1057 |
|
1058 |
|
1059 |
/** |
1060 |
* Returns the value of the variable if defined or the String |
1061 |
* "(not defined)" |
1062 |
*/ |
1063 |
static char *is_str_defined(char *var) { |
1064 |
|
1065 |
return (is_strdefined(var)?var:"(not defined)"); |
1066 |
|
1067 |
} |
1068 |
|
1069 |
|
1070 |
/** |
1071 |
* Returns the value of the variable if defined or the String |
1072 |
* "(default)" |
1073 |
*/ |
1074 |
static char *is_str_defined_default(char *var) { |
1075 |
|
1076 |
return (is_strdefined(var)?var:"(default)"); |
1077 |
|
1078 |
} |
1079 |
|
1080 |
|
1081 |
/** |
1082 |
* Returns TRUE if the given char is url unsafe |
1083 |
* @param c A unsigned char |
1084 |
* @return TRUE if the char is in the set of unsafe URL Characters |
1085 |
*/ |
1086 |
static int is_unsafe(unsigned char *c) { |
1087 |
|
1088 |
int i; |
1089 |
static unsigned char unsafe[]= "<>\"#{}|\\^~[]`"; |
1090 |
|
1091 |
ASSERT(c); |
1092 |
|
1093 |
if(33>*c || *c>176) |
1094 |
return TRUE; |
1095 |
|
1096 |
if(*c=='%') { |
1097 |
if( isxdigit(*(c + 1)) && isxdigit(*(c + 2)) ) return FALSE; |
1098 |
return TRUE; |
1099 |
} |
1100 |
|
1101 |
for(i=0; unsafe[i]; i++) |
1102 |
if(*c==unsafe[i]) return TRUE; |
1103 |
|
1104 |
return FALSE; |
1105 |
|
1106 |
} |
1107 |
|