Run command as another user in Linux with `su - user c` creates a duplicate process - linux

I want to run a process by a service as root user, because the daemon may have its own user.
But when I run it with system("su - root c ./testbin"), the system shows two processes (I check this via ps aux | grep testbin):
su - root c ./testbin
and
./testbin
How to achieve a single process?

The "su" process can not be avoided, but you can get it to end before your testbin does.
Your original question using "sleep" looks like this:
(su - root -c "sleep 120" &) ; ps aux | grep sleep
If you execute that line multiple times you will see multiple "su" processes as a result of the grep.
Backgrounding the subprocess allows the su process to end, like this:
(su - root -c "sleep 120 &" &) ; ps aux | grep sleep
When you execute that line multiple times you can see that the "su" processes disappear from the list but that the sleep commands continue.
Note that the ampersand inside the double quotes is for the sub process and that the ampersand just before the parentheses is for the 'su' command which is required to perform your question in a single line and speed up testing this case.
I checked if an equivalent of 'execv' exists for the command line, but this does not seem to be the case. Also, 'su' is a process that runs with the permissions of the caller and the subprocess of su runs with the permissions of the process forked by 'su'. It seems logical to me that you can not replace the 'su' process with its child as 'execv' does in 'C' for security reasons.

ps aux | grep testbin | grep -v grep
I think you just see your "grep" process. Use the command above.

Related

Bash script to list all processes in the foreground process group of a terminal

How can I write a bash script to print out the PIDs of all processes in the foreground process group of a given terminal (which is different from the one in which I run the script)? I know that the C function tcgetpgrp can do the job, but I am wondering if there exist any command line utilities that can do this more easily.
To find the pids of all processes in the foreground process group of pts/29, you can do (on linux):
ps ao stat=,pid=,tty= | awk '$1 ~ /\+/ && $3 ~ /pts\/29/{ print $2}'
ps is often different, and I am uncertain of the portability of that solution.
You can use pgrep's -t flag, which enables you to list process using a given tty.
For example :
# on a first ssh session, which gets pts/0 :
sleep 10
# on a second ssh session :
pgrep -t "pts/0"
1234 # the first session's bash process
5678 # the first session's sleep process

suspend a shell command without pid

