/* * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002, 2003, 2004 * Ohio University. * * --- * * Starting with the release of tcptrace version 6 in 2001, tcptrace * is licensed under the GNU General Public License (GPL). We believe * that, among the available licenses, the GPL will do the best job of * allowing tcptrace to continue to be a valuable, freely-available * and well-maintained tool for the networking community. * * Previous versions of tcptrace were released under a license that * was much less restrictive with respect to how tcptrace could be * used in commercial products. Because of this, I am willing to * consider alternate license arrangements as allowed in Section 10 of * the GNU GPL. Before I would consider licensing tcptrace under an * alternate agreement with a particular individual or company, * however, I would have to be convinced that such an alternative * would be to the greater benefit of the networking community. * * --- * * This file is part of Tcptrace. * * Tcptrace was originally written and continues to be maintained by * Shawn Ostermann with the help of a group of devoted students and * users (see the file 'THANKS'). The work on tcptrace has been made * possible over the years through the generous support of NASA GRC, * the National Science Foundation, and Sun Microsystems. * * Tcptrace is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Tcptrace is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Tcptrace (in the file 'COPYING'); if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * Author: Shawn Ostermann * School of Electrical Engineering and Computer Science * Ohio University * Athens, OH * ostermann@cs.ohiou.edu * http://www.tcptrace.org/ */ #include "tcptrace.h" static char const GCC_UNUSED copyright[] = "@(#)Copyright (c) 2004 -- Ohio University.\n"; static char const GCC_UNUSED rcsid[] = "@(#)$Header: /usr/local/cvs/tcptrace/mfiles.c,v 5.10 2004/09/28 18:59:35 mramadas Exp $"; /* * tcptrace.c - turn protocol monitor traces into xplot * * this set of functions allows a user to open "many files" * dispite the open file max limit. (Uses LRU) * * Author: Shawn Ostermann * Date: Tue Nov 1, 1994 */ #include struct mfile { FILE *stream; char *fname; MFILE *next; MFILE *prev; long fptr; }; /* local static routines */ static void Mcheck(MFILE *pmf); static void Mfopen_internal(MFILE *pmf, char *mode); static void Mf_totail(MFILE *pmf, MFILE *ptail); static void Mf_unlink(MFILE *pmf); static void M_closeold(void); static void M_mkdirp(char *directory); /* head and tail of LRU open file list */ MFILE mf_head; /* LEAST recently used */ MFILE mf_tail; /* MOST recently used */ MFILE mfc_head; /* closed files, LEAST recently closed */ MFILE mfc_tail; /* closed files, MOST recently closed */ void Minit(void) { mf_head.next = &mf_tail; mf_tail.prev = &mf_head; mf_head.fname = "HEAD"; mf_tail.fname = "TAIL"; mfc_head.next = &mfc_tail; mfc_tail.prev = &mfc_head; mfc_head.fname = "CLOSED HEAD"; mfc_tail.fname = "CLOSED TAIL"; } MFILE * Mfopen( char *fname, char *mode) { MFILE *pmf; char *directory; char *prefix; int len; if ((strcmp(mode,"w") != 0) && (strcmp(mode,"a") != 0)){ fprintf(stderr,"Sorry, Mfopen works only for mode \"w\" or \"a\"\n"); exit(-1); } pmf = (MFILE *) MallocZ(sizeof(MFILE)); /* use the directory specified by the user, if requested */ if (output_file_dir == NULL) directory = ""; else { directory = ExpandFormat(output_file_dir); M_mkdirp(directory); } /* attach a filename prefix, if the user asked for one */ if (output_file_prefix == NULL) prefix = ""; else prefix = ExpandFormat(output_file_prefix); len=strlen(fname)+strlen(directory)+strlen(prefix)+2; /* 2: for the slash and null */ pmf->fname = MallocZ(len); snprintf(pmf->fname,len,"%s%s%s%s", directory, (*directory)?"/":"", prefix, fname); // The 'b' in the file mode has no meaning in UNIX systems. // It has meaning in Operating Systems like Windows that // seem to treat text and binary files differently. if (strcmp(mode,"w") == 0) Mfopen_internal(pmf,"wb+"); else if (strcmp(mode,"a") == 0) Mfopen_internal(pmf,"ab+"); else { fprintf(stderr,"Mfopen: internal file mode inconsistancy\n"); exit(10); } /* put at the tail of the LRU list */ Mf_totail(pmf,&mf_tail); return(pmf); } /* not really an mfiles thing, but works even when we're out of fd's */ int Mfpipe( int pipes[2]) { int i; for (i=0; i <= 2; ++i) { if (pipe(pipes) == 0) return(0); if (errno != EMFILE) { perror("pipe"); exit(-1); } M_closeold(); } fprintf(stderr,"mfpipe - internal error, couldn't get pipes?\n"); exit(-1); } int Mfileno( MFILE *pmf) { /* Warning, I'll GIVE you the fd, but I won't guarantee that it'll stay */ /* where you want it if you call my functions back!!! */ Mcheck(pmf); return(fileno(pmf->stream)); } int Mvfprintf( MFILE *pmf, char *format, va_list ap) { int ret; Mcheck(pmf); ret = vfprintf(pmf->stream,format,ap); return(ret); } int Mfprintf( MFILE *pmf, char *format, ...) { va_list ap; int ret; va_start(ap,format); Mcheck(pmf); ret = vfprintf(pmf->stream,format,ap); va_end(ap); return(ret); } long Mftell( MFILE *pmf) { Mcheck(pmf); return(ftell(pmf->stream)); } int Mfseek( MFILE *pmf, long offset, int ptrname) { Mcheck(pmf); return(fseek(pmf->stream, offset, ptrname)); } int Mfwrite( void *buf, u_long size, u_long nitems, MFILE *pmf) { Mcheck(pmf); return(fwrite(buf,size,nitems,pmf->stream)); } int Mfclose( MFILE *pmf) { int ret; if (debug>1) fprintf(stderr,"Mfclose: called for file '%s'\n", pmf->fname); Mcheck(pmf); ret=fclose(pmf->stream); pmf->stream = NULL; return(ret); } int Mfflush( MFILE *pmf) { Mcheck(pmf); return(fflush(pmf->stream)); } static void Mfopen_internal( MFILE *pmf, char *mode) { FILE *stream; stream = fopen(pmf->fname,mode); if (stream == NULL) { if (errno != EMFILE) { perror(pmf->fname); exit(-1); } M_closeold(); /* now, try again */ stream = fopen(pmf->fname,mode); if (stream == NULL) { perror("fopen (second try)"); exit(-1); } } pmf->stream = stream; /* seek back to where we were last time, if this was previously opened */ if (pmf->fptr != 0) { if (fseek(stream, pmf->fptr, SEEK_SET) != 0) { perror("fseek"); exit(-1); } } return; } static void M_closeold(void) { MFILE *closehim; /* OK, close a file we haven't used for a while */ closehim = mf_head.next; closehim->fptr = ftell(closehim->stream); /* remember current position */ fclose(closehim->stream); closehim->stream = NULL; /* put closed file at the tail of the closed LRU list */ Mf_unlink(closehim); Mf_totail(closehim,&mfc_tail); if (debug > 1) fprintf(stderr,"Mfiles: too many files open, closed file '%s'\n", closehim->fname); } static void Mcheck( MFILE *pmf) { /* make sure that it's open */ if (pmf->stream == NULL) { if (debug > 1) fprintf(stderr,"Mcheck: re-opening file '%s'\n", pmf->fname); Mfopen_internal(pmf,"r+"); } /* put at the tail of the LRU list */ if (mf_tail.prev != pmf) { Mf_unlink(pmf); Mf_totail(pmf,&mf_tail); } } #ifdef OLD static void M_printlru(void) { MFILE *pmf; for (pmf = &mf_head; pmf; pmf=pmf->next) fprintf(stderr,"%s ==> ", pmf->fname); fprintf(stderr,"NULL \n"); for (pmf = &mfc_head; pmf; pmf=pmf->next) fprintf(stderr,"%s ==> ", pmf->fname); fprintf(stderr,"NULL \n"); } #endif /* OLD */ static void Mf_unlink( MFILE *pmf) { pmf->prev->next = pmf->next; pmf->next->prev = pmf->prev; } static void Mf_totail( MFILE *pmf, MFILE *ptail) { pmf->next = ptail; pmf->prev = ptail->prev; ptail->prev->next = pmf; ptail->prev = pmf; } /* try to create all of the directories in the argument */ /* like mkdirp() under Solaris, but that apparently isn't standard */ static void M_mkdirp(char *directory) { static dstring_t *pds = NULL; char *pch; char *temp; if (access(directory,W_OK) == 0) { /* it already exists */ return; } /* make a dynamic string to store the path components */ if (pds == NULL) pds = DSNew(); if (debug) fprintf(stderr,"Trying to create directory '%s'\n", directory); /* walk the directory and try to create the components */ pch = directory; while (pch) { pch = strchr(pch,'/'); /* N.B. pch will be null on the last go around */ /* copy that much of the directory */ DSErase(pds); if (pch) DSAppendStringN(pds,directory,(1 + pch - directory)); else DSAppendString(pds,directory); /* last loop */ temp = DSVal(pds); /* try to create it */ if (mkdir(temp,0755) == -1) { /* this will fail with EEXIST if the directory exists, which is fine */ if (errno != EEXIST) { /* couldn't make the directory */ perror(temp); fprintf(stderr, "Unable to create directory '%s' (as part of '%s')\n", temp, directory); exit(-1); } } else { if (debug) fprintf(stderr,"Created directory '%s'\n", temp); } /* if pch is NULL, then we're done and will fall out, else we need to increment pch past the current '/' */ if (pch) ++pch; } }