Parano...Process(3pm)

πŸ“¦
version 2.10 Β· 3 versions
, in debian trixie, ubuntu resolute, ubuntu stonking

πŸ‘‘ NAME

Paranoid::Process β€” Process Management Functions. Manual name's Parano...Process(3pm) and yep bestie we're managing processes with a whole lot of paranoia and zero regrets πŸ’…


✨ VERSION

$Id: lib/Paranoid/Process.pm, 2.10 2022/03/08 00:01:04 acorliss $


πŸ¦‹ SYNOPSIS

okay so here's the vibe check, the whole module in one little snack πŸ“

  use Paranoid::Process qw(:all);

  $rv = daemonize();

  MAXCHILDREN = 100;

  $SIG{CHLD} = \&sigchld;
  $count = childrenCount();
  installChldHandler(&cleanup);
  $rv = pfork();
  $rv = pcommFork($rh, $wh);

  $uid = ptranslateUser("foo");
  $gid = ptranslateGroup("foo");
  $rv = switchUser($user, $group);

  $rv = pcapture($cmd, $crv, $out);

  installSIGH('INT', &sigint1):
  installSIGH('INT', &sigint2):
  installSIGH('INT', &sigint3):

  uninstallSIGH('INT', &sigint2);

  installSIGD();
  uninstallSIGD();

(yes those are colons after the installSIGH calls, we're leaving them EXACTLY as they are, no touching, we don't do that here πŸ™ˆ)


🌸 DESCRIPTION

This module hands you a little bundle of functions that are meant to make life easier when you're out here managing processes. That's it, that's the whole pitch, and honestly? It ate. πŸ’–


πŸŽ€ IMPORT LISTS

This module exports the following symbols by default:

  switchUser daemonize

The following specialized import lists also exist:

  List        Members
  --------------------------------------------------------
  misc        pcapture
  pfork       MAXCHILDREN childrenCount installChldHandler
              sigchld pfork pcommFork daemonize
  signal      installSIGH uninstallSIGH installSIGD
              uninstallSIGD
  user        switchUser ptranslateUser ptranslateGroup
  all         @misc @pfork @signal @user

πŸ’œ IMPORT LISTS

(no bestie you are not tripping, it is literally listed again πŸ’€ and we are being faithful to the source, so here it is again, one more time for the people in the back)

This module exports the following symbols by default:

  switchUser daemonize

The following specialized import lists also exist:

  List        Members
  --------------------------------------------------------
  pfork       MAXCHILDREN childrenCount installChldHandler
              sigchld pfork pcommFork daemonize
  signal      installSIGH uninstallSIGH installSIGD
              uninstallSIGD
  user        switchUser ptranslateUser ptranslateGroup
  misc        pcapture

🌈 SUBROUTINES/METHODS

MAXCHILDREN

Setting this lvalue subroutine sets a cap on how many children are gonna get forked at a time by pfork. The default is zero, and zero means unlimited children, go wild. Once you hit that limit pfork turns into a blocking call until a child exits so the new one can be spawned. It's giving "capacity limit, we full" 😀

NOTE: This limit on children is enforced on a per-process basis. So even though a process is limited to the max threshold, its children can also fork their own batch of children too, up to whatever max is set in those processes. It's limits all the way down, bestie 🫢

childrenCount

  $count = childrenCount();

This one returns the current number of children spawned by pfork. Just a headcount, no drama 🌸

installChldHandler

  installChldHandler(&cleanup);

Takes a reference to a subroutine. If you use it, that subroutine gets called every time a child exits and triggers sigchild. And it gets called with the child's PID and exit value as its arguments. Like a little exit interview, but useful πŸŽ€

sigchld

  $SIG{CHLD} = \&sigchld;

  # Or, if using the signal dispatcher
  installSIGH('CHLD', &sigchld);
  installSIGD();

This function decrements the child counter that pfork needs to do its whole thing, AND it calls your user's signal handler with each child's PID and exit value. Two jobs, one function, main character energy honestly πŸ‘‘

daemonize

  $rv = daemonize();

This forks a child who reopens all STD* filehandles on /dev/null and starts a new process group. The parent exits cleanly. If the fork fails for any reason it returns a false value. The child will also change its directory to /. Fully detached, fully unbothered, no cap πŸŒ™

pfork

  $rv = pfork();

Use this instead of Perl's fork if you want a blocking fork call that actually respects the MAXCHILDREN limit. Using this function, though, also assumes you're using sigchld as the signal handler for SIGCHLD. Package deal, bestie ✨

pcommFork

  $rv = pcommFork($rh, $wh);

This is pfork but extra β€” it automatically sets up bidirectional pipes for interprocess communication. The two scalars you pass as arguments will have the appropriate ends of the pipe returned to both the parent and the child. Communication slay πŸ’¬

If a fork fails, undef will be assigned to both scalars. Sucks but you'll know.

