PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/inc/Proc/Simple.pm

https://github.com/ytnobody/Zacro
Perl | 496 lines | 218 code | 102 blank | 176 comment | 36 complexity | d519d188b218e9114f2a89288d92ac20 MD5 | raw file
  1. #line 1
  2. ######################################################################
  3. package Proc::Simple;
  4. ######################################################################
  5. # Copyright 1996-2001 by Michael Schilli, all rights reserved.
  6. #
  7. # This program is free software, you can redistribute it and/or
  8. # modify it under the same terms as Perl itself.
  9. #
  10. # The newest version of this module is available on
  11. # http://perlmeister.com/devel
  12. # or on your favourite CPAN site under
  13. # CPAN/modules/by-author/id/MSCHILLI
  14. #
  15. ######################################################################
  16. #line 108
  17. require 5.003;
  18. use strict;
  19. use vars qw($VERSION %EXIT_STATUS %INTERVAL
  20. %DESTROYED);
  21. use POSIX;
  22. use IO::Handle;
  23. $VERSION = '1.31';
  24. ######################################################################
  25. # Globals: Debug and the mysterious waitpid nohang constant.
  26. ######################################################################
  27. my $Debug = 0;
  28. my $WNOHANG = get_system_nohang();
  29. ######################################################################
  30. #line 146
  31. ######################################################################
  32. # $proc_obj=Proc::Simple->new(); - Constructor
  33. ######################################################################
  34. sub new {
  35. my $proto = shift;
  36. my $class = ref($proto) || $proto;
  37. my $self = {};
  38. # Init instance variables
  39. $self->{'kill_on_destroy'} = undef;
  40. $self->{'signal_on_destroy'} = undef;
  41. $self->{'pid'} = undef;
  42. $self->{'redirect_stdout'} = undef;
  43. $self->{'redirect_stderr'} = undef;
  44. bless($self, $class);
  45. }
  46. ######################################################################
  47. #line 226
  48. ######################################################################
  49. # $ret = $proc_obj->start("prg"); - Launch process
  50. ######################################################################
  51. sub start {
  52. my $self = shift;
  53. my ($func, @params) = @_;
  54. # Reap Zombies automatically
  55. $SIG{'CHLD'} = \&THE_REAPER;
  56. # Fork a child process
  57. $self->{'pid'} = fork();
  58. return 0 unless defined $self->{'pid'}; # return Error if fork failed
  59. if($self->{pid} == 0) { # Child
  60. # Mark it as process group leader, so that we can kill
  61. # the process group later. Note that there's a race condition
  62. # here because there's a window in time (while you're reading
  63. # this comment) between child startup and its new process group
  64. # id being defined. This means that killpg() to the child during
  65. # this time frame will fail. Proc::Simple's kill() method deals l
  66. # with it, see comments there.
  67. POSIX::setsid();
  68. $self->dprt("setsid called ($$)");
  69. if (defined $self->{'redirect_stderr'}) {
  70. $self->dprt("STDERR -> $self->{'redirect_stderr'}");
  71. open(STDERR, ">$self->{'redirect_stderr'}") ;
  72. autoflush STDERR 1 ;
  73. }
  74. if (defined $self->{'redirect_stdout'}) {
  75. $self->dprt("STDOUT -> $self->{'redirect_stdout'}");
  76. open(STDOUT, ">$self->{'redirect_stdout'}") ;
  77. autoflush STDOUT 1 ;
  78. }
  79. if(ref($func) eq "CODE") {
  80. $self->dprt("Launching code");
  81. $func->(@params); exit 0; # Start perl subroutine
  82. } else {
  83. $self->dprt("Launching $func @params");
  84. exec $func, @params; # Start shell process
  85. exit 0; # In case something goes wrong
  86. }
  87. } elsif($self->{'pid'} > 0) { # Parent:
  88. $INTERVAL{$self->{'pid'}}{'t0'} = time();
  89. $self->dprt("START($self->{'pid'})");
  90. # Register PID
  91. $EXIT_STATUS{$self->{'pid'}} = undef;
  92. $INTERVAL{$self->{'pid'}}{'t1'} = undef;
  93. return 1; # return OK
  94. } else {
  95. return 0; # this shouldn't occur
  96. }
  97. }
  98. ######################################################################
  99. #line 295
  100. ######################################################################
  101. # $ret = $proc_obj->poll(); - Check process status
  102. # 1="running" 0="not running"
  103. ######################################################################
  104. sub poll {
  105. my $self = shift;
  106. $self->dprt("Polling");
  107. # There's some weirdness going on with the signal handler.
  108. # It runs into timing problems, so let's have poll() call
  109. # the REAPER every time to make sure we're getting rid of
  110. # defuncts.
  111. $self->THE_REAPER();
  112. if(defined($self->{pid})) {
  113. if(CORE::kill(0, $self->{pid})) {
  114. $self->dprt("POLL($self->{pid}) RESPONDING");
  115. return 1;
  116. } else {
  117. $self->dprt("POLL($self->{pid}) NOT RESPONDING");
  118. }
  119. } else {
  120. $self->dprt("POLL(NOT DEFINED)");
  121. }
  122. 0;
  123. }
  124. ######################################################################
  125. #line 342
  126. ######################################################################
  127. # $ret = $proc_obj->kill([SIGXXX]); - Send signal to process
  128. # Default-Signal: SIGTERM
  129. ######################################################################
  130. sub kill {
  131. my $self = shift;
  132. my $sig = shift;
  133. # If no signal specified => SIGTERM-Signal
  134. $sig = POSIX::SIGTERM() unless defined $sig;
  135. # Use numeric signal if we get a string
  136. if( $sig !~ /^[-\d]+$/ ) {
  137. $sig =~ s/^SIG//g;
  138. $sig = eval "POSIX::SIG${sig}()";
  139. }
  140. # Process initialized at all?
  141. if( !defined $self->{'pid'} ) {
  142. $self->dprt("No pid set");
  143. return 0;
  144. }
  145. # Send signal
  146. if(CORE::kill($sig, $self->{'pid'})) {
  147. $self->dprt("KILL($sig, $self->{'pid'}) OK");
  148. # now kill process group of process to make sure that shell
  149. # processes containing shell characters, which get launched via
  150. # "sh -c" are killed along with their launching shells.
  151. # This might fail because of the race condition explained in
  152. # start(), so we ignore the outcome.
  153. CORE::kill(-$sig, $self->{'pid'});
  154. } else {
  155. $self->dprt("KILL($sig, $self->{'pid'}) failed ($!)");
  156. return 0;
  157. }
  158. 1;
  159. }
  160. ######################################################################
  161. #line 398
  162. ######################################################################
  163. # Method to set the kill_on_destroy flag
  164. ######################################################################
  165. sub kill_on_destroy {
  166. my $self = shift;
  167. if (@_) { $self->{kill_on_destroy} = shift; }
  168. return $self->{kill_on_destroy};
  169. }
  170. ######################################################################
  171. #line 420
  172. ######################################################################
  173. # Send a signal on destroy
  174. # undef means send the default signal (SIGTERM)
  175. ######################################################################
  176. sub signal_on_destroy {
  177. my $self = shift;
  178. if (@_) { $self->{signal_on_destroy} = shift; }
  179. return $self->{signal_on_destroy};
  180. }
  181. ######################################################################
  182. #line 450
  183. ######################################################################
  184. sub redirect_output {
  185. ######################################################################
  186. my $self = shift ;
  187. ($self->{'redirect_stdout'}, $self->{'redirect_stderr'}) = @_ ;
  188. 1 ;
  189. }
  190. ######################################################################
  191. #line 471
  192. ######################################################################
  193. sub pid {
  194. ######################################################################
  195. my $self = shift;
  196. # Allow the pid to be set - assume this is only
  197. # done internally so don't document this behaviour in the
  198. # pod.
  199. if (@_) { $self->{'pid'} = shift; }
  200. return $self->{'pid'};
  201. }
  202. ######################################################################
  203. #line 494
  204. ######################################################################
  205. sub t0 {
  206. ######################################################################
  207. my $self = shift;
  208. return $INTERVAL{$self->{'pid'}}{'t0'};
  209. }
  210. ######################################################################
  211. #line 513
  212. ######################################################################
  213. sub t1 {
  214. ######################################################################
  215. my $self = shift;
  216. return $INTERVAL{$self->{'pid'}}{'t1'};
  217. }
  218. #line 531
  219. ######################################################################
  220. # Destroy method
  221. # This is run automatically on undef
  222. # Should probably not bother if a poll shows that the process is not
  223. # running.
  224. ######################################################################
  225. sub DESTROY {
  226. my $self = shift;
  227. # Localize special variables so that the exit status from waitpid
  228. # doesn't leak out, causing exit status to be incorrect.
  229. local( $., $@, $!, $^E, $? );
  230. # Processes never started don't have to be cleaned up in
  231. # any special way.
  232. return unless $self->pid();
  233. # If the kill_on_destroy flag is true then
  234. # We need to send a signal to the process
  235. if ($self->kill_on_destroy) {
  236. $self->dprt("Kill on DESTROY");
  237. if (defined $self->signal_on_destroy) {
  238. $self->kill($self->signal_on_destroy);
  239. } else {
  240. $self->dprt("Sending KILL");
  241. $self->kill;
  242. }
  243. }
  244. delete $EXIT_STATUS{ $self->pid };
  245. if( $self->poll() ) {
  246. $DESTROYED{ $self->pid } = 1;
  247. }
  248. }
  249. ######################################################################
  250. #line 574
  251. ######################################################################
  252. # returns the exit status of the child process, undef if the child
  253. # hasn't yet exited
  254. ######################################################################
  255. sub exit_status{
  256. my( $self ) = @_;
  257. return $EXIT_STATUS{ $self->pid };
  258. }
  259. ######################################################################
  260. #line 595
  261. ######################################################################
  262. # waits until the child process terminates and then
  263. # returns the exit status of the child process.
  264. ######################################################################
  265. sub wait {
  266. my $self = shift;
  267. local $SIG{CHLD}; # disable until we're done
  268. my $pid = $self->pid();
  269. # test if the signal handler reap'd this pid some time earlier or even just
  270. # a split second before localizing $SIG{CHLD} above; also kickout if
  271. # they've wait'd or waitpid'd on this pid before ...
  272. return $EXIT_STATUS{$pid} if defined $EXIT_STATUS{$pid};
  273. # all systems support FLAGS==0 (accg to: perldoc -f waitpid)
  274. my $res = waitpid $pid, 0;
  275. my $rc = $?;
  276. $INTERVAL{$pid}{'t1'} = time();
  277. $EXIT_STATUS{$pid} = $rc;
  278. dprt("", "For $pid, reaped '$res' with exit_status=$rc");
  279. return $rc;
  280. }
  281. ######################################################################
  282. # Reaps processes, uses the magic WNOHANG constant
  283. ######################################################################
  284. sub THE_REAPER {
  285. # Localize special variables so that the exit status from waitpid
  286. # doesn't leak out, causing exit status to be incorrect.
  287. local( $., $@, $!, $^E, $? );
  288. my $child;
  289. my $now = time();
  290. if(defined $WNOHANG) {
  291. # Try to reap every process we've ever started and
  292. # whichs Proc::Simple object hasn't been destroyed.
  293. #
  294. # This is getting really ugly. But if we just call the REAPER
  295. # for every SIG{CHLD} event, code like this will fail:
  296. #
  297. # use Proc::Simple;
  298. # $proc = Proc::Simple->new(); $proc->start(\&func); sleep(5);
  299. # sub func { open(PIPE, "/bin/ls |"); @a = <PIPE>; sleep(1);
  300. # close(PIPE) or die "PIPE failed"; }
  301. #
  302. # Reason: close() doesn't like it if the spawn has
  303. # been reaped already. Oh well.
  304. #
  305. # First, check if we can reap the processes which
  306. # went out of business because their kill_on_destroy
  307. # flag was set and their objects were destroyed.
  308. foreach my $pid (keys %DESTROYED) {
  309. if(my $res = waitpid($pid, $WNOHANG) > 0) {
  310. # We reaped a zombie
  311. delete $DESTROYED{$pid};
  312. dprt("", "Reaped: $pid");
  313. }
  314. }
  315. foreach my $pid (keys %EXIT_STATUS) {
  316. dprt("", "Trying to reap $pid");
  317. if( defined $EXIT_STATUS{$pid} ) {
  318. dprt("", "exit status of $pid is defined - not reaping");
  319. next;
  320. }
  321. if(my $res = waitpid($pid, $WNOHANG) > 0) {
  322. # We reaped a truly running process
  323. $EXIT_STATUS{$pid} = $?;
  324. $INTERVAL{$pid}{'t1'} = $now;
  325. dprt("", "Reaped: $pid");
  326. } else {
  327. dprt("", "waitpid returned '$res'");
  328. }
  329. }
  330. } else {
  331. # If we don't have $WNOHANG, we don't have a choice anyway.
  332. # Just reap everything.
  333. dprt("", "reap everything for lack of WNOHANG");
  334. $child = CORE::wait();
  335. $EXIT_STATUS{$child} = $?;
  336. $INTERVAL{$child}{'t1'} = $now;
  337. }
  338. # Don't reset signal handler for crappy sysV systems. Screw them.
  339. # This caused problems with Irix 6.2
  340. # $SIG{'CHLD'} = \&THE_REAPER;
  341. }
  342. ######################################################################
  343. #line 700
  344. # Proc::Simple::debug($level) - Turn debug on/off
  345. sub debug { $Debug = shift; }
  346. ######################################################################
  347. #line 715
  348. sub cleanup {
  349. for my $pid ( keys %INTERVAL ) {
  350. if( !exists $DESTROYED{ $pid } ) {
  351. # process has been reaped already, safe to delete
  352. # its start/stop time
  353. delete $INTERVAL{ $pid };
  354. }
  355. }
  356. }
  357. ######################################################################
  358. # Internal debug print function
  359. ######################################################################
  360. sub dprt {
  361. my $self = shift;
  362. if($Debug) {
  363. require Time::HiRes;
  364. my ($seconds, $microseconds) = Time::HiRes::gettimeofday();
  365. print "[$seconds.$microseconds] ", ref($self), "> @_\n";
  366. }
  367. }
  368. ######################################################################
  369. sub get_system_nohang {
  370. ######################################################################
  371. # This is for getting the WNOHANG constant of the system -- but since
  372. # the waitpid(-1, &WNOHANG) isn't supported on all Unix systems, and
  373. # we still want Proc::Simple to run on every system, we have to
  374. # quietly perform some tests to figure out if -- or if not.
  375. # The function returns the constant, or undef if it's not available.
  376. ######################################################################
  377. my $nohang;
  378. open(SAVEERR, ">&STDERR");
  379. # If the system doesn't even know /dev/null, forget about it.
  380. open(STDERR, ">/dev/null") || return undef;
  381. # Close stderr, since some weirdo POSIX modules write nasty
  382. # error messages
  383. close(STDERR);
  384. # Check for the constant
  385. eval 'use POSIX ":sys_wait_h"; $nohang = &WNOHANG;';
  386. # Re-open STDERR
  387. open(STDERR, ">&SAVEERR");
  388. close(SAVEERR);
  389. # If there was an error, return undef
  390. return undef if $@;
  391. return $nohang;
  392. }
  393. 1;
  394. __END__