1 /* $Id: misc.c 10305 2018-12-02 14:21:56Z iulius $
2 **
3 ** Helper routines for the innfeed program.
4 **
5 ** Written by James Brister <brister@vix.com>
6 */
7
8 #include "innfeed.h"
9 #include "config.h"
10 #include "clibrary.h"
11
12 #include <assert.h>
13 #include <ctype.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <netdb.h>
17 #include <netinet/in.h>
18 #include <signal.h>
19 #include <syslog.h>
20 #include <sys/param.h>
21 #include <sys/stat.h>
22 #include <time.h>
23
24 /* FIXME: Default to a max length of 256 characters for path names if the
25 host headers doesn't give better information. Should be replaced by the
26 code from Stevens. */
27 #ifndef PATH_MAX
28 # define PATH_MAX 256
29 #endif
30
31 #include "inn/messages.h"
32 #include "inn/libinn.h"
33
34 #include "endpoint.h"
35 #include "misc.h"
36 #include "tape.h"
37
38 unsigned int openfds ;
39 int debuggingOutput ;
40 unsigned int loggingLevel ;
41 char **PointersFreedOnExit ;
42
43 char *timeToStringFormat = 0;
44 bool debuggingDump = true ;
45 extern void (*gPrintInfo) (void) ;
46 void (*gCleanUp) (void) = 0 ;
47
48
49 /* Log a message to stderr, called from warn or die. Mostly the same as the
50 standard message_log_stderr, but prepends the date to each line. */
51 void
error_log_stderr_date(int len UNUSED,const char * fmt,va_list args,int err)52 error_log_stderr_date(int len UNUSED, const char *fmt, va_list args, int err)
53 {
54 char timebuff[30];
55 time_t now;
56 struct tm *tm;
57
58 now = time(NULL);
59 tm = localtime(&now);
60 strftime(timebuff, sizeof(timebuff), "%Y-%m-%d %H:%M:%S", tm);
61 fprintf(stderr, "%s %s: ", timebuff,
62 (message_program_name ? message_program_name : "UNKNOWN"));
63 vfprintf(stderr, fmt, args);
64 if (err) fprintf(stderr, ": %s", strerror(err));
65 fprintf(stderr, "\n");
66 }
67
68 /* If desired, print out the state of innfeed, call a cleanup function, and
69 then dump core. Used as an exit handler for die. */
70 void
dump_core(void)71 dump_core(void)
72 {
73 #if SNAPSHOT_ON_DIE
74 if (debuggingDump && gPrintInfo != NULL)
75 (*gPrintInfo)();
76 #endif
77
78 if (gCleanUp != NULL)
79 (*gCleanUp)();
80
81 chdir(getTapeDirectory());
82
83 sleep(5);
84 abort();
85 }
86
87 /* An alternate version of die, used when we don't want to dump core. This
88 should somehow eventually be phased out to simplify things; it's
89 basically a copy of die() from lib/error.c that ignores the cleanup
90 handler and has innfeed's handlers hard-coded (ugh). */
91 void
logAndExit(int status,const char * format,...)92 logAndExit(int status, const char *format, ...)
93 {
94 va_list args;
95 int length;
96
97 va_start(args, format);
98 length = vsnprintf(NULL, 0, format, args);
99 va_end(args);
100 va_start(args, format);
101 error_log_stderr_date(length, format, args, 0);
102 va_end(args);
103 va_start(args, format);
104 message_log_syslog_err(length, format, args, 0);
105 va_end(args);
106 exit(status);
107 }
108
109
110
d_printf(unsigned int level,const char * fmt,...)111 void d_printf (unsigned int level, const char *fmt, ...)
112 {
113 static pid_t myPid ;
114 char timeString [30] ;
115 time_t now ;
116 struct tm *tm ;
117 va_list ap ;
118
119 if (myPid == 0)
120 myPid = getpid () ;
121
122 if (loggingLevel < level)
123 return ;
124
125 now = theTime() ;
126 tm = localtime (&now);
127 strftime (timeString, sizeof(timeString), "%b %d %H:%M:%S", tm);
128
129 va_start (ap, fmt) ;
130 fprintf (stderr, "%s %s[%ld]: ",timeString,
131 (message_program_name ? message_program_name : "UNKNOWN"),
132 (long) myPid) ;
133 vfprintf (stderr, fmt, ap) ;
134 va_end (ap) ;
135 }
136
logOrPrint(int level,FILE * fp,const char * fmt,...)137 void logOrPrint (int level, FILE *fp, const char *fmt, ...)
138 {
139 va_list ap ;
140
141 va_start (ap,fmt) ;
142 if (fp != NULL)
143 {
144 vfprintf (fp,fmt,ap) ;
145 fputc ('\n',fp) ;
146 }
147 else
148 {
149 char buffer [512] ; /* gag me */
150
151 vsnprintf (buffer,sizeof (buffer),fmt,ap) ;
152 syslog (level,"%s",buffer) ;
153 }
154 va_end (ap) ;
155 }
156
157
158
159 /* return true if the file exists and is a regular file. */
fileExistsP(const char * filename)160 bool fileExistsP (const char *filename)
161 {
162 struct stat buf ;
163
164 if (stat (filename,&buf) < 0)
165 return false ;
166
167 return (S_ISREG (buf.st_mode) ? true : false) ;
168 }
169
170
isDirectory(const char * filename)171 bool isDirectory (const char *filename)
172 {
173 struct stat buf ;
174
175 if (stat (filename,&buf) < 0)
176 return false ;
177
178 return (S_ISDIR (buf.st_mode) ? true : false) ;
179 }
180
181
182
getNntpResponse(char * p,int * code,char ** rest)183 bool getNntpResponse (char *p, int *code, char **rest)
184 {
185 bool rval = true ;
186 int cd = 0 ;
187 int digits = 0 ;
188
189 if (rest)
190 *rest = 0 ;
191 *code = 0 ;
192
193 if (p == NULL)
194 return false ;
195
196 while (*p && isspace((unsigned char) *p))
197 p++ ;
198
199 while (*p && isdigit((unsigned char) *p))
200 {
201 digits++ ;
202 cd = (cd * 10) + (*p - '0') ;
203 p++ ;
204 }
205
206 if (digits != 3)
207 return false ;
208
209 if (*p == '-')
210 p++ ;
211
212 while (*p && isspace((unsigned char) *p))
213 p++ ;
214
215 if (rest)
216 *rest = p ;
217
218 *code = cd ;
219
220 return rval ;
221 }
222
223
224
225 /* Pull out a message id from a response on to a streaming command */
getMsgId(const char * p)226 char *getMsgId (const char *p)
227 {
228 const char *q ;
229 char *rval ;
230
231 while (*p && isspace((unsigned char) *p)) p++ ;
232 while (*p && !isspace((unsigned char) *p)) p++ ; /* skip response code */
233 while (*p && isspace((unsigned char) *p)) p++ ;
234
235 if ( *p == '\0' )
236 return NULL ;
237
238 q = p ;
239 while ( *q && !isspace((unsigned char) *q) )
240 q++ ;
241
242 rval = xstrndup (p, q - p) ;
243
244 return rval ;
245 }
246
247
248
249
findNonBlankString(char * ptr,char ** tail)250 char *findNonBlankString (char *ptr, char **tail)
251 {
252 char *p, *q ;
253
254 for (p = ptr ; *p && isspace((unsigned char) *p) ; p++)
255 /* nada */ ;
256 if ( ! *p )
257 return NULL ;
258
259 for (q = p ; *q && !isspace((unsigned char) *q) ; q++)
260 /* nada */ ;
261
262 *tail = q ;
263
264 return p ;
265 }
266
267
268 /* strtok can't handle zero length tokens. */
mystrtok(char * line,const char * sep)269 char *mystrtok (char *line, const char *sep)
270 {
271 static char *newPoint ;
272 char *oldline ;
273
274 if (line == NULL && newPoint == NULL)
275 return NULL ;
276
277 if (line != NULL)
278 {
279 oldline = line ;
280 while (*line != '\0' && strchr (sep,*line) == NULL)
281 line++ ;
282
283 if (*line == '\0')
284 newPoint = NULL ;
285 else
286 {
287 newPoint = line + 1 ;
288 *line = '\0' ;
289 }
290 }
291 else
292 {
293 if (newPoint == NULL)
294 return NULL ;
295
296 oldline = newPoint ;
297 line = oldline ;
298
299 while (*line != '\0' && strchr (sep,*line) == NULL)
300 line++ ;
301
302 if (*line == '\0')
303 newPoint = NULL ;
304 else
305 {
306 newPoint = line + 1 ;
307 *line = '\0' ;
308 }
309 }
310
311 return oldline ;
312 }
313
314
315
trim_ws(char * string)316 void trim_ws (char *string)
317 {
318 char *p ;
319 unsigned int len ;
320
321 assert (string != NULL) ;
322
323 len = strlen (string) ;
324 if (len == 0)
325 return ;
326
327 for (p = string + len - 1 ; p >= string && isspace((unsigned char) *p) ; p--)
328 /* nada */ ;
329 *++p = '\0' ;
330 }
331
332
333 #if 0
334 /* Scribble on top of memory we're about to free. */
335 void deadBeef (void *base, size_t byteCount)
336 {
337 unsigned char *b = (unsigned char *) base ;
338 int i ;
339
340 #if 0
341
342 memset (base, 0, byteCount) ;
343
344 #else
345
346 assert (b != NULL) ;
347
348 for (i = 0 ; i < ((int) byteCount) - 4 ; i += 4)
349 {
350 #if 0
351 *((int *) (b + i)) = 0xdeadbeef ;
352 #else
353 b [i + 0] = (unsigned char) 0xde ;
354 b [i + 1] = (unsigned char) 0xad ;
355 b [i + 2] = (unsigned char) 0xbe ;
356 b [i + 3] = (unsigned char) 0xef ;
357 #endif
358 }
359
360 switch (byteCount % 4)
361 {
362 case 0:
363 *(b + i + 3) = (unsigned char) 0xef ;
364
365 case 3:
366 *(b + i + 2) = (unsigned char) 0xbe ;
367
368 case 2:
369 *(b + i + 1) = (unsigned char) 0xad ;
370
371 case 1:
372 *b = (unsigned char) 0xde ;
373 }
374
375 #endif
376 }
377 #endif
378
379 /* Not using plain flock or lockf 'cause I don't want to waste file
380 descriptors. This routine is based on the file shlock.c from INN. */
lockFile(const char * fileName)381 bool lockFile (const char *fileName)
382 {
383 char buff [20] ;
384 char tmpName [PATH_MAX+sizeof(long)+10], realName [PATH_MAX] ;
385 char *p ;
386 int fd, i ;
387 pid_t pid = getpid () ;
388
389 strlcpy (realName,fileName,sizeof (realName)) ;
390 if ((p = strrchr (realName, '/')) != NULL)
391 {
392 *p = '\0' ;
393 snprintf (tmpName, sizeof(tmpName), "%s/lockf%ld", realName,
394 (long) pid) ;
395 *p = '/' ;
396 }
397 else
398 snprintf (tmpName, sizeof(tmpName), "lockf%ld", (long) pid) ;
399
400 /* Create the temporary name for the lock file. */
401 while ((fd = open (tmpName, O_RDWR | O_CREAT | O_EXCL, 0644)) < 0)
402 {
403 switch (errno)
404 {
405 default:
406 unlink (tmpName) ;
407 syswarn ("ME lock file open: %s", tmpName) ;
408 return false ;
409
410 case EEXIST:
411 if (unlink (tmpName) < 0)
412 {
413 syswarn ("ME lock file unlink: %s", tmpName) ;
414 return false ;
415 }
416 break;
417 }
418 }
419
420 /* stick our pid in the temp file. */
421 snprintf (buff,sizeof(buff),"%ld\n",(long) pid) ;
422 if (write (fd,buff,(size_t) strlen (buff)) != (int) strlen (buff))
423 {
424 syswarn ("ME lock file pid-write") ;
425 close (fd) ;
426 unlink (tmpName) ;
427 return false ;
428 }
429 close (fd) ;
430
431 /* now link the real name to the temp file. */
432 while (link (tmpName,realName) < 0)
433 {
434 switch (errno)
435 {
436 default: /* opps. bailing out. */
437 syswarn ("ME lock file link: %s", realName) ;
438 unlink (tmpName) ;
439 return false ;
440
441 case EEXIST:
442 /* the real lock file exists. So pull out the pid in there and
443 see if that process is still alive. */
444 if ((fd = open (realName,O_RDONLY)) < 0)
445 {
446 syswarn ("ME lock file open: %s", realName) ;
447 unlink (tmpName) ;
448 return false ;
449 }
450
451 if ((i = read (fd,buff,sizeof (buff) - 1)) <= 0)
452 {
453 close (fd) ;
454 unlink (tmpName) ;
455 return false ;
456 }
457 close (fd) ;
458
459 buff [i] = '\0' ;
460 pid = (pid_t) atol (buff) ;
461 if (pid <= 0)
462 {
463 warn ("ME lock bad-pid info in %s: %s", realName, buff) ;
464 unlink (tmpName) ;
465 return false ;
466 }
467
468 /* now send a null signal to the process named inside to see if
469 it's still alive. */
470 if (kill (pid,0) == 0)
471 {
472 warn ("ME lock in-use already: %s by pid %ld", realName,
473 (unsigned long) pid);
474 unlink (tmpName) ;
475 return false ; /* process is still alive */
476 }
477
478 /* process that took out the lock is gone */
479 if (unlink (realName) < 0)
480 {
481 syswarn ("ME lock file unlink: %s", realName) ;
482 unlink (tmpName) ;
483 return false ;
484 }
485 }
486 }
487
488 unlink (tmpName) ;
489
490 return true ;
491 }
492
493
unlockFile(const char * lockfile)494 void unlockFile (const char *lockfile)
495 {
496 unlink (lockfile) ;
497 }
498
499
endsIn(const char * string,const char * tail)500 bool endsIn (const char *string, const char *tail)
501 {
502 size_t len = strlen (tail) ;
503 size_t slen = strlen (string) ;
504
505 if (slen < len)
506 return false ;
507 else if (strcmp (string + slen - len, tail) == 0)
508 return true ;
509 else
510 return false ;
511 }
512
513
514 /* append the contents of src to dest. src is removed if append if
515 successful */
appendFile(const char * dest,const char * src)516 bool appendFile (const char *dest, const char *src)
517 {
518 FILE *inTmp, *outTmp ;
519 char buff [BUFSIZ] ;
520 size_t rval ;
521
522 /* append the outputFilename file to the inputFilename file */
523 if ((outTmp = fopen (dest, "a")) == NULL)
524 die ("fopen (%s): %s",dest, strerror (errno)) ;
525 if ((inTmp = fopen (src, "r")) == NULL)
526 die ("fopen (%s): %s",src, strerror (errno)) ;
527
528 while ((rval = fread (buff,sizeof (char),BUFSIZ,inTmp)) > 0)
529 {
530 if (fwrite (buff,sizeof (char), rval, outTmp) != rval)
531 die ("fwrite: %s", strerror (errno)) ;
532 }
533
534 if (ferror (inTmp))
535 die ("Error on inTmp in newTape") ;
536 if (ferror (outTmp))
537 die ("Error on outTmp in newTape") ;
538
539 if (fclose (inTmp) != 0)
540 die ("fclose (inTmp): appendFile (%s,%s): %s",dest,src,strerror (errno)) ;
541
542 if (fclose (outTmp) != 0)
543 die ("fclose (outTmp): appendFile (%s,%s): %s",dest,src,strerror (errno)) ;
544
545 if (unlink (src) != 0)
546 die ("unlink (%s): %s", src, strerror (errno)) ;
547
548 return true ;
549 }
550
551
552 /* return true if file1 is older than file2 */
isOlder(const char * file1,const char * file2)553 bool isOlder (const char *file1, const char *file2)
554 {
555 struct stat buf1 ;
556 struct stat buf2 ;
557
558 if (stat (file1,&buf1) < 0)
559 return false ;
560
561 if (stat (file2,&buf2) < 0)
562 return false ;
563
564 return ((buf1.st_mtime < buf2.st_mtime) ? true : false) ;
565 }
566
567
freeCharP(char * charp)568 void freeCharP (char *charp)
569 {
570 free (charp) ;
571 }
572
573
574 /* return the length of the file reference by the given file descriptor */
fileLength(int fd)575 long fileLength (int fd)
576 {
577 struct stat buf ;
578
579 if (fstat (fd,&buf) < 0)
580 return false ;
581
582 return ((long) buf.st_size) ;
583 }
584
585
586
boolToString(bool val)587 const char *boolToString (bool val)
588 {
589 return val ? "true" : "false" ;
590 }
591
timeToString(time_t t,char * buffer,size_t size)592 char* timeToString(time_t t, char* buffer, size_t size)
593 {
594 static const char defaultFormat[] = "%a %b %d %H:%M:%S %Y" ;
595 const struct tm *const tm = localtime(&t);
596 strftime (buffer, size,
597 timeToStringFormat == 0 ? defaultFormat : timeToStringFormat, tm);
598 return buffer;
599 }
600
addPointerFreedOnExit(char * pointerToFree)601 void addPointerFreedOnExit (char *pointerToFree)
602 {
603 static int totalPointers = 0 ;
604 static int nextPointer = 0 ;
605
606 if (nextPointer == 0 || nextPointer == totalPointers - 1)
607 {
608 int i;
609
610 totalPointers += 16 ;
611 if (PointersFreedOnExit == NULL)
612 PointersFreedOnExit = xmalloc (sizeof(char *) * totalPointers) ;
613 else
614 PointersFreedOnExit =
615 xrealloc (PointersFreedOnExit, sizeof(char *) * totalPointers) ;
616
617 for (i = nextPointer; i < totalPointers; i++)
618 PointersFreedOnExit [i] = NULL;
619 }
620 PointersFreedOnExit [nextPointer++] = pointerToFree ;
621 }
622
623
624 /* borrows heavily from the shrinkfile program by chongo. */
shrinkfile(FILE * fp,long size,char * name,const char * mode)625 bool shrinkfile (FILE *fp, long size, char *name, const char *mode)
626 {
627 long currlen = ftello (fp) ;
628 char *tmpname ;
629 char buffer [BUFSIZ] ;
630 FILE *tmpFp ;
631 int c ;
632 int i ;
633 int fd ;
634
635 if (currlen <= size)
636 {
637 d_printf (1,"No need to shrink file (%s %ld vs %ld\n",
638 name,size,currlen) ;
639 return true ;
640 }
641
642 /* create a temp file. */
643 tmpname = concat (name,".XXXXXX",(char *)0) ;
644 fd = mkstemp (tmpname) ;
645
646 if (fd < 0)
647 {
648 syswarn ("ME error creating temp shrink file for %s", name) ;
649 free (tmpname) ;
650 return false ;
651 }
652
653 if ((tmpFp = fdopen (fd,"w")) == NULL)
654 {
655 syswarn ("ME error opening temp shrink file %s", tmpname) ;
656 free (tmpname) ;
657 return false ;
658 }
659
660 if (fseeko (fp,currlen - size,SEEK_SET) != 0)
661 {
662 fclose (tmpFp) ;
663 warn ("ME error seeking to point %ld in %s", currlen - size, name) ;
664 free (tmpname) ;
665 return false ;
666 }
667
668 /* find the end of the next line in the shrinking file. */
669 while ((c = fgetc (fp)) != '\n')
670 if (c == EOF)
671 {
672 warn ("ME no newline in shrinking file %s", name) ;
673 fclose (tmpFp) ;
674 fseeko (fp,currlen,SEEK_SET) ;
675 free (tmpname) ;
676 return false ;
677 }
678
679 /* copy the tail of the shrinking file to the temp file. */
680 while ((i = fread (buffer,1,sizeof (buffer),fp)) > 0)
681 {
682 if (fwrite (buffer,1,i,tmpFp) != (size_t) i)
683 {
684 fclose (tmpFp) ;
685 syswarn ("ME fwrite failed to temp shrink file %s", tmpname) ;
686 fseeko (fp,currlen, SEEK_SET) ;
687 free (tmpname) ;
688 return false ;
689 }
690 }
691
692 if (i < 0)
693 logAndExit (1,"ME fread failed on file %s: %s",name, strerror (errno)) ;
694
695 fclose (tmpFp) ;
696
697 if (unlink (name) != 0)
698 logAndExit (1,"ME oserr unlink %s: %s",name, strerror (errno)) ;
699
700 /* we're in the same directory so this is ok. */
701 if (rename (tmpname,name) != 0)
702 logAndExit (1,"ME oserr rename %s, %s: %s", tmpname, name,
703 strerror (errno)) ;
704
705 if (freopen (name,mode,fp) != fp)
706 logAndExit (1,"ME freopen on shrink file failed %s: %s", name,
707 strerror (errno)) ;
708
709 fseeko (fp,0,SEEK_END) ;
710 size = ftello (fp) ;
711
712 notice ("ME file %s shrunk from %ld to %ld", name, currlen, size) ;
713
714 free (tmpname) ;
715
716 return true ;
717 }
718
719
720
pathMax(const char * pathname UNUSED)721 long pathMax (const char *pathname UNUSED)
722 {
723 static long rval = 0 ;
724
725 if (rval > 0)
726 return rval ;
727
728 #if defined (PATH_MAX)
729
730 rval = PATH_MAX ;
731
732 #elif defined (_POSIX_PATH_MAX)
733
734 rval = _POSIX_PATH_MAX ;
735
736 #elif defined (DO_HAVE_PATHCONF) && defined (_PC_PATH_MAX)
737
738 if (pathname == NULL)
739 pathname = "/tmp" ;
740
741 rval = pathconf (pathname,_PC_PATH_MAX) ;
742
743 #else
744
745 rval = 255 ;
746 if (!logged)
747 {
748 syslog (LOG_ERR,NO_PATH_MAX,rval) ;
749 logged = true ;
750 }
751
752 #endif
753
754 return rval ;
755 }
756