5

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.

16
  • check the terminal status? If you run from the cli, you get a full terminal. if you run under exec() or other methods, you don't get an interactive terminal. Commented Jun 20, 2014 at 16:48
  • @MarcB - Thank you for the response. Can you elaborate please? stty() - if this is what you think of - is not implemented in my Linux. Commented Jun 20, 2014 at 16:56
  • 1
    isatty(1) tells you the output goes to a terminal; isatty(0) would tell you about input. And system() can be used with output going to terminal and you can run it from the command line with input coming from files and output going to files. What are you really needing to know? There's an element of an XY Problem here. What is the problem you are trying to solve by determining whether the program was run via system()? As you've found, system is usually implemented so that it runs a shell, perhaps Bash, with -c and 'what you provided as an argument to system()'. Commented Jun 20, 2014 at 17:12
  • 3
    The problem is that these different contexts (command line/script/system()) are very similar to each other (intentionally), so that any means of distinguishing them would be a definite hack (finding out if there's an attached terminal might indeed help, but it's a dubious heuristic). Can you say why you want to know this? There might be a clear solution to that problem. Commented Jun 20, 2014 at 17:15
  • 1
    @Grzegorz but if usage is allowed from a script, then all a command-line user has to do is write a script #! /bin/sh -\nyour-magic-command "$@" and suddenly they're allowed. Perhaps check for a special ENABLE_MAGIC_COMMAND=yes environment variable, or a lock file which only the system()-calling program can write. If the special arguments are, say, passwords, then an environment variable or named pipe might be how to pass them to a system() child in a way that a command-line would find difficult – these wouldn't find their way to the history. Commented Jun 20, 2014 at 20:03

3 Answers 3

1

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
Sign up to request clarification or add additional context in comments.

1 Comment

I'm sorry to come back to you so late. I have completely missed that question and your answer, and I don't remember how did that happen. I have read your answer and I believe - despite the fact that I won't be using it now, that your thorough analysis answers my question. Therefore I am upping it and marking as the answer. Thanks a lot, Mark.
0

You can intercept signal from PID when the script is done and check "kill" for it.

Comments

0

Not sure if that solves your problem but environment variables can give you a good hint. For example:

 $ set | grep "^_="
 _=
 $ bash -c "set" | grep "^_="
 _=/bin/bash
 $ sh -c "set" | grep "^_="
 _='/bin/sh'

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.