/*
 * TCPVIEW
 *
 * Author:	Martin Hunt
 *		Networks and Distributed Computing
 *		Computing & Communications
 *		University of Washington
 *		Administration Building, AG-44
 *		Seattle, WA  98195
 *		Internet: martinh@cac.washington.edu
 *
 *
 * Copyright 1992 by the University of Washington
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appears in all copies and that both the
 * above copyright notice and this permission notice appear in supporting
 * documentation, and that the name of the University of Washington not be
 * used in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  This software is made
 * available "as is", and
 * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
 * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
 * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
 * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#ifndef lint
static char rcsid[] =
    "@(#) $Header: /usr/staff/martinh/tcpview/RCS/capture.c,v 1.2 1993/04/22 20:14:24 martinh Exp $ (UW)";
#endif

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <signal.h>

#include  <X11/IntrinsicP.h>
#include  <X11/StringDefs.h>
#include  <X11/CoreP.h>
#include  <Xm/Xm.h>
#include  <Xm/Frame.h>
#include  <Xm/MessageB.h>
#include  <Xm/RowColumn.h>
#include  <Xm/Separator.h>
#include  <Xm/ToggleB.h>
#include  <Xm/BulletinB.h>
#include  <Xm/SelectioB.h>
#include  <Xm/TextF.h>
#include  <Xm/Form.h>

#include "net/bpf.h"
#include "interface.h"
#include "tcpview.h"
#include "motif.h"
#include "filter.h"

#ifdef __STDC__
struct bpf_program *parse(char *, int, int, u_long);  /* gencode.c */
void readfile(char *);
char *malloc( int );
static void max_bytes_callback( Widget, caddr_t, caddr_t );
static void time_limit_callback( Widget, caddr_t, caddr_t );
static void num_frame_callback( Widget, caddr_t, caddr_t );
static void device_callback( Widget, caddr_t, caddr_t );
static void prom_callback( Widget, caddr_t, caddr_t );
static void done_callback( Widget, caddr_t, caddr_t );
static void cancel_callback( Widget, caddr_t, caddr_t );
static void dev_cancel( Widget, caddr_t, caddr_t );
#else
struct bpf_program *parse();  /* gencode.c */
void readfile();
char *malloc();
static void max_bytes_callback();
static void time_limit_callback();
static void num_frame_callback();
static void device_callback();
static void prom_callback();
static void done_callback();
static void cancel_callback();
static void dev_cancel();
#endif /* __STDC__ */

static   int if_fd;
static   char *strbuf;
static Widget Work, bb_widget, mb_widget, tl_widget, nf_widget, dev_widget, pr_widget;
static char *TempName;
static XtIntervalId TimeoutID = 0;
static char StderrName[64];

/* global configuration variables */
int MaxBytes;
char *Device=0;

static long TimeLimit = 0;
static long NumberOfFrames = -1;
static char *promstring[] = { "ON", "OFF" } ;
static u_short mb_semaphore;
static u_short tl_semaphore;
static u_short nf_semaphore;
static u_short dev_semaphore;

char *ltos( num )
long num;
{
  static char str[32];
  sprintf(str,"%d",num);
  return( str );
}

