1

I tried to make the title succinct, but couldn't think of a way to totally encapsulate the question. Here's what I want to do: when running a perl script, I want to know if there are any other processes other than 'myself' that have a connection to the same STDERR stream (in the current tty or potentially appended/redirected to the same STDERR file) and if so, my goal is to be able to tell the difference between my STDERR errors/warnings/verbose messages and any others from other concurrently running programs. When I detect this as a possibility, I want to prepend my script name to all messages I print to STDERR so that the user knows which process the message came from (or at least which ones were from a script I wrote). At the moment, I don't care how I accomplish this or whether a solution encapsulates all possibilities - for now let's call it a proof of concept that captures a reasonable number of use-cases for a decent default behavior. And I am attempting to do this via system calls.

There are 2 scenarios that I wish to account for: messages from sibling processes run concurrently in a piped series of commands and messages from other child processes run serially by the same parent script. I'm not going to try to handle the case of background processes with a handle on the same STDERR stream, but if those are captured, that would be a bonus.

I believe I have a pretty good handle on the first scenario: sibling processes. I can get all the PIDs of any sibling processes using pgrep -P $ppid. The simple existence of sibling processes is enough to warrant the script-name prepend to my messages, but I could even go a step further and use lsof to find out if they have their stderr going to the same STDERR stream as myself - if for example they are running in the background and have their STDERR going to the same TTY (inferred by file descriptor '2'). I could even check if I'm a part of a series of piped commands with perl's -p or -t functions (though I do not want to prepend in the event that a file has been redirected in or out using '<' or '>', so -p or -t alone won't work). Regardless, I am satisfied with how to handle concurrently running sibling processes.

The scenario that I haven't sufficiently figured out is serially run processes controlled by a parent script. My idea is to try and make a reasonable guess as to whether my parent process is an interactive shell or whether it's a script.

I've tried reading up on lsof to figure out if there was a way to do this. I was sort of guessing that any errors from an interactive shell/terminal would not be printed to the same STDERR stream as my script, but apparently they do, so I can't simply look for whether any output is going to my tty from the parent to distinguish the 2 scenarios.

I created a one-liner to use for testing in different contexts:

perl -e '$ppid=getppid();print("lsof -p $ppid:\n",`lsof -p $ppid`)'

I ran it in 2 contexts: one directly on the command line and the other from inside a shell script. The only difference I could see in the lsof run on the parent's PID was that when run on the command line, there were 5 read/write connections to the tty and when run from in the shell script, there were 3 connections to the tty and one read-only read of the shell script. None of the file descriptors in any case were 0, 1, or 2.

Interactive shell:

...
tcsh    87854 me   15u   CHR   16,2    0t3625    13083 /dev/ttys002
tcsh    87854 me   16u   CHR   16,2    0t3625    13083 /dev/ttys002
tcsh    87854 me   17u   CHR   16,2    0t3625    13083 /dev/ttys002
tcsh    87854 me   18u   CHR   16,2    0t3625    13083 /dev/ttys002
tcsh    87854 me   19u   CHR   16,2    0t3625    13083 /dev/ttys002

Inside shell script:

tcsh    89384 me   16r   REG    1,3       363 19758500 /whatever/tmpdelete2.tcsh
tcsh    89384 me   17u   CHR   16,2    0t4721    13083 /dev/ttys002
tcsh    89384 me   18u   CHR   16,2    0t4721    13083 /dev/ttys002
tcsh    89384 me   19u   CHR   16,2    0t4721    13083 /dev/ttys002

So my question is: is there enough information here to reasonably infer whether or not the parent process is an interactive shell or not?

Could I reasonably infer from the parent process's REGular read-only read from a file that the parent process is not an interactive terminal session? Or could I infer from the presence of 5 read/write connections to the tty that the parent is an interactive shell?

To slightly test this idea, I tried putting a sleep 1 on the line above my one-liner to see if the read of the script would go away from the lsof output at some point during a long script execution. I also tried redirecting into and out from the shell script when I ran it.

In my use case, I can tell the difference, but I'm not sure about how other shells behave. I'm really only concerned about shell scripts. I can't think of any reasonable scenario where someone calls a perl script from inside anything else...

hepcat72
  • 890
  • 4
  • 22
  • 3
    Looking at stderr of other processes isn't enough. They could have redirected something else to the same tty also. And people routinely do *all sorts* of unreasonable things. I think this work isn't likely to be generally possible and is probably not a good idea. – Etan Reisner Nov 18 '14 at 19:56
  • What do you mean by "something else"? If you mean background processes that don't belong to the parent, then I'll either see them via pgrep as siblings & prepend my script, or I won't see them because I'm running in a script, in which case I'll prepend anyway- so it wouldn't matter. Not sure what else there could be- unless maybe I'm in a subshell- that could be an issue, but this feature isn't really critical- as long as it performs reasonably well, I'd be satisfied. So barring being in a subshell, what else could go wrong? (serious question - not a blissfully ignorant rhetorical one). ;) – hepcat72 Nov 18 '14 at 23:16
  • What I meant was that they could have opened any other fd to the tty. You cannot assume that only stderr will be going there. Not to mention that any other process that has access to your tty could write to it randomly (though obviously you can't expect to handle that). – Etan Reisner Nov 19 '14 at 00:04
  • Yes, there can be non-stderr outputs to the TTY, but I'm only concerned w/ errors, which can be readily distinguish from other output already. If a process is sending errors elsewhere, I'm not going to worry about it (it's their questionable design decision, i.e. their problem). Besides, it's only a concern with the 1st scenario using pgrep, which is for catching concurrent processes in an interactive shell, so nothing should pop up that wasn't already there when a pipe series is executed. So it sounds like you're suggesting I consider other tty's & not necessarily saying it won't work, right? – hepcat72 Nov 19 '14 at 15:02
  • Your process should always prefix its error messages with the program name. Errors that don't identify where they came from are a nuisance. – Jonathan Leffler Jun 01 '20 at 17:06
  • @Jonathan Leffler - I don't disagree, but I just don't want to make that decision for whoever decides they want to use the module I'm developing. Others have different aesthetics when it comes to STDERR output. E.g., I hate seeing messy traces in errors. I just want to know what the error is. If I want a trace, I'll run in debug mode. – hepcat72 Jun 01 '20 at 17:10
  • Maybe your module shouldn't be writing to standard error at all; it should report the error to the calling code and leave the reporting to the calling code. – Jonathan Leffler Jun 01 '20 at 17:12
  • Another valid suggestion. If I was writing in an object oriented language that had exceptions with try/throw/catch mechanisms, I'd have done it that way. But we're getting off track here. I'm asking a how question, not a should question. – hepcat72 Jun 01 '20 at 17:15
  • Besides, I'm not talking about my own errors - I'm talking about users of my module using my `error()` method, which is connected to default command line options they can either opt to use or not. And it comes with a number of features, including this one, all controlled via automatically generated command line flags (again, which they can choose to use or roll their own). – hepcat72 Jun 01 '20 at 17:23
  • Those options (which affect the behavior of `error()`) that are default-added to the interface simply by using my module are --error-limit, --debug, --pipeline (the option in question), --quiet, --logfile, and --logfile-suffix (unless I'm forgetting any). The module provides an instant fully fledged and highly configurable command line interface to the simplest of scripts just by declaring the module. It allows any script the benefit of a full and easy-to-use command line interface. It's not public yet though. – hepcat72 Jun 01 '20 at 17:29

0 Answers0