I was recently working with something which required me to build a code base and flash it into the board (hw) and then once that is done open a serial console (where the hw is connected through USB) and issue further commands on the hw. Now, that is a regular job and I wrote a python script which does all the manual works of cleaning, building etc. But flashing the binary onto the hardware is where the problem started coming. The first time I flash the board there is no issue. However, once I opened a serial console, next time when I try to flash the binary the script finds the serial console to be already occupied.

BTW the flash command also uses the serial connection to flash the binary on the hardware. So the only solution seems to be killing the serial console program manually and then try to flash it again which works. To kill the serial console all you need to do is to find out the process id of the serial console program (like minicom or screen) and then use “kill -9 command” on that process id. To find that process id I do the below bash command:

[code language=’bash’]
$ ps -eaf | grep -w ‘SCREEN’
vbhadra 7471 7470 0 13:10 ? 00:00:00 SCREEN /dev/ttyUSB0 115200
vbhadra 7473 6368 0 13:10 pts/0 00:00:00 grep –color=auto -w SCREEN
[/code]

My serial console utility is called “SCREEN”. As you can see in the above code snippet that there is an instance of the SCREEN program is currently running in the system. Notice that the SCREEN program is occupying the USB serial port /dev/ttyUSB0. To release the serial console from SCREEN program I will do the below:

[code language=”bash”]
fuser -k /dev/ttyUSB0
[/code]

To do the same in the script I need to do a bit of filtering on the above output lines. So what I am essentially doing manually is that I am checking the name of the program SCREEN which is the 8th field in the below output line:

[code language=”bash”]
vbhadra 7471 7470 0 13:10 ? 00:00:00 SCREEN /dev/ttyUSB0 115200
[/code]
filed[1] – vbhadra (user name)
filed[2] – 7471 (parent pid)

filed[8] – SCREEN (name of the program I am after)

If the name of the program is SCREEN then I am looking at the next field i.e. /dev/ttyUSB0 and using that in the below command:

[code language=”bash”]
fuser -k /dev/ttyUSB0
[/code]

To get the individual fields from the above output line we can use awk command as below:

[code language=”bash”]
ps -eaf | grep -w ‘SCREEN’ | awk ‘{print $8 ” ” $9}’
[/code]

If you issue the above command on a console (and you have instance SCREEN program running in your system), you should see something similar to this:

[code language=”bash”]
$ ps -eaf | grep -w ‘SCREEN’ | awk ‘{print $8 ” ” $9}’
SCREEN /dev/ttyUSB0
grep –color=auto
[/code]
The above output needs further parsing. But before we go into that the problem in hand is to be able to issue the above command from a python script.

To do this from python we can leverage the sub-process module of python and the input/output PIPEs of the sub-process module. Do the below steps in your python script:

[code language=”bash”]
$ python
Python 2.7.15+ (default, Oct 7 2019, 17:39:04)
[GCC 7.4.0] on linux2
Type “help”, “copyright”, “credits” or “license” for more information.
>>> import subprocess as sp
>>> out = sp.Popen([‘ps’, ‘-eaf’], stdout=sp.PIPE, stderr=sp.PIPE)
>>>
[/code]
In the above two line what we have done is a created a new object of the subprocess module, passed “ps -eaf” as our new subprocess to be launched, and we have connected the stdout (this belongs to the out subprocess which we have just created) to the sp.PIPE and the stderr (this also belongs to the out object) to the sp.PIPE.

Now the output of the command “ps -eaf” and the possible error from the “ps -eaf” command has been captured in our out object. How do we know that it has been captured? Let’s print the out object.

[code language=”bash”]
>>> print out
subprocess.Popen object at 0x7fe3e4b35090
>>>
[/code]
Not quite what we wanted. Because the out itself is just a pointer to the subprocess.Popen object. To see the stdout and the stderr do the below:

[code language=”bash”]
>>> o, r = out.communicate()
>>>
[/code]
I cannot elaborate much about the communicate() here. Simply put it returns a tuple which is stddata and the stderror back to the called. In the above code we have used a tuple (o, r) to capture the tuple returned by the communicate() method. The name (o, r) has no special significance, I could have used my name and surname for that matter as well.
Now you can print o and r which represents the stdout and the stderr (output and error) from the command “ps -eaf”:

[code language=”bash”]
>>> print o
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Nov01 ? 00:00:10 /sbin/init splash
root 2 0 0 Nov01 ? 00:00:00 [kthreadd]
root 3 2 0 Nov01 ? 00:00:00 [rcu_gp]
root 4 2 0 Nov01 ? 00:00:00 [rcu_par_gp]


>>> print r

>>>
[/code]
Now, the next challenge is to connect the output of the “ps -eaf” to the grep utility to filter only the output lines containing SCREEN. To do that we can create another subprocess object and connect the output of the “ps -eaf” command i.e. out.stdout to the input i.e. stdin of the newly created subprocess object. Have a look at the below code snippet:

[code language=”bash”]
>>> output = sp.Popen([‘grep’, ‘-i’, ‘SCREEN’], stdin=out.stdout, stdout=sp.PIPE)
Traceback (most recent call last):
File “”, line 1, in
File “/usr/lib/python2.7/subprocess.py”, line 386, in __init__
errread, errwrite), to_close = self._get_handles(stdin, stdout, stderr)
File “/usr/lib/python2.7/subprocess.py”, line 812, in _get_handles
p2cread = stdin.fileno()
ValueError: I/O operation on closed file
>>>
[/code]

