libssh2 - channel read is hanging - c

I'm currently developing a remote job scheduler on perl.
It has to connect via ssh to x servers and execute already defined jobs/jobs groups.
I use Net:SSH2 which is build upon libssh2.
My program usually works fine with like 400/500 servers, but when i try to run the basic uptime command on 1000 servers, one or more of my threads hangs and never finishes, or like 30 minutes after.
It's random : sometimes it finishes on time, sometimes not.
I tracked the problem as coming from this Net::SSH2 command : $in .= $buf while $chan->read( $buf, 10240 );
Here is the full code of the connection :
my $chan = $this->{netssh2}->channel() or die $!;
$chan->blocking(1);
$chan->exec($command);
my ($in,$err,$buf,$buf_err);
$in .= $buf while $chan->read( $buf, 10240 );
$err .= $buf_err while $chan->read( $buf_err, 10240, 1 );
$chan->send_eof;
1 while !$chan->eof;
$chan->wait_closed;
I then downloaded a Net::SSH2 source package and modified the C-perl linking (xs) file.
It showed me that the problem comes from this line :
count = libssh2_channel_read_ex(ch->channel, XLATEXT, pv_buffer, size);
This command comes with the libssh2 library : http://www.libssh2.org/libssh2_channel_read_ex.html
Sometimes (about 1 in 1000 times) the program enters this read and never leaves. Servers affected are differents most of the time.
Do you have any idea what I should be looking for/checking ?
I've been working on this for a few day, I'd like an external advice very much :)

Related

Powershell ForEach - new loops starting before current loops ending

I have a quick and dirty Powershell script for running sql scripts against many servers sequentially. I have the servers stored in an array, and loop through them with ForEach. The ForEach roughly looks like this:
ForEach ($Server in $ServerList) {
Write-Host "Executing $Script against $Server..."
Invoke-SqlCmd ........
}
But the problem I have is my output looks something like this:
Executing script.sql against Server1
Executing script.sql against Server2
Executing script.sql against Server3
<Output from Server1>
<Output from Server2>
<Output from Server3>
Executing script.sql against Server4
<Output from Server4>
Executing script.sql against Server5
<Output from Server5>
...you get the idea. Is there any way to marry up the outputs so that the output appears under the message depicting which server is currently being executed on? It would help with using output for debugging etc. Executing on PS7 by the way.
What you're observing here is not that the next iteration of the foreach loops starts before the last one has ended - foreach (the loop statement, not the cmdlet) only invokes the loop body in series, never concurrently.
That doesn't mean the next iteration won't start before the formatting subsystem in the host application (eg. powershell.exe or powershell_ise.exe or pwsh.exe) has written any output from the loop body to the screen buffer.
The default host applications usually waits a few 100 milliseconds to see if there's more than one output object of the same type in the output stream - which will then inform how to format the output (table vs list view etc.).
Write-Host on the other hand is an instruction to bypass all that and instead write a message straight to the host application.
So this differentiated delay makes it look like the Write-Host statement at the top is being executed before the code from 2 iterations back - but what you're observing is actually an intentional decoupling of output vs rendering/presentation.
As zett42 notes, you can force the host application to synchronously render the output you want displayed in order by piping it to Out-Host:
ForEach ($Server in $ServerList) {
Write-Host "Executing $Script against $Server..."
Invoke-SqlCmd ........ |Out-Host
}
Since PowerShell must now fulfill your request to render the output before it can move on with the next statement/iteration, it'll no longer delay it for formatting purposes :)

How to track Icecast2 visits with Matomo?