void cap_opt_callback( widget, name, call_data )
Widget  widget;
char    *name;
caddr_t call_data;
{
  Widget ok, main_row, frame, rc, rd;
  int n;
  Arg args[5];

  mb_semaphore = tl_semaphore = nf_semaphore = dev_semaphore = 0;

  n=0;
  XtSetArg( args[n], XmNautoUnmanage, False ); n++;
  XtSetArg( args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL ); n++; 
  bb_widget = XmCreateBulletinBoardDialog (widget,"capture_options",args,n);

  n = 0;
  XtSetArg( args[n], XmNorientation, XmVERTICAL ); n++;
  main_row = XmCreateRowColumn( bb_widget,"main",args,n);
  XtManageChild( main_row ); 

  n=0;
  XtSetArg ( args[n], XmNshadowThickness, 4 ); n++;
  frame = XmCreateFrame (main_row,"frame",args,n);
  XtManageChild(frame);

  n = 0;
  XtSetArg( args[n], XmNorientation, XmVERTICAL ); n++;
  rc = XmCreateRowColumn( frame,"row",args,n);
  XtManageChild( rc ); 

  n=0;
  CreateLabelWidget( rc, "interface_opts","  INTERFACE OPTIONS  ", args, n );
  XmCreateSeparator( rc, "sep", args, n );

  n=0;
  XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++;
  rd = XmCreateRowColumn( rc,"data",args,n);
  XtManageChild( rd ); 
  CreateLabelWidget( rd, "device", "Device Name ", args, 0 );
  if( Device == NULL ) {
    Device = lookup_device();
    if( Device == NULL )
      eprint("Can't find any interfaces");
  }
  dev_widget = CreateSimpleButton( rd, Device, device_callback, (caddr_t)0 ); 

  n=0;
  XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++;
  rd = XmCreateRowColumn( rc,"rd",args,n);
  XtManageChild( rd ); 
  CreateLabelWidget( rd, "prom_mode", "Promiscuous Mode ", args, 0 );
  pr_widget = CreateSimpleButton( rd, promstring[pflag], prom_callback, NULL ); 

  n=0;
  XtSetArg ( args[n], XmNshadowThickness, 4 ); n++;
  frame = XmCreateFrame (main_row,"frame",args,n);
  XtManageChild(frame);

  n = 0;
  XtSetArg( args[n], XmNorientation, XmVERTICAL ); n++;
  rc = XmCreateRowColumn( frame,"row2",args,n);
  XtManageChild( rc ); 

  n=0;
  CreateLabelWidget( rc, "cap_opts","  CAPTURE OPTIONS  ", args, n );
  XmCreateSeparator( rc, "sep", args, n );

  n=0;
  XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++;
  rd = XmCreateRowColumn( rc,"data",args,n);
  XtManageChild( rd ); 
  CreateLabelWidget( rd, "frame_num", "Number of Frames ", args, 0 );
  if( NumberOfFrames > 0 )
    nf_widget = CreateSimpleButton( rd, ltos(NumberOfFrames), num_frame_callback, (caddr_t)0 ); 
  else
    nf_widget = CreateSimpleButton( rd, "infinite", num_frame_callback, (caddr_t)0 ); 

  n=0;
  XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++;
  rd = XmCreateRowColumn( rc,"data",args,n);
  XtManageChild( rd ); 
  CreateLabelWidget( rd, "time", "Time Limit ", args, n );
  if( TimeLimit > 0 )
    tl_widget = CreateSimpleButton( rd, ltos(TimeLimit), time_limit_callback, (caddr_t)0 ); 
  else
    tl_widget = CreateSimpleButton( rd, "infinite", time_limit_callback, (caddr_t)0 ); 
  CreateLabelWidget( rd, "time_sec", " seconds", args, 0 );

  n=0;
  XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++;
  rd = XmCreateRowColumn( rc,"data",args,n);
  XtManageChild( rd ); 
  CreateLabelWidget( rd, "bytes", "Max Bytes Per Frame ", args, 0 );
  mb_widget = CreateSimpleButton( rd, ltos(MaxBytes), max_bytes_callback, (caddr_t)0 ); 

  /* create 'OK' pushbutton */
  n = 0;
  ok = CreatePushButton( main_row, "done", " DONE ", args, n, done_callback);
  
  /* Set OK button as default */
  BulletinDefaultButton( bb_widget, ok );

  XtManageChild( bb_widget ); 
}       /* cap_opt_callback */

void get_long( parent, def, name, title, callback, semaphore )
Widget parent;
long def;
char *name, *title;
void (*callback)();
u_short *semaphore;
{
  Widget w;
  Arg args[2];
  int n;
  XmString ms, ms2;

  ms = XmStringCreateSimple(title);
  ms2 = XmStringCreateSimple( ltos(def) );
  n = 0;
  XtSetArg (args[n], XmNselectionLabelString, ms); n++;
  if( def > 0 ) {
    XtSetArg (args[n], XmNtextString, ms2); n++;
  }
  w = XmCreatePromptDialog( bb_widget, name ,args,n);
  XtAddCallback( w, XmNokCallback, (*callback), (caddr_t)0);
  XtAddCallback( w, XmNcancelCallback, cancel_callback, semaphore);
  XtAddCallback( w, XmNunmapCallback, cancel_callback, semaphore);
  XtManageChild( w );
  XmStringFree( ms );
  XmStringFree( ms2 );
}



static void mb_result( widget, data, foo )
     Widget  widget;
     char *data;
     XmSelectionBoxCallbackStruct *foo;
{
  char *string;
  XmStringGetLtoR( foo->value, XmSTRING_DEFAULT_CHARSET, &string); 

  /* make sure MaxBytes is not too small or too large */
  MaxBytes = max(DEFAULT_SNAPLEN, atoi(string));
  MaxBytes = min(MaxBytes, MAX_SNAPLEN);

  SetLabel( mb_widget, ltos(MaxBytes) );
  mb_semaphore = 0;
}


