How to check if a drive is connected (mounted)? - c++

is it possible wit Qt Qprocess to detect if a mounted Loacation(maybe NAS) is connected?
Generally i would check in /proc/mounts if there is an entry, but if i disconnect to the NAS the file doesn't realizes it.
with the df command i can check if a mountpoint is available. But if the connection is disconnected the df process doesn't gives an output. Maybe infinite.
I tried it with
QProcess p;
p.start("bash", QStringList() << "-c" <<
"df -P -T /media/storage/ | grep QIS | awk -F ' ' '{print $1}'");
if (p.waitForFinished(2))
{
qDebug() << "Nothing";
p.close();
}
But nothing happens.
It seems that my program "freezes" when i try to df to a directory which ist'n mounted. Is it possible to cancel the process if there is no answer from the df process after, for example, 2 seconds?

Regarding QProcess, QProcess::waitForFinished takes msec as argument, not sec. Furthermore, it is better to connect to QProcess::finished signal instead of blocking the event loop.
That being said, why using QProcess, when there is a QStorageInfo with a mountedVolumes method:
for (const auto &storage : QStorageInfo::mountedVolumes()) {
if (storage.isValid() && storage.isReady()) {
if (!storage.isReadOnly()) {
// ...
}
}
}

Related

QThread doesn't provide output from called shellscript

I have a function that executes system calls and writes the output into a QTextEdit.
string SystemCallFactory::runSysCallWithoutButton(unique_ptr<SystemCall> sysCall)
{
cout << "Running " << sysCall->getCommand() << endl;
textEdit->setText("");
textEdit->repaint();
QProcess process;
process.start(QString::fromStdString(sysCall->getCommand()));
process.waitForFinished();
QString output(process.readAllStandardOutput());
textEdit->append(output);
textEdit->repaint();
return output.toStdString();
}
The problem is that this works when getCommand() is a real command, e.g. ls /etc but does not work correctly when it refers to a shellscript. In my calls, the call that does not work looks like
/home/turtle10000/tilematching.sh /some/folder /some/specific.file
The script gets executed and does what it's supposed to do, but readAllStandardOutput() returns an empty string. When I run it in a terminal, it shows the expected output.
This is the shellscript:
#!/bin/sh -v
WORKSPACE=$1
SVPFILE=$2
cd $WORKSPACE
ls -1 *.all > datalist.0.mb-1
mbset -I datalist.0.mb-1 -PSVPMODE:1 -PSVPFILE:$SVPFILE
mbprocess -I datalist.0.mb-1
ls -1 *p.mb58 > datalist.1.mb-1
mbset -I datalist.1.mb-1 -PSONAROFFSETX:-0.079 -PSONAROFFSETY:0.196 -PSONAROFFSETZ:0.048 -PVRUOFFSETX:-0.4473 -PVRUOFFSETY:0.000 -PVRUOFFSETZ:-0.3395 -PROLLBIAS:0.1 -PPITCHBIAS:1.32
mbprocess -I datalist.1.mb-1
ls -1 *pp.mb58 > datalist.2.mb-1
mblist -I datalist.2.mb-1 -MA -O^X^Y-z -JU > output.xyz
#remove broken lines (error in input files)
sed -ie '/ /d' output.xyz
Edit: as Botje guessed, some of output appears in stderr instead of stdout.
As Botje suggested, I checked if the output went to stderr and it did. For whatever reason, the output of mbset and mbprocess, tools from the mb-system package, goes to stderr.
In this case, I changed the line
QString output(process.readAllStandardOutput());
to
QString output(process.readAllStandardError());
Another option would probably be to channel the stderr output to stdout in the script.

AWK catching a regular expression

I have been using this little script for months now with success. Today I realize there is one output it cant seem to catch, screen comes up blank with a new prompt:
user#computer ~]$ myscan ipsFile 23
user#computer ~]$
Here is the code
#!/bin/bash
sudo nmap -v -Pn -p T:$2 -reason -i $1 | awk ' {
if (/syn-ack/) {
print "Yes"
c++
}
else if (/no-response|reset|host-unreach/) {
print "No"
c++
}
}
END { print c} '
If I run the nmap against one of the IPs then it returns
Starting Nmap 5.51 ( http://nmap.org ) at 2017-09-26 11:44 CDT
Initiating Parallel DNS resolution of 1 host. at 11:44
Completed Parallel DNS resolution of 1 host. at 11:44, 0.00s elapsed
Initiating Connect Scan at 11:44
Scanning 1.1.1.1 [1 port]
Completed Connect Scan at 11:44, 0.20s elapsed (1 total ports)
Nmap scan report for 1.1.1.1
Host is up, received user-set (0.20s latency).
PORT STATE SERVICE REASON
23/tcp filtered telnet host-unreach
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.26 seconds
How can I catch the 'host-unreach' portion?
Let's try and debug this. Execute this:
nmap -v -Pn -p T:23 -reason -i ipsFile | awk '{print $0}/syn-ack/{print "Yes";c++}/no-response|reset|host-unreach/{print "No";c++}END {print c}' > out.txt
The only difference here is that the awk script prints $0 (i.e. the output of your nmap calls) to file out.txt. Try to grep your unreach value.
I tried this myself and found that instead of a host-unreach I got a net-unreach. Might be the same thing in your case.
Have you tried piping stderr to stdout like
#!/bin/bash
sudo nmap -v -Pn -p T:$2 -reason -i $1 2>&1 | awk ' {
if (/syn-ack/) {
print "Yes"
c++
}
else if (/no-response|reset|host-unreach/) {
print "No"
c++
}
}
END { print c} '