My beloved web radio has an icecast2 instance and it just works. We have also a Matomo instance to track visits on our WordPress website, using only Free/Libre and open source software.
The main issue is that, since Matomo tracks visits via JavaScript, direct visits to the web-radio stream are not intercepted by Matomo as default.
How to use Matomo to track visits to Icecast2 audio streams?
Yep it's possible. Here my way.
First of all, try the Matomo internal import script. Be sure to set your --idsite= and the correct path to your Matomo installation:
su www-data -s /bin/bash
python2.7 /var/www/matomo/misc/log-analytics/import_logs.py --show-progress --url=https://matomo.example.com --idsite=1 --recorders=2 --enable-http-errors --log-format-name=icecast2 --strip-query-string /var/log/icecast2/access.log
NOTE: If you see this error
[INFO] Error when connecting to Matomo: HTTP Error 400: Bad Request
In this case, be sure to have all needed plugins activated:
Administration > System > Plugins > Bulk plugin
So, if the script works, it should start printing something like this:
0 lines parsed, 0 lines recorded, 0 records/sec (avg), 0 records/sec (current)
Parsing log /var/log/icecast2/access.log...
1013 lines parsed, 200 lines recorded, 99 records/sec (avg), 200 records/sec (current)
If so, immediately stop the script to avoid to import duplicate entries before installing the definitive solution.
To stop the script use CTRL+C.
Now we need to run this script every time the log is rotated, before rotation.
The official documentation suggests a crontab but I don't recommend this solution. Instead, I suggest to configure logrotate instead.
Configure the file /etc/logrotate.d/icecast2. From:
/var/log/icecast2/*.log {
...
weekly
...
}
To:
/var/log/icecast2/*.log {
...
daily
prerotate
su www-data -s /bin/bash --command 'python2.7 ... /var/log/icecast2/access.log' > /var/log/logrotate-icecast2-matomo.log
endscript
...
}
IMPORTANT: In the above example replace ... with the right command.
Now you can also try it manually:
logrotate -vf /etc/logrotate.d/icecast2
From another terminal you should be able to see its result in real-time with:
tail -f /var/log/logrotate-icecast2-matomo.log
If it works it means everything will work perfectly and automatically, importing all visits every day, without any duplicate and without missing any lines.
More documentation here about the import script itself:
https://github.com/matomo-org/matomo-log-analytics
More documentation here about logrotate:
https://linux.die.net/man/8/logrotate

Decreasing multiple IF ELSE conditions in Unix, Implementing array

Currently I have below script to check if corresponding services are running on my server or not using some internal logic. Please find code snippet below:
MY_SERVER_ID=X22 //stored somewhere in profile of the servers
if [ "$MY_SERVER_ID" = "X11" -o "$MY_SERVER_ID" = "X22" -o "$MY_SERVER_ID" = "X33" ]
then
##do xx
echo " Service 1 : Running "
fi
if [ "$MY_SERVER_ID" = "X11" -o "$MY_SERVER_ID" = "X22" ]
then
##do xx
echo " Service 2 : Running "
fi
Now as all servers do not run all the services the If conditions become a lot more unreadable/ unnecessary complex. Currently I have 10+ servers and 8+ services where different services run on different servers. Also in future any service can be invoked/ start running on a particular node it wasn't running before in which case I have to go and change the script again.
I understand that in case of any change I definitely have to change the script and update in all the servers however I would like to make the process less painful than it already is.
I can implement something like an array that is defined at the start of the script to point out whether a given service runs on a particular node or not. Something I picked up from this question on stackoverflow.
I know multi dimensional arrays could be easily implemented in C but as I am quite new to shell scripting I would like to know if there is any possibility to make my script more readable and easily editable!!!
Since all your if checks are with the same variable, you can simplify it to case
case "$MY_SERVER_ID" in
X11|X22|X33)
echo "Service 1 running" ;;
esac
bash doesn't have multi-dimensional arrays, but you could use a space-delimited string as a substitute for a second dimension, as long as the values are simple.
declare -A services
all_services=([X11]="1 2" [X22]="1 2" [X33]="1")
services=${all_services[$MY_SERVER_ID]}
for i in $services; do
echo "Service $i running"
done
Another possibility is storing the data in a JSON file and using the jq utility to parse it and extract values. This more elaborate version is left as an exercise for the reader (I'm not really very experienced with this tool).

How to check how many files in folder - by croned script in MAC OS X

I have this problem:
I'm bored by constantly checking if there are new subtitles for my favourite tv series and i want to create something what inform me when it will be available.
There is an idea:
i'm checking if there are subtitles for my tv series file. e:
(Qnapi is an app to download any subtitles)
open -a QNapi.app my.favourite.tvseries.mp4
delay 20
killall QNapi
then i need to do something like this:
if (count(files) > 1) then
mail "There are subtitles!", "my#email.com", "myother#email.com"
And then i need to put this in a hourly executed loop.
I belive i can handle with loop but how to check files count in folder and put this whole thing into .sh file?
Ok i have this for now:
open -a Qnapi.app mytvseries.mp4
sleep 5
killall QNapi
sleep 5
FCOUNT="$(ls -1 | wc -l)"
if [ "${FCOUNT}" -gt 2 ]
then
echo "There's Beans"
fi

Auto-Running a C Program on Raspberry PI

how can I make my C code to auto-run on my Raspberry PI? I have seen a tutorial so as to achieve that but I do not really know what I am still missing. My initialization script is shown as it follows:
#! /bin/sh
# /etc/init.d/my_settings
#
# Something that could run always can be written here
### BEGIN INIT INFO
# Provides: my_settings
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# X-Interactive: true
# Short-Description: Script to start C program at boot time
# Description: Enable service provided by my_settings
### END INIT INFO
# Carry out different functions when asked to by the system
case "$1" in
start)
echo "Starting RPi Data Collector Program"
# run application you want to start
sudo /home/pi/Documents/C_Projects/cfor_RPi/charlie &
;;
stop)
echo "Killing RPi Data Collector Program"
# kills the application you want to stop
sudo killall charlie
;;
*)
echo "Usage: /etc/init.d/my_settings {start | stop}"
exit 1
;;
esac
exit 0
The problem is that my program does not run at boot time and I do not really know why. What would I be missing? Is this "killall" statement "killing" some useful process during execution time? I am making this code to run as a background application but I know that after a few seconds, when the RPi is initializing, it asks for an username and a password in order to initialize the session. Is it possible that my RPi is not executing this code because I am not providing the logging information? I do not have a monitor so that my program has to run once I plug my Rpi in. Thanks a lot in advance!!
You'll have to create links to that init script in the proper /etc/rcX.d folders. On raspbian this is done by:
sudo update-rc.d YOUR_INIT_SCRIPT_NAME defaults
You can read this debian how-to for further information. Also you should read more about run levels in Debian.
How scripts/services are run at startuptime, generally depends on the type of init system used. Off the top of my head, I'd distginguish the following 4 types:
Embedded style: A single shell script has all the commands to start the system. Usually the script is at one off the paths the kernel tries to start as init process.
BSD style
System V style: This uses /etc/inittab and latr scripts in /etc/rc*.d/ to start services one by one
systemd
Raspbian dervices from Debian, so I suppose System V style. You have to symlink your script to /etc/rc2.d like
ln -s /etc/init.d/your-script /etc/rc2.d/S08my-script
Not the structure of the link name: It says, it should be started when the run level is entered, and the '08' determines the position (do a ls /etc/rc2.d/ to see the other links).
More details: init(8).
update-rc.d(8) is the proper wway to create the symlinks on debian. See the manpage:
update-rc.d - install and remove System-V style init script links
I advice to read at least the man pages update-rc.d(8) and init(8).
http://www.akeric.com/blog/?p=1976
Here a tutorial on how to auto-loggin and start a script at boot.
If it still don t work, there s either a problem in your script or in your C program.

Resources