static void tl_result( widget, data, foo )
     Widget  widget;
     char *data;
     XmSelectionBoxCallbackStruct *foo;
{
  char *string;
  XmStringGetLtoR( foo->value, XmSTRING_DEFAULT_CHARSET, &string); 
  TimeLimit = atoi(string);
  if( TimeLimit <= 0 )
    SetLabel( tl_widget, "infinite" );
  else
    SetLabel( tl_widget, ltos(TimeLimit) );
  tl_semaphore = 0;
}

static void nf_result( widget, data, foo )
     Widget  widget;
     char *data;
     XmSelectionBoxCallbackStruct *foo;
{
  char *string;
  XmStringGetLtoR( foo->value, XmSTRING_DEFAULT_CHARSET, &string); 
  NumberOfFrames = atoi(string);
  if( NumberOfFrames <= 0 ) {
    NumberOfFrames = -1;
    SetLabel( nf_widget, "infinite" );
  } else
    SetLabel( nf_widget, ltos(NumberOfFrames) );
  nf_semaphore = 0;
}


static void dev_result( widget, data, foo )
     Widget  widget;
     char *data;
     XmSelectionBoxCallbackStruct *foo;
{
  char *string;
  static char buf[16];

  XmStringGetLtoR( foo->value, XmSTRING_DEFAULT_CHARSET, &string); 
  strcpy(buf, string);
  Device = buf;
  SetLabel( dev_widget, Device );
  dev_semaphore = 0;
}


static void max_bytes_callback( widget, data, foo )
     Widget  widget;
     caddr_t data;
     caddr_t foo;
{
  if( mb_semaphore )
    return;
  mb_semaphore = 1;
  get_long( widget, MaxBytes, 
	   "max_bytes",
	   "Maximum Number of Bytes to Capture Per Frame", 
	   mb_result, &mb_semaphore );
}

static void time_limit_callback( widget, data, foo )
     Widget  widget;
     caddr_t data;
     caddr_t foo;
{
  if( tl_semaphore )
    return;
  tl_semaphore = 1;
  get_long( widget, TimeLimit, 
	   "time_limit",
	   "Time Limit for Capture in Seconds", 
	   tl_result, &tl_semaphore );
}

static void num_frame_callback( widget, data, foo )
     Widget  widget;
     caddr_t data;
     caddr_t foo;
{
  if( nf_semaphore )
    return;
  nf_semaphore = 1;
  get_long( widget, NumberOfFrames, 
	   "number_frames",
	   "Number of Frames to Capture", 
	   nf_result, &nf_semaphore );
}


static void prom_callback( widget, data, foo )
     Widget  widget;
     caddr_t data;
     caddr_t foo;
{
  if( pflag )
    pflag = 0;
  else
    pflag = 1;
  SetLabel( pr_widget, promstring[pflag]);
}

static void done_callback( widget, data, foo )
     Widget  widget;
     caddr_t data;
     caddr_t foo;
{
  XtUnmanageChild( bb_widget );
}

static void cancel_callback( widget, data, foo )
     Widget  widget;
     caddr_t data;
     caddr_t foo;
{
  *data = 0;
  XtUnmanageChild(widget);
}

static void dev_cancel( widget, data, foo )
     Widget  widget;
     caddr_t data;
     caddr_t foo;
{
  dev_semaphore = 0;
  XtUnmanageChild(widget);
}

static void device_callback( parent, data, foo )
     Widget  parent;
     caddr_t data;
     caddr_t foo;
{
  Widget w;
  Arg args[2];
  int n;
  XmString ms, ms2;

  if( dev_semaphore )
    return;
  dev_semaphore = 1;

  ms = XmStringCreateSimple("Network Device Name");
  if( Device ) 
    ms2 = XmStringCreateSimple( Device );
  else
    ms2 = XmStringCreateSimple("");
  n = 0;
  XtSetArg (args[n], XmNselectionLabelString, ms); n++;
  XtSetArg (args[n], XmNtextString, ms2); n++;
  w = XmCreatePromptDialog( parent, "dev" ,args,n);
  XtAddCallback( w, XmNokCallback, dev_result, (caddr_t)0);
  XtAddCallback( w, XmNcancelCallback, dev_cancel, (caddr_t)0);
  XtManageChild( w );
  XmStringFree( ms );
  XmStringFree( ms2 );
}


static void end_capture()
{
  if( if_fd >= 0)
    wrapup(if_fd);
  *StrPtr = 0;
  sf_write_end();
  exit(0);
}

u_short DoneWithCapture;