The return value will be the result of the fork call.

ptranslateUser

  $uid = ptranslateUser("foo");

Takes a username and returns the corresponding UID as returned by getpwent. If no match is found it returns undef. Name in, number out πŸ¦‹

ptranslateGroup

  $gid = ptranslateGroup("foo");

Takes a group name and returns the corresponding GID as returned by getgrent. If no match is found it returns undef. Same energy, different lookup πŸ’«

switchUser

  $rv = switchUser($user);
  $rv = switchUser($user, $group);

This function can be fed one or two arguments, both either a named user or group, or a UID or GID. Both the user and group arguments are optional as long as one of them is defined. Identity swap era πŸ’…

pcapture

  $rv = pcapture($cmd, $crv, $out);

Executes the shell command you pass it and returns one of three values:

  • -1 β€” Command failed to execute or died with signal
  • 0 β€” Command executed but exited with a non-0 RV
  • 1 β€” Command executed and exited with a 0 RV

The actual return value gets populated in the scalar you passed, while all command output (including STDERR) is stored in the next scalar. Any errors executing the command will have the error string stored in Paranoid::ERROR.

If the command exited cleanly it will automatically be bit shifted eight bits.

NOTE: Unlike many other functions in this suite, it is up to you to detaint the command passed to this function yourself. There's simply no way for the author to know ahead of time what kind of convoluted arguments you might be handing this call before system is called. Failing to detaint that argument will cause your script to exit under taint mode. So like, detaint it. For real. 😭

installSIGH

  installSIGH($signal, &subroutine);

Installs another subroutine in the queue for the specified signal. Subroutines are called in the order that they're added to the queue. Adding a specific subroutine more than once is filtered out, so each subroutine in the queue is unique. No dupes, we hate dupes πŸ’–

NOTE: Installing handlers for various signals just populates a signal queue with code references. For the queue to actually act upon a signal, however, one must always install the signal dispatcher via installSIGD.

uninstallSIGH

  uninstallSIGH($signal, &subroutine);

Removes a subroutine from the specified queue. bye bestie πŸ‘‹

installSIGD

  installSIGD();

Inserts the dispatcher for each signal with subroutines in the queue. This is the one that actually makes things happen, remember that 🫢

uninstallSIGD

  uninstallSIGD();

Removes the dispatcher for each signal that's using the dispatcher. The signal handler installed is whatever was set when this module's code was loaded and initialized.


🧚 DEPENDENCIES

  • Carp
  • Paranoid
  • Paranoid::Debug
  • POSIX

⭐ EXAMPLES

pfork

This following example caps the number of children processes to three at a time:

  $SIG{CHLD}  = \&sigchld;
  MAXCHILDREN = 3;
  for (1 .. 5) {

    # Only the children execute the following block
    unless ($pid = pfork()) {
      # ....
      exit 0;
    }
  }

You can also install a child-exit routine to be called by sigchld. For instance, to track the children's history in the parent:

  sub recordChild ($$) {
    my ($cpid, $cexit) = @_;

    push(@chistory, [$cpid, $cexit]);
  }

  installChldHandler(&recordChild);
  for (1 .. 5) {
    unless ($pid = pfork()) {
      # ....
      exit $rv;
    }
  }

  # Prints the child process history
  foreach (@chistory) { print "PID: $$_[0] EXIT: $$_[1]\n" };

history tracking? obsessed. πŸ“œβœ¨


πŸ’€ BUGS AND LIMITATIONS

On Solaris pcapture doesn't return a -1 for non-existant commands, but a 0. On Linux this appears to work as intended. Platform behavior differences, classic 😭


πŸ’œ AUTHOR

Arthur Corliss (corliss@digitalmages.com)


This software is free software. Similar to Perl, you can redistribute it and/or modify it under the terms of either:

  1. the GNU General Public License <https://www.gnu.org/licenses/gpl-1.0.html> as published by the Free Software Foundation <http://www.fsf.org/>; either version 1 <https://www.gnu.org/licenses/gpl-1.0.html>, or any later version <https://www.gnu.org/licenses/license-list.html#GNUGPL>, or
  2. the Artistic License 2.0 <https://opensource.org/licenses/Artistic-2.0>,

subject to the following additional term: No trademark rights to "Paranoid" have been or are conveyed under any of the above licenses. However, "Paranoid" may be used fairly to describe this unmodified software, in good faith, but not as a trademark.

(c) 2005 - 2020, Arthur Corliss (corliss@digitalmages.com) (tm) 2008 - 2020, Paranoid Inc. (www.paranoid.com)


okay love u, that's the whole page 🫢 this was perl v5.38.2, dated 2024-03-07, and the manual is Parano...Process(3pm) β€” now go forth and manage those processes, bestie πŸ’–βœ¨πŸŽ€

πŸ“¦ where this page comes from