In the above code you can see the line, stdin=out.stdout. This means that the stdin (input) of the current object is connected to the stdout of the out object which we created previously. Also, stdout=sp.PIPE means that the stdout (output) of the current subprocess is connected to the PIPE of the subprocess command.
You might have noticed the above adventure was not quite successful as it tripped over a bunch of errors. And why is that? It is because of the below operation we did after creating the out object:

[code language=”bash”]
out = sp.Popen([‘ps’, ‘-eaf’], stdout=sp.PIPE, stderr=sp.PIPE)
o, r = out.communicate()
[/code]

Once we use communicate() on the out object, the input/output files descriptors are closed and hence the error. The communicate was only for demonstration purposes. Now we will do the real stuff. Do the below again:

[code language=”bash”]
>>> out = sp.Popen([‘ps’, ‘-eaf’], stdout=sp.PIPE, stderr=sp.PIPE)
>>> output = sp.Popen([‘grep’, ‘-i’, ‘SCREEN’], stdin=out.stdout, stdout=sp.PIPE)
>>>
[/code]
Now, the earlier error is gone. Now you can test it again as before if you like:

[code language=”bash”]
>>> out = sp.Popen([‘ps’, ‘-eaf’], stdout=sp.PIPE, stderr=sp.PIPE)
>>> output = sp.Popen([‘grep’, ‘-w’, ‘SCREEN’], stdin=out.stdout, stdout=sp.PIPE)
>>> o, e = output.communicate()
>>> print o
vbhadra 8151 8150 0 13:56 ? 00:00:00 SCREEN /dev/ttyUSB0 115200

>>> print e
None
>>>
[/code]
As you can see, we have been able to filter only the line containing the SCREEN.
Now, ready to pipe this output to the awk command. Do the below as explained earlier:

[code labguage=”bash”]
>>> result = sp.Popen([‘awk’, ‘{print $8 ” ” $9}’], stdin=output.stdout, stdout=sp.PIPE)
Traceback (most recent call last):
File “”, line 1, in
File “/usr/lib/python2.7/subprocess.py”, line 386, in __init__
errread, errwrite), to_close = self._get_handles(stdin, stdout, stderr)
File “/usr/lib/python2.7/subprocess.py”, line 812, in _get_handles
p2cread = stdin.fileno()
ValueError: I/O operation on closed file
>>>
[/code]

Same problem again. As we have already operated communicate() the I/O error above is coming. So do the below all over again:

[code language=”bash”]
>>> out = sp.Popen([‘ps’, ‘-eaf’], stdout=sp.PIPE, stderr=sp.PIPE)
>>> output = sp.Popen([‘grep’, ‘-w’, ‘SCREEN’], stdin=out.stdout, stdout=sp.PIPE)
>>> result = sp.Popen([‘awk’, ‘{print $8 ” ” $9}’], stdin=output.stdout, stdout=sp.PIPE)
>>> r, e = result.communicate()
>>> print r
SCREEN /dev/ttyUSB0

>>>
>>>
[/code]
As you can we have almost stripped the whole output except the interesting parts for our purposes. Now we are ready to parse the final output result for just grabbing the serial terminal string only. Do the below:

[code language=”bash”]
>>> import subprocess as sp
>>> out = sp.Popen([‘ps’, ‘-eaf’], stdout=sp.PIPE, stderr=sp.PIPE)
>>> output = sp.Popen([‘grep’, ‘-w’, ‘SCREEN’], stdin=out.stdout, stdout=sp.PIPE)
>>> result = sp.Popen([‘awk’, ‘{print $8 ” ” $9}’], stdin=output.stdout, stdout=sp.PIPE)
>>> o, e = result.communicate()
>>> str = o.split()
>>> if str[0] == “SCREEN”:
… print str[1]

/dev/ttyUSB0
>>>
[/code]
Now in the above code snippet from the python shell, you can see we have sucessfully extracted the serial port which we would like to release, /dev/ttyUSB0. Now to release we will replace the print statement above as below:

[code language=”bash”]
>>> if str[0] == “SCREEN”:
… ret = sp.check_call([‘fuser’, ‘-k’, str[1]])

/dev/ttyUSB0: 8837
>>>
[/code]

All of these can be embedded in a single script as below:

[code language=”bash”]
#!/usr/bin/env python

import subprocess as sp

def grep_and_return():
o = sp.Popen([‘ps’, ‘-eaf’], stdout=sp.PIPE, stderr=sp.PIPE)
out = sp.Popen([‘grep’, ‘-w’, ‘SCREEN’], stdin=o.stdout, stdout=sp.PIPE)
output = sp.Popen([‘awk’, ‘{print $8 ” ” $9}’], stdin=out.stdout, stdout=sp.PIPE)
r, e = output.communicate()
ret = r.split()
if ret[0] == “SCREEN”:
print ret[1]
#output = sp.check_call([‘fuser’, ‘-k’, ret[1]])

def main():
ret = grep_and_return()

if __name__ == “__main__”:
main()
[/code]
The above code can be downloaded from github here.

For any question please post below!

Have fun scripting!

 

Leave a Reply