"killall wpa_supplicant" affects "ip route add ..." in a strange way - linux

Not sure if the title explains my situation correctly, but in details it looks like this:
I'm writing a simple bash script to set up a wireless network, using wlp2s0 interface.
ip route flush dev wlp2s0
ip addr flush dev wlp2s0
ip link set wlp2s0 down
killall wpa_supplicant
ip link set wlp2s0 up
ip addr add 192.168.1.200/24 dev wlp2s0
ip route add default via 192.168.1.1
wpa_supplicant -B -D wext -i wlp2s0 -c wireless.conf
It kills all previously started wpa_supplicants and then starts a new one.
Now, the problem is that the killall call causes ip route add to cry:
RTNETLINK answers: Network is unreachable
no matter if wpa_supplicant was actually started before.
It can be "fixed" by adding a sleep 1 call after killall, but of course I'd like to avoid this. It can also be fixed by removing the killall command and calling it manually before the script.
So the question is - how can I work around this strange behaviour of killall? Maybe someone has any idea why are these strange things happening.
EDIT: ip route add does not give that error if killall isn't called before it.

Why did you think it was strange? Successful return of killall doesn't necessarily mean wpa_supplicant has finished processing the incoming SIGTERM signal. It's only that the signal was delivered to the wpa_supplicant process, at best. Pehaps wpa_supplicant needed some more time (such as sleep 1) to finish execution of its clean-up handler (wpa_supplicant_terminate_proc() in wpa_supplicant.c)
http://hostap.epitest.fi/cgit/hostap/tree/src/utils/eloop.c#n753
http://hostap.epitest.fi/cgit/hostap/tree/wpa_supplicant/wpa_supplicant.c#n4033
So, I think you really need sleep 1.
Update
I always rely on polling method like this
TIMEO=5
for ((i=0; i<TIMEO; ++i)); do
if pidof -s wpa_supplicant > /dev/null; then
sleep 1
else
break
fi
done
if ((TIMEO==i)); then
echo "timeout"
else
echo "it's gone"
fi
assuming there will not be multiple instances of wpa_supplicant.

Related

Bash poweroff script hangs system

My intention is to cycle through my list of ips and poweroff if my ping succeeds first. However the systems seems to hang. After running this script I can't ping the systems anymore and they aren't powered off. If I run ssh 192.168.1.ip "sudo poweroff" through terminal I dont encounter this issue. Any advice?
for ((ip=40, cnt=0; ip<=max; ip++, cnt++))
do
if ping -c 1 192.168.1.$ip &> /dev/null
then
printf "\n${array[$cnt]}: Ping Successful"
ssh 192.168.1.$ip "sudo poweroff"
printf "\n${array[$cnt]}: Power Down Executed\n"
sleep 1
else
printf "\n${array[$cnt]}: Ping Failed\n"
fi
done
After running a single ssh 192.168.1.40 "sudo poweroff" the system is properly powered off. When running my script, I am unable to ping the systems however I can visually see the fans and leds are still on. I think I should use a KVM to take a closer look since ssh doesn't allow allow connection after this script is run. Still at first glance I dont understand how running ssh 192.168.1.40 "sudo poweroff" and running it through my script really makes a difference. Anyways I'll try to add more information tomorrow.
ssh 192.168.1.$ip "(sleep 5; sudo poweroff)&" to put the process in the background on the remote host and sleep for 5 seconds before powering off to give time for the script to complete and exit the remote host before it goes down... – David C. Rankin
This Resolved my Issue.

How to set up a udev rule for ETH "link down"/"link up"?

