1 /*
2  * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
3  *               2002, 2003, 2004
4  *	Ohio University.
5  *
6  * ---
7  *
8  * Starting with the release of tcptrace version 6 in 2001, tcptrace
9  * is licensed under the GNU General Public License (GPL).  We believe
10  * that, among the available licenses, the GPL will do the best job of
11  * allowing tcptrace to continue to be a valuable, freely-available
12  * and well-maintained tool for the networking community.
13  *
14  * Previous versions of tcptrace were released under a license that
15  * was much less restrictive with respect to how tcptrace could be
16  * used in commercial products.  Because of this, I am willing to
17  * consider alternate license arrangements as allowed in Section 10 of
18  * the GNU GPL.  Before I would consider licensing tcptrace under an
19  * alternate agreement with a particular individual or company,
20  * however, I would have to be convinced that such an alternative
21  * would be to the greater benefit of the networking community.
22  *
23  * ---
24  *
25  * This file is part of Tcptrace.
26  *
27  * Tcptrace was originally written and continues to be maintained by
28  * Shawn Ostermann with the help of a group of devoted students and
29  * users (see the file 'THANKS').  The work on tcptrace has been made
30  * possible over the years through the generous support of NASA GRC,
31  * the National Science Foundation, and Sun Microsystems.
32  *
33  * Tcptrace is free software; you can redistribute it and/or modify it
34  * under the terms of the GNU General Public License as published by
35  * the Free Software Foundation; either version 2 of the License, or
36  * (at your option) any later version.
37  *
38  * Tcptrace is distributed in the hope that it will be useful, but
39  * WITHOUT ANY WARRANTY; without even the implied warranty of
40  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
41  * General Public License for more details.
42  *
43  * You should have received a copy of the GNU General Public License
44  * along with Tcptrace (in the file 'COPYING'); if not, write to the
45  * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
46  * MA 02111-1307 USA
47  *
48  * Author:	Shawn Ostermann
49  * 		School of Electrical Engineering and Computer Science
50  * 		Ohio University
51  * 		Athens, OH
52  *		ostermann@cs.ohiou.edu
53  *		http://www.tcptrace.org/
54  */
55 #include "tcptrace.h"
56 static char const GCC_UNUSED copyright[] =
57     "@(#)Copyright (c) 2004 -- Ohio University.\n";
58 static char const GCC_UNUSED rcsid[] =
59     "@(#)$Header$";
60 
61 
62 /*
63  * tcptrace.c - turn protocol monitor traces into xplot
64  *
65  * this set of functions allows a user to open "many files"
66  * dispite the open file max limit.   (Uses LRU)
67  *
68  * Author:	Shawn Ostermann
69  * Date:	Tue Nov  1, 1994
70  */
71 
72 #include <errno.h>
73 
74 
75 struct mfile {
76     FILE *stream;
77     char *fname;
78     MFILE *next;
79     MFILE *prev;
80     long fptr;
81 };
82 
83 
84 /* local static routines */
85 static void Mcheck(MFILE *pmf);
86 static void Mfopen_internal(MFILE *pmf, char *mode);
87 static void Mf_totail(MFILE *pmf, MFILE *ptail);
88 static void Mf_unlink(MFILE *pmf);
89 static void M_closeold(void);
90 static void M_mkdirp(char *directory);
91 
92 
93 /* head and tail of LRU open file list */
94 MFILE mf_head;  /* LEAST recently used */
95 MFILE mf_tail;  /* MOST recently used */
96 
97 MFILE mfc_head;  /* closed files, LEAST recently closed */
98 MFILE mfc_tail;  /* closed files, MOST recently closed */
99 
100 
101 void
Minit(void)102 Minit(void)
103 {
104     mf_head.next = &mf_tail;
105     mf_tail.prev = &mf_head;
106     mf_head.fname = "HEAD";
107     mf_tail.fname = "TAIL";
108 
109     mfc_head.next = &mfc_tail;
110     mfc_tail.prev = &mfc_head;
111     mfc_head.fname = "CLOSED HEAD";
112     mfc_tail.fname = "CLOSED TAIL";
113 }
114 
115 
116 
117 
118 MFILE *
Mfopen(char * fname,char * mode)119 Mfopen(
120     char *fname,
121     char *mode)
122 {
123     MFILE *pmf;
124     char *directory;
125     char *prefix;
126 	int len;
127 
128     if ((strcmp(mode,"w") != 0) && (strcmp(mode,"a") != 0)){
129 	fprintf(stderr,"Sorry, Mfopen works only for mode \"w\" or \"a\"\n");
130 	exit(-1);
131     }
132 
133     pmf = (MFILE *) MallocZ(sizeof(MFILE));
134 
135     /* use the directory specified by the user, if requested */
136     if (output_file_dir == NULL)
137 	directory = "";
138     else {
139 	directory = ExpandFormat(output_file_dir);
140 	M_mkdirp(directory);
141     }
142 
143     /* attach a filename prefix, if the user asked for one */
144     if (output_file_prefix == NULL)
145 	prefix = "";
146     else
147 	prefix = ExpandFormat(output_file_prefix);
148 
149 
150 	len=strlen(fname)+strlen(directory)+strlen(prefix)+2;
151 			/* 2: for the slash and null */
152 
153     pmf->fname = MallocZ(len);
154 
155     snprintf(pmf->fname,len,"%s%s%s%s",
156 	    directory,
157 	    (*directory)?"/":"",
158 	    prefix,
159 	    fname);
160 
161     // The 'b' in the file mode has no meaning in UNIX systems.
162     // It has meaning in Operating Systems like Windows that
163     // seem to treat text and binary files differently.
164     if (strcmp(mode,"w") == 0)
165 	Mfopen_internal(pmf,"wb+");
166     else if (strcmp(mode,"a") == 0)
167 	Mfopen_internal(pmf,"ab+");
168     else {
169 	fprintf(stderr,"Mfopen: internal file mode inconsistancy\n");
170 	exit(10);
171     }
172 
173     /* put at the tail of the LRU list */
174     Mf_totail(pmf,&mf_tail);
175 
176     return(pmf);
177 }
178 
179 
180 /* not really an mfiles thing, but works even when we're out of fd's */
181 int
Mfpipe(int pipes[2])182 Mfpipe(
183     int pipes[2])
184 {
185     int i;
186 
187     for (i=0; i <= 2; ++i) {
188 	if (pipe(pipes) == 0)
189 	    return(0);
190 
191 	if (errno != EMFILE) {
192 	    perror("pipe");
193 	    exit(-1);
194 	}
195 
196 	M_closeold();
197     }
198 
199     fprintf(stderr,"mfpipe - internal error, couldn't get pipes?\n");
200     exit(-1);
201 }
202 
203 
204 int
Mfileno(MFILE * pmf)205 Mfileno(
206     MFILE *pmf)
207 {
208     /* Warning, I'll GIVE you the fd, but I won't guarantee that it'll stay */
209     /* where you want it if you call my functions back!!! */
210 
211     Mcheck(pmf);
212     return(fileno(pmf->stream));
213 }
214 
215 
216 
217 
218 int
Mvfprintf(MFILE * pmf,char * format,va_list ap)219 Mvfprintf(
220     MFILE *pmf,
221     char *format,
222     va_list ap)
223 {
224     int ret;
225 
226     Mcheck(pmf);
227     ret = vfprintf(pmf->stream,format,ap);
228 
229     return(ret);
230 }
231 
232 
233 
234 int
Mfprintf(MFILE * pmf,char * format,...)235 Mfprintf(
236     MFILE *pmf,
237     char *format,
238      ...)
239 {
240     va_list ap;
241     int ret;
242 
243     va_start(ap,format);
244 
245     Mcheck(pmf);
246     ret = vfprintf(pmf->stream,format,ap);
247 
248     va_end(ap);
249 
250     return(ret);
251 }
252 
253 
254 long
Mftell(MFILE * pmf)255 Mftell(
256     MFILE *pmf)
257 {
258     Mcheck(pmf);
259     return(ftell(pmf->stream));
260 }
261 
262 
263 int
Mfseek(MFILE * pmf,long offset,int ptrname)264 Mfseek(
265     MFILE *pmf,
266     long offset,
267     int ptrname)
268 {
269     Mcheck(pmf);
270     return(fseek(pmf->stream, offset, ptrname));
271 }
272 
273 
274 int
Mfwrite(void * buf,u_long size,u_long nitems,MFILE * pmf)275 Mfwrite(
276     void *buf,
277     u_long size,
278     u_long nitems,
279     MFILE *pmf)
280 {
281     Mcheck(pmf);
282     return(fwrite(buf,size,nitems,pmf->stream));
283 }
284 
285 
286 int
Mfclose(MFILE * pmf)287 Mfclose(
288     MFILE *pmf)
289 {
290     int ret;
291 
292     if (debug>1)
293 	fprintf(stderr,"Mfclose: called for file '%s'\n", pmf->fname);
294 
295     Mcheck(pmf);
296     ret=fclose(pmf->stream);
297     pmf->stream = NULL;
298     return(ret);
299 }
300 
301 
302 int
Mfflush(MFILE * pmf)303 Mfflush(
304     MFILE *pmf)
305 {
306     Mcheck(pmf);
307     return(fflush(pmf->stream));
308 }
309 
310 
311 
312 static void
Mfopen_internal(MFILE * pmf,char * mode)313 Mfopen_internal(
314     MFILE *pmf,
315     char *mode)
316 {
317     FILE *stream;
318 
319     stream = fopen(pmf->fname,mode);
320 
321     if (stream == NULL) {
322 
323 	if (errno != EMFILE) {
324 	    perror(pmf->fname);
325 	    exit(-1);
326 	}
327 
328 	M_closeold();
329 
330 	/* now, try again */
331 	stream = fopen(pmf->fname,mode);
332 	if (stream == NULL) {
333 	    perror("fopen (second try)");
334 	    exit(-1);
335 	}
336     }
337 
338     pmf->stream = stream;
339 
340     /* seek back to where we were last time, if this was previously opened */
341     if (pmf->fptr != 0) {
342 	if (fseek(stream, pmf->fptr, SEEK_SET) != 0) {
343 	    perror("fseek");
344 	    exit(-1);
345 	}
346     }
347 
348     return;
349 }
350 
351 static void
M_closeold(void)352 M_closeold(void)
353 {
354     MFILE *closehim;
355 
356     /* OK, close a file we haven't used for a while */
357     closehim = mf_head.next;
358     closehim->fptr = ftell(closehim->stream);  /* remember current position */
359     fclose(closehim->stream);
360     closehim->stream = NULL;
361 
362     /* put closed file at the tail of the closed LRU list */
363     Mf_unlink(closehim);
364     Mf_totail(closehim,&mfc_tail);
365 
366     if (debug > 1)
367 	fprintf(stderr,"Mfiles: too many files open, closed file '%s'\n",
368 		closehim->fname);
369 }
370 
371 
372 
373 static void
Mcheck(MFILE * pmf)374 Mcheck(
375     MFILE *pmf)
376 {
377     /* make sure that it's open */
378     if (pmf->stream == NULL) {
379 	if (debug > 1)
380 	    fprintf(stderr,"Mcheck: re-opening file '%s'\n", pmf->fname);
381 	Mfopen_internal(pmf,"r+");
382     }
383 
384     /* put at the tail of the LRU list */
385     if (mf_tail.prev != pmf) {
386 	Mf_unlink(pmf);
387 	Mf_totail(pmf,&mf_tail);
388     }
389 
390 }
391 
392 #ifdef OLD
393 static void
M_printlru(void)394 M_printlru(void)
395 {
396     MFILE *pmf;
397 
398     for (pmf = &mf_head; pmf; pmf=pmf->next)
399 	fprintf(stderr,"%s ==> ", pmf->fname);
400     fprintf(stderr,"NULL \n");
401 
402     for (pmf = &mfc_head; pmf; pmf=pmf->next)
403 	fprintf(stderr,"%s ==> ", pmf->fname);
404     fprintf(stderr,"NULL \n");
405 }
406 #endif /* OLD */
407 
408 
409 static void
Mf_unlink(MFILE * pmf)410 Mf_unlink(
411     MFILE *pmf)
412 {
413     pmf->prev->next = pmf->next;
414     pmf->next->prev = pmf->prev;
415 }
416 
417 
418 static void
Mf_totail(MFILE * pmf,MFILE * ptail)419 Mf_totail(
420     MFILE *pmf,
421     MFILE *ptail)
422 {
423     pmf->next = ptail;
424     pmf->prev = ptail->prev;
425     ptail->prev->next = pmf;
426     ptail->prev = pmf;
427 }
428 
429 
430 /* try to create all of the directories in the argument */
431 /* like mkdirp() under Solaris, but that apparently isn't standard */
432 static void
M_mkdirp(char * directory)433 M_mkdirp(char *directory)
434 {
435     static dstring_t *pds = NULL;
436     char *pch;
437     char *temp;
438 
439     if (access(directory,W_OK) == 0) {
440 	/* it already exists */
441 	return;
442     }
443 
444     /* make a dynamic string to store the path components */
445     if (pds == NULL)
446 	pds = DSNew();
447 
448     if (debug)
449 	fprintf(stderr,"Trying to create directory '%s'\n", directory);
450 
451 
452     /* walk the directory and try to create the components */
453     pch = directory;
454     while (pch) {
455 	pch = strchr(pch,'/');
456 	/* N.B. pch will be null on the last go around */
457 
458 	/* copy that much of the directory */
459 	DSErase(pds);
460 	if (pch)
461 	    DSAppendStringN(pds,directory,(1 + pch - directory));
462 	else
463 	    DSAppendString(pds,directory); /* last loop */
464 	temp = DSVal(pds);
465 
466 	/* try to create it */
467 	if (mkdir(temp,0755) == -1) {
468 	    /* this will fail with EEXIST if the directory exists,
469 	       which is fine */
470 	    if (errno != EEXIST) {
471 		/* couldn't make the directory */
472 		perror(temp);
473 		fprintf(stderr,
474 			"Unable to create directory '%s' (as part of '%s')\n",
475 			temp, directory);
476 		exit(-1);
477 	    }
478 	} else {
479 	    if (debug)
480 		fprintf(stderr,"Created directory '%s'\n", temp);
481 	}
482 
483 	/* if pch is NULL, then we're done and will fall out,
484 	   else we need to increment pch past the current '/' */
485 	if (pch)
486 	    ++pch;
487     }
488 }
489