Read output in terminal and compare it with a string

How to check whether a program like 'fortune' is already installed programatically?
This is my code:
if(choice==1)
{
cout<<"Need to Install program first? [y/n]: ?"<<endl;
cin>>yn;
if(yn=='y' || yn=='Y')
{
cout<<"Installing..."<<endl;
cout<<"Enter password if asks:"<<endl;
system(" sleep 2");
system("sudo apt-get install fortune");
}
I have tried this, but couldn't get what I want.
dpkg-query -1W fortune 2>&1 | read line ; do echo $line | say ; done
UPDATE: I have solved this long back and thanks to #John Zwinck
If you want to know if a program like fortune is installed, you can simply check if it exists!
if (access("/usr/bin/fortune", R_OK | X_OK) == 0)
printf("we have good fortune\n");
If you have a program which should be somewhere in $PATH but you don't know where, you could try to run it:
if (system("fortune --help") != -1)
printf("we have good fortune\n");

Audit bash commands for bash RPG

I want to be able to write a program that when you type in commands, it will do things like count the amount of times you've used cd. Something similar to this:
[ : ~ $] cd public_html
Congratulations! You've earned the Badge 'cd master!'. Level up!
All my C++ file consists of so far is:
#include <iostream>
int main(int argc, char* argv[]) {
int counter = 0;
for (int i = 1; i < argc; i++) {
std::cout << argv[i] << std::endl;
if (argv[i] == "cd")
std::cout << "Badge earned 'cd master!' +5120 experience points" << std::endl;
}
return 0;
}
As it reflects one attempted solution involving:
#!/bin/sh
bash | tee ./main
and
bind 'RETURN: "echo $(!!) | tee ~/.main \n"'
I've decided to go with
export PROMPT_COMMAND='history | tail -n1'
But that would mean having to parse the output.
What's the easiest way of accomplishing this?
edit
Here's what I've managed to produce:
#!/bin/sh
export COUNTER=0
export MAXWIDTH=10
export TNL=1000
update_prompt() {
export PS1="> "
}
cd() {
COUNTER=$(($COUNTER + 25));
echo +25;
builtin cd $#;
}
help() {
echo "custom commands are: score";
}
score() {
echo $COUNTER"/"$TNL
BAR=$(yes "#" | head -n 10 | tr -d '\n')
OVERLAY=$(yes "%" | head -n 10 | tr -d '\n')
WIDTH=$(echo "$COUNTER*$MAXWIDTH/$TNL" | bc)
FIRST=${BAR:0:WIDTH}
SECOND=${OVERLAY:0:$((MAXWIDTH-WIDTH))}
echo "["$FIRST$SECOND"]"
}
exit() {
echo "Bye bye";
builtin exit $#;
}
export -f update_prompt
export -f cd # export the function
export -f help
export -f score
export -f exit
bash # run subshell with the exported functions
update_prompt
An easy solution is to overwrite the cd command of your shell inside the shell itself. For example, in Bash or ZSH:
cd() {
echo "Congratulations";
builtin cd $#;
}
(This is for example used in projects like autoenv.)
You can do the same for all other commands. You can also call your C++ code from there.
If you want to put that into a script, e.g. name it learn-bash.sh:
cd() { ... }
export -f cd # export the function
bash # run subshell with the exported functions
Another solution, where you have much more power, but which is way more involved: take the source code of Bash (it's C) and extend it (by C or C++). Then you can basically do whatever you want. And you have everything directly there, i.e. the parsed commands, etc.
After every command COMMAND_PROMPT is executed within bash. You could use that with history to see the last command used.
You can read here on how PS1, PS2, PS3 and COMMAND_PROMPT work in bash.
There are a few answers on exactly this question already on SO:
How can I intercept commands that contain a certain string?
bash: how to intercept every command
bash: How to intercept command line and do various actions based on the contents?
I've done something similar a while ago, and here's the solution I've found.
You want to add the following lines to .bashrc:
hook() {
whatever "$#"
}
invoke_hook() {
[ -n "$COMP_LINE" ] && return
[ "$BASH_COMMAND" = "$PROMPT_COMMAND" ] && return
local command=`history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//g"`;
hook "$command"
}
trap 'invoke_hook' DEBUG
Replace whatever with your C++ program. This will execute your hook before each command, and will pass the original command as the arguments.

Get PID of new process

So I'm creating a process in Perl like this:
my $process = `nohup ./run > /dev/null 2>&1 &`;
Which returns something along the lines of
[1] 2905
How do I go about getting the process ID from this so later on in the script execution I can run something like:
exec("kill -9 $pid");
Here's what I've got so far:
/\[1\] ([0-9]+)/g
but it looks quite messy, is there any way to improve upon this regular expression? Will that regex always work? Is there any case where it wont be [1]?
how about
#ar = split(/\s+/, $process);
$pid = $ar[1];
You should probably be using a "fork and exec" pattern here.
if (my $pid = fork()) {
# You're in the parent process
# $pid contains the PID of the new child process
...
} else {
# You're in the new child process
# exec() your new command
exec($cmd);
# Execution never gets here
}
Edit: Actually, given that you're basically creating a daemon process here, perhaps you should look at Proc::Daemon instead.