5
votes

I would like to do a packet capture using tshark, a command-line flavor of Wireshark, while connecting to a remote host device on telnet. I would like to invoke the function I wrote for capture:

def wire_cap(ip1,ip2,op_fold,file_name,duration):  # invoke tshark to capture traffic during session
    if duration == 0:
        cmd='"tshark" -i 1 -P -w '+ op_fold+file_name+'.pcap src ' + str(ip1) + ' or src '+ str(ip2)
    else:
        cmd='"tshark" -i 1 -a duration:'+str(duration)+' -P -w '+ op_fold+file_name+'.pcap src ' + str(ip1) + ' or src '+ str(ip2)

    p = subprocess.Popen(cmd, shell=True,stderr=subprocess.PIPE)
    while True:
        out = p.stderr.read(1)
        if out == '' and p.poll() != None:
            break
        if out != '':
            sys.stdout.write(out)
            sys.stdout.flush()

For debugging purpose, I would like to run this function in the background by calling it as and when required and stopping it when I've got the capture. Something like:

Start a thread or a background process called wire_capture
//Do something here
Stop the thread or the background process wire_capture

By reading a bit, I realized that thread.start_new_thread() and threading.Thread() seems to be suitable only when I know the duration of the capture (an exit condition). I tried using thread.exit() but it acted like sys.exit() and stopped the execution of the program completely. I also tried threading.Event() as follows:

if cap_flg:
    print "Starting a packet capture thread...."
    th_capture = threading.Thread(target=wire_cap, name='Thread_Packet_Capture', args=(IP1, IP2, output, 'wire_capture', 0, ))
    th_capture.setDaemon(True)
    th_capture.start()

.
.
.
.
.

if cap_flg:
    thread_kill = threading.Event()
    print "Exiting the packet capture thread...."
    thread_kill.set()
    th_capture.join()

I would like to know how can I make the process stop when I feel like stopping it (Like an exit condition that can be added so that I can exit the thread execution). The above code I tried doesn't seem to work.

1
Perhaps the problem is that setDaemon() is ignored as described in the docs: the main thread is not a daemon thread and therefore all threads created in the main thread default to daemon = False. The entire Python program exits when no alive non-daemon threads are left.LMC
@LuisMuñoz I missed that part. Probably that is why its not running in the background. But, I would like to know if there is an alternative to threading then. Any leads on that?skrowten_hermit
Not much experience with threads on python unfortunately. Perhaps launching that thread from an auxiliary one could help.LMC
How about ask popen for pid and then simply kill it?lojza

1 Answers

2
votes

The threading.Event() approach is on the right track, but you need the event to be visible in both threads, so you need to create it before you start the second thread and pass it in:

if cap_flg:
    print "Starting a packet capture thread...."
    thread_kill = threading.Event()
    th_capture = threading.Thread(target=wire_cap, name='Thread_Packet_Capture', args=(IP1, IP2, output, 'wire_capture', 0, thread_kill))
    th_capture.setDaemon(True)
    th_capture.start()

In that while loop, have the watching thread check the event on every iteration, and stop the loop (and also probably kill the tshark it started) if it is set. You also need to make sure that the process doesn't sit waiting forever for output from the process, and ignoring the termination event, by only reading from the pipe if there is data available:

def wire_cap(ip1,ip2,op_fold,file_name,duration,event):  # invoke tshark to capture traffic during session
    if duration == 0:
        cmd='"tshark" -i 1 -P -w '+ op_fold+file_name+'.pcap src ' + str(ip1) + ' or src '+ str(ip2)
    else:
        cmd='"tshark" -i 1 -a duration:'+str(duration)+' -P -w '+ op_fold+file_name+'.pcap src ' + str(ip1) + ' or src '+ str(ip2)

    p = subprocess.Popen(cmd, shell=True,stderr=subprocess.PIPE)
    while not event.is_set():
        # Make sure to not block forever waiting for 
        # the process to say things, so we can see if
        # the event gets set. Only read if data is available.
        if len(select.select([p.stderr], [], [], 0.1)) > 0:
            out = p.stderr.read(1)
            if out == '' and p.poll() != None:
                break
            if out != '':
                sys.stdout.write(out)
                sys.stdout.flush()
    p.kill()

And then to actually tell the thread to stop you just set the event:

if cap_flg:
    print "Exiting the packet capture thread...."
    thread_kill.set()
    th_capture.join()