static void child_end()
{
  int status;
  FILE *fp;
  int num;
  char buf[512];

  if( Work) XtUnrealizeWidget( Work );
  DoneWithCapture = 1;
  signal( SIGCHLD, SIG_IGN);
  (void)wait(&status);
/*  fprintf(stderr,"child exited with status %d\n",WEXITSTATUS(status)); */

  fp = fopen(StderrName,"r");
  if( fp ) {
    num = fread( buf, 1, sizeof(buf), fp );
    if( num ) {
      buf[num]='\0';
      eprint( buf );
    }
    unlink( StderrName );
    fclose( fp );
  }
  if( WEXITSTATUS(status) == 0 )
    readfile( TempName );
  unlink( TempName );
}

/* semaphore */
static u_short Stopped=0;

static void stop_capture_callback( widget, data, call_data )
Widget  widget;
caddr_t data;
caddr_t call_data;
{
  Stopped = 1;
  if( kill( (pid_t)data, SIGINT ) < 0 )
    xperror("kill");
  if( TimeoutID ) {
    XtRemoveTimeOut( TimeoutID );
    TimeoutID = 0;
  }
  Stopped = 0;
}

static void timer_expired( data, id )
caddr_t data;
XtIntervalId *id;
{
  if( Stopped )
    return;
  TimeoutID = 0;
  (void)kill( (pid_t)data, SIGINT );
}

void capture_callback( widget, name, call_data )
Widget  widget;
char    *name;
caddr_t call_data;
{
  u_long netmask, localnet;
  struct bpf_program *bpf;
  pid_t pid;
  extern long thiszone;
  extern int snaplen, Linktype, Precision;

  DoneWithCapture = 0;
  Work = NULL;

  {
    struct timeval now;
    struct timezone tz;
    
    if (gettimeofday(&now, &tz) < 0) {
      xperror("tcpdump: gettimeofday");
      exit(1);
    }
    thiszone = tz.tz_minuteswest * -60;
    if (localtime((time_t *)&now.tv_sec)->tm_isdst)
      thiszone += 3600;
  }
  Precision = clock_sigfigs();

  /* this should come from the capture options */
  /* default and max should be based on DLC */
  snaplen = MaxBytes;

  /* pick a temporary name for stderr */
  strcpy(StderrName,tmpnam(NULL));

  /* pick a temporary name to save frames to */
  TempName = tmpnam(NULL);

  update_fstr(&Filter);

  (void)signal( SIGCHLD, child_end );

  if( (pid = fork()) < 0 ) {
    eprint("Problem forking capture process");
    return;
  }
  if( pid == 0 ) {

    /* send stderr to a temp file */
    (void)freopen(StderrName,"w",stderr); 
    
    if( Device == NULL ) {
      Device = lookup_device();
      if( Device == NULL ) {
	eprint("Can't find device");
	exit(1);
      }
    }
    if_fd = initdevice( Device, pflag, &Linktype );
    lookup_net( Device, &localnet, &netmask );
    bpf = parse( Fstr, 1, Linktype, netmask );
    
    /* set up printf buffer */
    StrPtr = strbuf = malloc(512);
    
    sf_write_init( TempName, Linktype, thiszone, snaplen, Precision );
    (void)signal( SIGTERM, end_capture );
    (void)signal( SIGINT, end_capture );
    (void)signal( SIGHUP, end_capture );
    readloop( NumberOfFrames, if_fd, bpf, sf_write );
    end_capture();
  } else {	/* parent */
    XmString ms, ms_stop;
    Arg args[2];
    int n;

    if( TimeLimit > 0 ) 
      TimeoutID = XtAddTimeOut( TimeLimit*1000, timer_expired, (caddr_t)pid );

    /* now create a dialog with "Stop" button */
    ms = XmStringCreateSimple( "Capturing");
    ms_stop = XmStringCreateSimple( "STOP" );
    n = 0;
    XtSetArg( args[n], XmNmessageString, ms ); n++;
    XtSetArg( args[n], XmNokLabelString, ms_stop ); n++;
    Work = XmCreateWorkingDialog( widget,"STOP",args,n );
    RemoveDialButton( Work, XmDIALOG_HELP_BUTTON );
    RemoveDialButton( Work, XmDIALOG_CANCEL_BUTTON );
    XtAddCallback( Work, XmNokCallback,stop_capture_callback, (caddr_t)pid );

    if( !DoneWithCapture )
      XtManageChild( Work );

    XmStringFree( ms );
    XmStringFree( ms_stop );
  }
}       /* capture_callback */




syntax highlighted by Code2HTML, v. 0.9.1