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