Implementing background processing - linux

UPDATE:
There was an obvious debugging step I forget. What happens if I try a command like ps &
in the regular old bash shell? The answer is that I see the same behavior. For example:
[ahoffer#uw1-320-21 ~/program1]$ ps &
[1] 30166
[ahoffer#uw1-320-21 ~/program1]$ PID TTY TIME CMD
26423 pts/0 00:00:00 bash
30166 pts/0 00:00:00 ps
<no prompt!>
If I then press Enter, the command shell reports the exit status and the console displays the exit status and the prompt:
[ahoffer#uw1-320-21 ~/program1]$ ps&
[1] 30166
[ahoffer#uw1-320-21 ~/program1]$ PID TTY TIME CMD
26423 pts/0 00:00:00 bash
30166 pts/0 00:00:00 ps
[1] Done ps
[ahoffer#uw1-320-21 ~/program1]$
PS: I am using PuttY to access the Linux machine via SSH on port 22.
ORIGINAL QUESTION:
I am working on a homework assignment. The task is to implement part of a command shell interpreter on Linux using functions like fork(), exec(). I have a strange bug that occurs when my code executes a command as a background process.
For example, in the code below, the command ls correctly executes ls and prints its output to the console. When the command is finished, the The event loop in the calling code correctly prints the prompt, "% ", to the console.
However, when ls & is executed, ls executes correctly and its output is printed to the console. However, the prompt, " %", is never printed!
The code is simple. Here is what the pseudo code looks like:
int child_pid;
if ( (child_pid=fork()) == 0 ) {
//child process
...execute the command...
}
else {
//Parent process
if( delim == ';' )
waidpid(child_pid);
}
//end of function.
The parent process blocks if the delimiter is a semicolon. Otherwise the function ends and the code re-enters the event loop. However, if the parent sleeps while the the background command executes, the prompt appears correctly:
...
//Parent process
if( delim == ';' ) {
waidpid(child_pid)
}
else if( delim == '&' ) {
sleep(1);
//The prompt, " %", is correctly printed to the
// console when the parent wakes up.
}
No one in class knows why this happens. The OS is RedHat Enterprise 5 and the compiler is g++.

Related

Exit status of $? using python when segmentation fault occured

I need to execute echo $? using python3 and capture the exit status. I need this especially for capturing the Segmentation fault (core dumped) status.
I tried :
>>> os.system('echo $?')
0
0
got 0 0. Also, for segfault,
>>> os.system('./a.out')
Segmentation fault (core dumped)
35584
After above command, I again got:
>>> os.system('echo $?')
0
0
Also, why is 0 getting printed twice?
I went through the doccumentation of python-3 which says:
os.system(command)
On Unix, the return value is the exit status of the process encoded in the format specified for wait(). Note that POSIX does not specify the meaning of the return value of the C system() function, so the return value of the Python function is system-dependent.
Does this something say about such behavior?
Help me clarify on this.
Note: I already ran the ulimit -c unlimited before all above steps. The expected result should be non-zero or 139(to be specific).
Edit: I am thinking if there is a limitation on this!
Thanks!
No, you don't need to execute echo $?. It wouldn't be useful. The exit status of the program is the return value of the function os.system. That's what the number 35584 is. The documentation os os.system tells you to read the documentation of os.wait which explains
a 16-bit number, whose low byte is the signal number that killed the process, and whose high byte is the exit status (if the signal number is zero); the high bit of the low byte is set if a core file was produced.
However, note that depending on the shell, with os.system('./a.out'), you may be getting the exit status of a.out or the exit status of the shell itself. Normally there's no difference, because the exit status of the shell is the exit status of the last command it executes. But if the command dies from a signal, then there is a difference. The shell won't kill itself with the same signal, it will return a status that encodes the signal. In most shells, that's 128 + signal_number. For example, if the program dies of signal 11 (segfault on Linux) and leaves a core dump, then its status as returned by wait is 11. But if there's a shell in between, then the shell will exit normally with the exit code 128+11. That's what you're seeing: 35584 is (128 + 11) << 8.
To avoid this complication, use subprocess.call or one of its variants (you can use subprocess.run if you don't need your code to run Python <=3.4).
returncode = subprocess.call(['./a.out'], shell=False).returncode
if returncode & 0xff == 0:
exit_code = returncode >> 8
print('The program exited normally with status {}.'.format(exit_code))
else:
print('The program was killed by signal {}.'.format(returncode))
If you run os.system('echo $?'), this starts a new shell. You're printing the initial value of $? in that shell, before it has run any command, and the initial value of $? in a shell is 0.
You see 0 twice in the interactive environment because the first one is the one printed by the echo command and the second one is the value of the Python expression. Compare os.system('echo hello').
Note that with os.system, you can't access the output of the command, so if you print something with echo, you can't use it in the program. You'd have to use functions in the subprocess module for that, but you need this only if you need the output of ./a.out, not to get its exit status.
When running:
>>> os.system('echo $?')
0
0
and if your previous command was successful a first 0 will be print by echo $? and another one will be the return code of the call to echo $? that has just succeeded so you will have another 0 printed.
The return code of the script/command that you execute will directly be returned to your python program by os.system function so you do not need to use echo $?
Examples:
$ more return_code*
::::::::::::::
return_code1.py
::::::::::::::
import os
print os.system('sleep 1')
#will print 0 after 1sec
::::::::::::::
return_code2.py
::::::::::::::
import os
print os.system('ls abcdef')
#will print a rc!=0 if the file abcdef is not present in your working directory
Executions:
$ python return_code1.py
0
and
$ python return_code2.py
ls: cannot access 'abcdef': No such file or directory
512
I have written the following code for the above question and worked as expected. I used subprocess.Popen() method for achieving my requirement. I used sample.returncode to get the exit status of the shell.
def run_cmd():
ret = 0
sample_cmd = "./a.out"
sample = subprocess.Popen(sample_cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out_stdout, out_stderr = sample.communicate()
if sample.returncode != 0:
print ("OUTPUT: %s\nERROR: %s\n"%(out_stdout, out_stderr))
print ("Command: %s \nStatus: FAIL "%(sample_cmd))
sys.stdout.flush()
if sample.returncode == 139:
print('Segmentation fauilt(core dumped) occured...with status: ', sample.returncode)
ret = sample.returncode
else:
ret = 1
else:
print ("OUTPUT: %s\n"%(out_stdout))
print ("Command: %s \nStatus: PASS "%(sample_cmd))
ret = 0

Forking Turtle inshell command not streaming stdout

I'm using the the following function to fork commands in my Turtle script:
forkCommand shellCommand = do
pid <- inshell (shellCommand <> "& echo $!") empty
return $ PID (lineToText pid)
The reason for doing this is because I want to get the PID of the forked process that I'm running.
The issue is that the command I'm ruining isn't streaming any stdout. For example you could set shellCommand to:
"python -c \"print('Hello, World')\""
and you won't see the print occur.

Perl script to capture tcpdump traces on Linux

Hi I have written a script, which was working fine previously with 'snoop' commands. This script forks child in the script to start tcpdump. When i have to stop the dump I kill the child but when i look at the pcap generated in wireshark, it shows the error "The capture file appears to have been cut short in the middle of a packet". My commands are
my $snoopAPP = &startService("tcpdump -w /tmp/app.pcap -i bond0>/dev/null 2>&1" , '');
kill 9, -$snoopAPP;waitpid $snoopAPP, 0;
sub startService(){
#runs a program in background and returns PID which can be used later to kill the process
#arguments are 1, path , 2nd the name of the file
my $processPath = $_[0];chomp($processPath);
if ($_[1] ne ''){$processPath = $processPath . " >$path/$_[1].log";}
print "\nStarting ... \n-- $processPath\n";
my $pid = fork();
die "unable to fork $processPath: $!" unless defined($pid);
if (!$pid) { # child
setpgrp(0, 0);
exec("$processPath");
die "\nunable to exec: $!\n";
exit;
}
print " ---- PID: $pid\n";
return $pid;
}
Another post suggests to wait for tcpdump to exit, which I am doing already, but still it results in the same error message.
Try
kill 15, -$snoopAPP
Signal 9, SIGKILL, is an immediate terminate, and doesn't give the application the opportunity to finish up, so, well, the capture file stands a good chance of being cut short in the middle of a packet.
Signal 15, SIGTERM, can be caught by an application, so it can clean up before terminating. Tcpdump catches it and finishes writing out buffered output.

How to get the exit code of spawned process in expect shell script?

I am trying to execute a script that executes an EXPECT script and a spawned process which has exit code in it. But I'm unable to get the exit code of the spawned process to main script. I'm always getting zero as success.
expect script is :
[Linux Dev:anr ]$ cat testexit.sh
#!/bin/bash
export tmp_script_file="/home/anr/tmp_script_temp.sh"
cp /home/anr/tmp_script $tmp_script_file
chmod a+x $tmp_script_file
cat $tmp_script_file
expect << 'EOF'
set timeout -1
spawn $env(tmp_script_file)
expect {
"INVALID " { exit 4 }
timeout { exit 4 }
}
EOF
echo "spawned process status" $?
rm -f $tmp_script_file
echo "done"
Spawned script:
[Linux Dev:anr ]$ cat tmp_script
exit 3
Execution of Expect script:
[Linux Dev:anr ]$ ./testexit.sh
exit 3
spawn /home/anr/tmp_script_temp.sh
spawned process status 0
done
Problem is I am unable to get the spawned exit return code to expect script. I want the exit code 3 of spawned script to main script and main script should be exit with exit code 3.
Please help me to get the spawned exit code to main script.
You get the exit status of the spawned process with the wait command:
expect <<'END'
log_user 0
spawn sh -c {echo hello; exit 42}
expect eof
puts $expect_out(buffer)
lassign [wait] pid spawnid os_error_flag value
if {$os_error_flag == 0} {
puts "exit status: $value"
} else {
puts "errno: $value"
}
END
hello
exit status: 42
From the expect man page
wait [args]
delays until a spawned process (or the current process if none is named) terminates.
wait normally returns a list of four integers. The first integer is the pid of the process that was waited upon. The second integer is the corresponding spawn id. The third integer is -1 if an operating system error occurred, or 0 otherwise. If the third integer was 0, the fourth integer is the status returned by the spawned process. If the third integer was -1, the fourth integer is the value of errno set by the operating system. The global variable errorCode is also set.
Change
expect {
"INVALID " { exit 4 }
timeout { exit 4 }
}
to
expect {
"INVALID " { exit 4 }
timeout { exit 4 }
eof
}
Then add the lassign and if commands.
With the help of glenn, I got solution.. and my final script is::
expect script is
[Linux Dev:anr ]$ cat testexit.sh
#!/bin/bash
export tmp_script_file="/home/anr/tmp_script_temp.sh"
cp /home/anr/tmp_script $tmp_script_file
chmod a+x $tmp_script_file
cat $tmp_script_file
expect << 'EOF'
set timeout -1
spawn $env(tmp_script_file)
expect {
"INVALID " { exit 4 }
timeout { exit 4 }
eof
}
foreach {pid spawnid os_error_flag value} [wait] break
if {$os_error_flag == 0} {
puts "exit status: $value"
exit $value
} else {
puts "errno: $value"
exit $value
}
EOF
echo "spawned process status" $?
rm -f $tmp_script_file
echo "done"
Spawned script:
[Linux Dev:anr ]$ cat tmp_script
exit 3
Execution of Expect script:
[Linux Dev:anr ]$ ./testexit.sh
exit 3
spawn /home/anr/tmp_script_temp.sh
exit status: 3
spawned process status 3
done
Thanks Glenn once again..
After struggling few days with expanding variable inside the expect heredoc, finally i came across an another approach i thought may be helpful to someone in need. My requirement was to pass command and password to a shell function, execute the command in remote host as part of expect heredoc and get the return exit code.
Example:
function shell_function {
# Get the command and password as arguments
# Run command using expect
# Return the exit code
}
shell_function <cmd> <password>
echo $?
Like everyone else expanding of variable inside the heredoc was a problem, which required exporting the value into an environment variable and use env to get the variable inside heredoc. Since, password was one of the arguments i didn't want to store it as part of an environment variable. So, instead of enclosing heredoc opening with single quotes, the variables of heredoc have been escaped. This allowed the direct usage of arguments passed.
Following is the final script:
#! /bin/bash
# This function runs a command like 'ssh' and provides the password
function run_with_password {
cmd="$2"
paswd="$1"
expect << END
set timeout 60
spawn $cmd
expect {
"yes/no" { send "yes\r" }
"*assword*" { send -- $paswd\r }
}
expect EOF
catch wait result
exit [lindex \$result 3]
END
}
my_password="AnswerIS42Really?"
cmd_to_run="ssh userid#hostname"
cmd_to_run="$cmd_to_run ls .sawfish"
run_with_password $my_password "$cmd_to_run"
echo "Command run code: $?"
In the above code the escaped expect variable is $result. After changing the variable to \$result, the script started working like charm.
My sincere thanks to users who have provided answers to following questions, which served as a stepping stones to reach my solution.
Douglas Leeder: help with expect script, run cat on remote comp and get output of it to the variable
glenn jackman: How to return spawned process exit code in Expect script?

perl fork() exec() , child process gone wild

I am using Linux and .sh is in tcsh.
I have made a very basic fork and exec, but I need help in implementing safeties to it.
Basically my perl script calls a .sh script in a child process. But when I do Ctrl+c to kill the parent, the signal gets ignored by the child.
1) How do I capture the SIGINT for the child process as well?
2) The child process that runs the .sh script still STDOUT to the screen of the xterm. How can I remove this? I was thinking of doing running the script in the background
exec("shell.sh args &");
But haven't tested as I need to figure out how to keep the child from going wild first.
3) The parent process(perl script) doesn't wait on the child(.sh script). So I've read a lot about the child becoming a zombie??? Will it happen after the script is done? And how would I stop it?
$pid = fork();
if($pid < 0){
print "Failed to fork process... Exiting";
exit(-1);
}
elsif ($pid ==0) {
#child process
exec("shell.sh args");
exit(1);
}
else { #execute rest of parent}
But when I do ctrl+c to kill the parent, the signal gets ignored by the child.
The signal is sent to two both the parent and the child.
$ perl -E'
if (my $pid = fork()) {
local $SIG{INT} = sub { say "Parent got SIGINT" };
sleep;
waitpid($pid, 0);
} else {
local $SIG{INT} = sub { say "Child got SIGINT" };
sleep;
}
'
^CParent got SIGINT
Child got SIGINT
If that child ignores it, it's because it started a new session or because it explicitly ignores it.
The child procces that runs the .sh script still STDOUT to the screen of the xterm. How can I remove this?
Do the following in the child before calling exec:
open(STDOUT, '>', '/dev/null');
open(STDERR, '>', '/dev/null');
Actually, I would use open3 to get some error checking.
open(local *CHILD_STDIN, '<', '/dev/null') or die $!;
open(local *CHILD_STDOUT, '>', '/dev/null') or die $!;
my $pid = open3(
'<&CHILD_STDIN',
'>&CHILD_STDOUT',
'>&CHILD_STDOUT',
'shell.sh', 'args',
);
The parent process(perl script) doesn't wait on the child(.sh script). So I've read alot about the child becoming a zombie???
Children are automatically reaped when the parent exits, or if they exit after the parent exits.
$ perl -e'
for (1..3) {
exec(perl => (-e => 1)) if !fork;
}
sleep 1;
system("ps");
' ; ps
PID TTY TIME CMD
26683 pts/13 00:00:00 bash
26775 pts/13 00:00:00 perl
26776 pts/13 00:00:00 perl <defunct> <-- zombie
26777 pts/13 00:00:00 perl <defunct> <-- zombie
26778 pts/13 00:00:00 perl <defunct> <-- zombie
26779 pts/13 00:00:00 ps
PID TTY TIME CMD
26683 pts/13 00:00:00 bash
26780 pts/13 00:00:00 ps
<-- all gone
If the parent exits before the children do, there's no problem.
If the parent exits shortly after the children do, there's no problem.
If the parent exits a long time after the children do, you'll want to reap them. You could do that using wait or waitpid (possibly from a SIGCHLD handler), or you could cause them to be automatically reaped using $SIG{CHLD} = 'IGNORE';. See perlipc.
Use waitpid in the parent thread: http://perldoc.perl.org/functions/waitpid.html
waitpid($pid, 0);
You can also redirect stdout of your exec to /dev/null:
exec("shell.sh args > /dev/null");

Resources