I need something like $command & stop This should execute a command and suspend it. The application later resumes back the command for complete results.
I understand that job can be suspended with stop signal to the corresponding pid.
$kill -SIGSTOP 12753
When we execute a command, we barely know its pid. There is extra command involved to take a pid and do the required. I want to avoid the extra command and a time interval.
Basically The application is for a measure of network performance. Trigger all the commands put them in halt mode. The halted commands are resumed back as per the kind of traffic needed.
The process ID of the most recently started background command is available in the shell parameter $!:
$ command & kill -SIGSTOP $!
(Check the documentation for your shell's implementation of kill for the correct format.)
Try killall with the --signal option where you can specify the name of the process.
linux:~ # killall
Usage: killall [OPTION]... [--] NAME...
killall -l, --list
killall -V, --version
-e,--exact require exact match for very long names
-I,--ignore-case case insensitive process name match
-g,--process-group kill process group instead of process
-i,--interactive ask for confirmation before killing
-l,--list list all known signal names
-q,--quiet don't print complaints
-r,--regexp interpret NAME as an extended regular expression
-s,--signal SIGNAL send this signal instead of SIGTERM
-u,--user USER kill only process(es) running as USER
-v,--verbose report if the signal was successfully sent
-V,--version display version information
-w,--wait wait for processes to die
Verified by starting md5sum in a shell session:
linux$ md5sum
and in another session, ran:
killall -s SIGSTOP md5sum
yielding the following in the md5sum session:
[1]+ Stopped md5sum
Kindly confirm if you want to halt your command or run in background(append '&' to your command)?
If your application is expected to start halted command later, then why dont you start your command(to be halted) in that application itself.
This helps :
sleep 5 & kill -SIGSTOP $!
In above, have executed sleep(demo command) for 5 seconds in background.
Next have send to kill for stopping it using its PID obtained by $!.
Demo & kludge using timeout, (for some reason timeout intereprets a '0s' duration as "run forever"), to stop yes before it outputs anything:
# run 'yes' command, let it print 5 numbered lines, but stop it immediately
timeout -s SIGSTOP .000000001s yes | head -n 5 | cat -n
Output (to STDERR):
[1]+ Stopped timeout -s SIGSTOP .000000001s yes | head -n 5 | cat -n
Now restart it:
fg > /dev/null
Output:
1 y
2 y
3 y
4 y
5 y
Technique for users stuck with v8.12 or earlier coreutils, (pre-2011), wherein timeout lacks sub-second intervals. Requires waiting a second.
Wrap the command string in a shell invocation, preceded by a 1s wait -- so timeout waits 1 second, and simultaneously, so does the command string. Total wait time 1 second:
timeout -s SIGSTOP 1s sh -c "sleep 1s; yes | head -n 5 | cat -n"
Output is the same as before, fg is the same too.
Finesse, if waiting even 1 second before sleeping is too much, it can be run in the background like so:
timeout -s SIGSTOP 1s sh -c "sleep 1s; yes | head -n 5 | cat -n" &
Output (process number will vary):
[1] 14601
Then after a second, the output will be the same as the previous two timeout examples.
Assuming you are using the same command, find the command name in ps output, you can launch it in one terminal then open a new terminal
ps -ely
after retrieving the command name:
command & kill -SIGSTOP $(pidof command_name)
pidof needs the exact command name to be able to find the pid.
then to resume it:
kill -SIGCONT $(pidof command_name)
if the command name is not constant, but there is a pattern, you can create a script like this, you can call it pof.sh:
ps -ely | grep $1 | tr -s ' ' | cut -d" " -f3
command & kill -SIGSTOP $(bash pof.sh pattern)
One drawback with this script, is that in case many lines match the pattern it will returns all of theirs pids, if this is a problem, you can put the output in an array and go on from there.

Linux bash script that kills a process (not started by me) after x amount of time

I'm pretty inexperienced with Linux bash. That being said, I have a CentOS7 machine that runs a COTS application server. This application server runs other processes that sometimes hang. Since I have no control over the start of these processes, I'm looking for a script that runs every 2 minutes that kills processes of the name "spicer" that have been running for longer than 10 minutes. I've looked around and have only been able to find answers for processes that are run and owned by me.
I use the command ps -eo pid, command,etime | grep spicer to get all the spicer processes. The output of this command looks like:
18216 spicer -l/opt/otmm-10.5/Spi 14:20
18415 spicer -l/opt/otmm-10.5/Spi 11:49
etc...
18588 grep --color=auto spicer
I don't know if there's a way to parse this directly in bash. I'm also not well-versed at all in other Linux tools. I know that awk (or gawk) could possibly help.
EDIT
I have no control over the data that the process is working on.
What about wrapping the executable of spicer and start it using the timeout command? Let's say it is installed in /usr/bin/spicer. Then issue:
cp /usr/bin/spicer{,.orig}
echo '#!/bin/bash' > /usr/bin/spicer
echo 'timeout 10m spicer.orig "$#"' >> /usr/bin/spicer
Another approach would be to create a cronjob defintion into /etc/cron.d/kill_spicer. Like this:
* * * * * root kill $(ps --no-headers -C spicer -o pid,etimes | awk '$2>=600{print $1}')
The cronjob will get executed minutely and uses ps to obtain a list of spicer processes that run longer than 10minutes and passes them to kill.
Probably you even want kill -9 if the process is hanging.
You can use the -C option of ps to select processes by name.
ps --no-headers -C spicer -o pid,etime
Then you can use cut to filter the results, if the spacing is consistent. On my system the pid field takes up 8 characters, so I'd use
kill $(ps --no-headers -C spicer -o pid,etime | cut -c-8)
If the spacing is inconsistent (but if so, what kind of messed up ps are you using? :-P), you can use awk { print $1 } instead of cut.

Bash script commands not running

I have seafile (http://www.seafile.com/en/home/) running on my NAS and I set up a cron tab that runs a script every few minutes to check if the seafile server is up, and if not, it will start it
The script looks like this:
#!/bin/bash
# exit if process is running
if ps aux | grep "[s]eafile" > /dev/null
then exit
else
# restart process
/home/simon/seafile/seafile-server-latest/seafile.sh start
/home/simon/seafile/seafile-server-latest/seahub.sh start-fastcgi
fi
running /home/simon/seafile/seafile-server-latest/seafile.sh start and /home/simon/seafile/seafile-server-latest/seahub.sh start-fastcgi individually/manually works without a problem, but when I try to manually run this script file, neither of those lines execute and seafile/seahub do not start
Is there an error in my script that is preventing execution of those 2 lines? I've made sure to chmod the script file to 755
The problem is likely that when you pipe commands into one another, you don't guarentee that the second command doesn't start before the first (it can start, but not do anything while it waits for input). For example:
oj#ironhide:~$ ps -ef | grep foo
oj 8227 8207 0 13:54 pts/1 00:00:00 grep foo
There is no process containing the word "foo" running on my machine, but the grep that I'm piping ps to appears in the process list that ps produces.
You could try using pgrep instead, which is pretty much designed for this sort of thing:
if pgrep "[s]eafile"
Or you could add another pipe to filter out results that include grep:
ps aux | grep "[s]eafile" | grep -v grep
If the name of this script matches the regex [s]eafile it will trivially always take the exit branch.
You should probably be using pidof in preference of reinventing the yak shed anyway.
turns out the script itself was working ok, although the change to using pgrep is much nicer. the problem was actually in the crontab (didn't include the sh in the command)

count processes in shell script [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Quick-and-dirty way to ensure only one instance of a shell script is running at a time
I am new to shell script.
what I wanna do is to avoid running multiple instances of a script.
I have this shell script cntps.sh
#!/bin/bash
cnt=`ps -e|grep "cntps"|grep -v "grep"`
echo $cnt >> ~/cntps.log
if [ $cnt < 1 ];
then
#do something.
else
exit 0
fi
if I run it this way $./cntps.sh, it echoes 2
if I run it this way $. ./cntps.sh, it echoes 0
if I run it with crontab, it echoes 3
Could somebody explain to me why is this happening?
And what is the proper way to avoid running multiple instances of a script?
I changed your command slightly to output ps to a log file so we can see what is going on.
cnt=`ps -ef| tee log | grep "cntps"|grep -v "grep" | wc -l`
This is what I saw:
32427 -bash
20430 /bin/bash ./cntps.sh
20431 /bin/bash ./cntps.sh
20432 ps -ef
20433 tee log
20434 grep cntps
20435 grep -v grep
20436 wc -l
As you can see, my terminal's shell (32427) spawns a new shell (20430) to run the script. The script then spawns another child shell (20431) for command substitution (`ps -ef | ...`).
So, the count of two is due to:
20430 /bin/bash ./cntps.sh
20431 /bin/bash ./cntps.sh
In any case, this is not a good way to ensure that only one process is running. See this SO question instead.
Firstly, I would recommend using pgrep rather than this method. Secondly I presume you're missing a wc -l to count the number of instances from the script
In answer to your counting problems:
if I run it this way $./cntps.sh, it echoes 2
This is because the backtick call: ps -e ... is triggering a subshell which is also called cntps.sh and this triggers two items
if I run it this way $. ./cntps.sh, it echoes 0
This is caused as you're not running, it but are actually sourcing it into the currently running shell. This causes there to be no copies of the script running by the name cntps
if I run it with crontab, it echoes 3
Two from the invocation, one from the crontab invocation itself which spawns sh -c 'path/to/cntps.sh'
Please see this question for how to do a single instance shell script.
Use a "lock" file as a mutex.
if(exists("lock") == false)
{
touch lock file // create a file named "lock" in the current dir
execute_script_body // execute script commands
remove lock file // delete the file
}
else
{
echo "another instance is running!"
}
exit

Resources