Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing if my program is executed from the bash/ksh/csh command line

Tags:

c

linux

I would like to know if my program is executed from a command line, or executed through system() call, or from a script.

I initially thought about getting parent id (getppid()), and looking up /proc/#pppid directory checking either the exe link or contents of the cmdline file. If it is /bin/bash, or /bin/csh, or /bin/sh I would know that it runs from the command line.

The problem is that it is not true, because a standalone script would also tell me /bin/bash. Even if it worked, it could have been very specific Linux version approach and could stop working in a future.

Is there a better way to do that?

Thank you for any advice or pointing out to some direction.

like image 659
Grzegorz Avatar asked Nov 11 '22 06:11

Grzegorz


1 Answers

Most shells written since 1980 support job control, which is implemented by assigning a process group to each process in a pipeline of commands. The process group is set by setpgrp(), which sets the process's pgrp to its pid.

The pgrp is inherited across forks.

So, if your shell is a relatively modern one, a program started by an interactive shell will have getpid() == getpgrp(), and any additional processes forked by that process (say, if it's a shell script or if it calls system()) will have getpid() != getpgrp().

Here's a test program, and its behavior under bash (it will behave the same under ksh93 and tcsh as well):

pp.c

#include <unistd.h>
#include <stdio.h>

main()
{
    printf("pid=%d pgrp=%d\n", (int)getpid(), (int)getpgrp());
}


$ ./pp
pid=3164 pgrp=3164
$ ./pp &
[1] 3165
$ pid=3165 pgrp=3165

In a pipeline, the leftmost command is the process group leader. (This isn't documented, but bash, ksh93, and tcsh all do it this way).

$ ls|./pp
pid=3179 pgrp=3178
$ ./pp|cat
pid=3180 pgrp=3180

A program called with system() will have the same pgrp as its parent:

pps.c

#include <stdlib.h>

main()
{
    system("./pp");
}


$ ./pps
pid=4610 pgrp=4608

In a shell script, the shell is the process group leader, and any command invoked by it will inherit the pgrp:

pp.sh

#!/bin/sh
./pp


$ ./pp.sh
pid=4501 pgrp=4500

But if the shell script execs a program, the pid doesn't change, and the execed program will be a process group leader, so you probably don't want to do that.

ppe.sh

#!/bin/sh
exec ./pp


$ ./ppe.sh
pid=4504 pgrp=4504

In the unlikely case that the user turns off job control, every command is going to have the same pgrp as the shell:

$ set +m
$ ./pp
pid=4521 pgrp=2990
$ ./pp
pid=4522 pgrp=2990
like image 100
Mark Plotnick Avatar answered Nov 14 '22 23:11

Mark Plotnick