I like to switch on a green LED(connected through GPIOs), when eth0 is connected. When disconnected I like to switch the green LED of and a red one on.
Thought that udev is maybe the right place for it. I created the simple demo rule:
KERNEL=="eth0", SUBSYSTEM=="net", ACTION=="add", RUN+="/sbin/set_BSPleds eth0 on"
This rule should call a script when the eth0 is added. It was never executed.
After I was looking to the udev monitor by entering "udevadm monitor -k -u" at the shell. There were no events coming when I unplug/plug the lan cable.
root#sama5d3xek:/etc/udev/rules.d# udevadm monitor -k -uh0
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent
root#sama5d3xek:/etc/udev/rules.d#
Seems that there are no uevents for eth0. The ethernet driver is provided my ATMEL. I am building a custom Linux by the help of Yocto.
My question is, how to get the "link down"/"link up" events to udev?
If it does not works with udev, what alternative way is the best?
As others have already mentioned, it seems one can't use udev for this.
Ubuntu: wait for network link up and execute a bash command suggests
inotifywait -e modify /sys/class/net/eth0/carrier; echo 'Change detected'
(Currently, / is on nfs for my box, so I can't really say if it will work.)
In other posts, there are some concerns about using the inotify API on /sys: inotify_add_watch fails on /sys/class/net/eth0/operstate .
I think the Right Way(TM) do do this would be to use the netlink(7) API, preferably through a daemon such as netplugd.
Hope that helps :)
Ethernet devices are devices, but connections are not.
You could trace connection through /dev/xconsole, dmesg or /var/log/kern.log.
Sample, using rsyslog:
You could (as root):
echo ':msg, contains, "Link is" |/run/netlink' >/etc/rsyslog.d/netlinkstate.conf
mkfifo /run/netlink
chgrp user /run/netlink
chmod 640 /run/netlink
service rsyslog force-reload
Then, logged as user, simply:
read line < /run/netlink
will wait for input from fifo, then hold until something happen.
state=${line#*Link is } eventtime=${line%% $HOSTNAME*}
echo $eventtime $state
2016-11-21T17:40:50.956181+01:00 Down
or
echo $eventtime $state
2016-11-21T17:40:50.956181+01:00 Up 100 Mbps Full Duplex, Flow Control: Rx/Tx
echo $eventtime ${state%% *}
2016-11-21T17:40:50.956181+01:00 Up
Under bash, you could use timeout for emptying fifo and read only last entry:
while read -t .01 entry </run/netlink;do line="$entry";done
state=${line#*Link is }
eventtime=${line%% $HOSTNAME*}
shortstate=${state%% *}
Nota: I've used /run to store fifo. This could be not the better place as this won't exist on next reboot.

Detect IP-Address change on an interface

I would like to trigger a service when a change of an ip address on a specific interface occurs. Is there a target for this or some other method I am not aware of to achieve this using systemd on Linux (Kernel 3.19)?
The service would be used to send a SIGNAL to a defined process. The Linux is running on an embedded system.
Thanks!
Because you use Systemd you might already use systemd-networkd for managing your devices instead of relying on 3rd party code.
You could use the structured journal output to get the last 2 ADDRESS field of the current BOOD_ID.(sadly, there is no notification mechanism for address changes in systemd-networkd):
→ sudo journalctl -F ADDRESS -u systemd-networkd -n 2
192.168.178.29
So, if there is only one line output, there was no address change.
There is an solution in other question of StackOverflow. Just here:
Detecting a change of IP address in Linux
I like this code, it's easy, you onli need a cron job with frecuency as you need (I made a little change):
#!/bin/bash
OLD_IP=`cat ip.txt`
NEW_IP=`/sbin/ifconfig | awk -F "[: ]+'{ print $4}'`
if [ $NEW_IP != OLD_IP ]; then
YOU_COMMAND <commands>
echo $NEW_IP > ip.txt
fi
exit 0

is it possible to create a non-child process inside a shell script?

I'm using a shell process pool API at Github, for a script, as below
function foobar()
{
mytask($1);
}
job_pool_init 100 0
tcpdump -i eth0 -w tempcap & #
for i in `seq 1 4`;do
mesg="hello"$i
job_pool_run foobar $mesg
sleep 5
done
job_pool_wait
pkill tcpdump #
echo 'all finish'
job_pool_shutdown
if I comment the tcpdump line,
then it works fine, as expected,
but when the tcpdump line is there,
There is a wait command in job_pool_wait, which waits for the ending of all children process, if there is no such a tcpdump line, it is as expected.
But I want to capture something until all the child processes finish, so I have to use a tcpdump. In this script, tcpdump process is a child process,
job_pool_wait will also wait for the ending of tcpdump process, which is not expected.
so a solution is to make tcpdump not a child process,
how can I do it,
or any other solutions?
thanks!
You should be able to run tcpdump in a sub-shell in the background:
(tcpdump -i eth0 -w tempcap &)
This should prevent it from appearing as a direct descendant of your script.
Answering your literal question, yes, run the command with exec. But I doubt that's what you really wanted.
I think what you really wanted is to be able to wait on specific pid. The wait command takes an optional pid. Either that round need to check when wait returns whether the process that just terminated is a process you're interested in, and wait again if it's not.

Bash script to (more or less) reliably check if the Internet is up

I need a Bash (or a plain shell) script to put in a cronjob that every minute checks if the Internet is up.
This is how I did it:
#! /bin/sh
host1=google.com
host2=wikipedia.org
curr_date=`date +"%Y%m%d%H%M"`
echo -n "${curr_date};"
((ping -w5 -c3 $host1 || ping -w5 -c3 $host2) > /dev/null 2>&1) &&
echo "up" || (echo "down" && exit 1)
How would you do it? Which hosts would you ping?
Clarifications:
By "internet is up", I mean my internet connection.
By "up", I mean to have usable connection (doesn't really matter if we are talking about the DNS being down or the connection is really really slow [mind the -w for timeout]). That is also why I didn't include any IP but only hosts.
Should I also ping Stack Overflow? I mean, if I can't access Google, Wikipedia or Stack Overflow, I don't want Internet :p
That one seems like a good solution. Just add a few more hosts, and maybe some pure IP hosts so you don't rely on DNS functioning (which in itself depends on your definition of "up").
Thanks for your code, it works great, I've left only one line actually:
((ping -w5 -c3 8.8.8.8 || ping -w5 -c3 4.2.2.1) > /dev/null 2>&1) && echo "up" || (echo "down" && exit 1)
What portion of Internet connectivity are you looking to check? DHCP? DNS? Physically being plugged into a jack? Kernel recognizing the presence of the NIC?
You can manually query your ISP's DNS server(s) by using the host(1) command. This is generally a good indication of whether your router has lost its connection to the ISP.
You can query what interfaces your kernel has by using netstat(8) or ifconfig(8).
You can get detailed statistics about the interface using ifstat.

Resources