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