1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """Utility functions for processes.
22
23 """
24
25
26 import os
27 import sys
28 import subprocess
29 import errno
30 import select
31 import logging
32 import signal
33 import resource
34
35 from cStringIO import StringIO
36
37 from ganeti import errors
38 from ganeti import constants
39 from ganeti import compat
40
41 from ganeti.utils import retry as utils_retry
42 from ganeti.utils import wrapper as utils_wrapper
43 from ganeti.utils import text as utils_text
44 from ganeti.utils import io as utils_io
45 from ganeti.utils import algo as utils_algo
46
47
48
49 _no_fork = False
50
51 (_TIMEOUT_NONE,
52 _TIMEOUT_TERM,
53 _TIMEOUT_KILL) = range(3)
54
55
57 """Disables the use of fork(2).
58
59 """
60 global _no_fork
61
62 _no_fork = True
63
64
66 """Holds the result of running external programs.
67
68 @type exit_code: int
69 @ivar exit_code: the exit code of the program, or None (if the program
70 didn't exit())
71 @type signal: int or None
72 @ivar signal: the signal that caused the program to finish, or None
73 (if the program wasn't terminated by a signal)
74 @type stdout: str
75 @ivar stdout: the standard output of the program
76 @type stderr: str
77 @ivar stderr: the standard error of the program
78 @type failed: boolean
79 @ivar failed: True in case the program was
80 terminated by a signal or exited with a non-zero exit code
81 @ivar fail_reason: a string detailing the termination reason
82
83 """
84 __slots__ = ["exit_code", "signal", "stdout", "stderr",
85 "failed", "fail_reason", "cmd"]
86
87 - def __init__(self, exit_code, signal_, stdout, stderr, cmd, timeout_action,
88 timeout):
89 self.cmd = cmd
90 self.exit_code = exit_code
91 self.signal = signal_
92 self.stdout = stdout
93 self.stderr = stderr
94 self.failed = (signal_ is not None or exit_code != 0)
95
96 fail_msgs = []
97 if self.signal is not None:
98 fail_msgs.append("terminated by signal %s" % self.signal)
99 elif self.exit_code is not None:
100 fail_msgs.append("exited with exit code %s" % self.exit_code)
101 else:
102 fail_msgs.append("unable to determine termination reason")
103
104 if timeout_action == _TIMEOUT_TERM:
105 fail_msgs.append("terminated after timeout of %.2f seconds" % timeout)
106 elif timeout_action == _TIMEOUT_KILL:
107 fail_msgs.append(("force termination after timeout of %.2f seconds"
108 " and linger for another %.2f seconds") %
109 (timeout, constants.CHILD_LINGER_TIMEOUT))
110
111 if fail_msgs and self.failed:
112 self.fail_reason = utils_text.CommaJoin(fail_msgs)
113 else:
114 self.fail_reason = None
115
116 if self.failed:
117 logging.debug("Command '%s' failed (%s); output: %s",
118 self.cmd, self.fail_reason, self.output)
119
121 """Returns the combined stdout and stderr for easier usage.
122
123 """
124 return self.stdout + self.stderr
125
126 output = property(_GetOutput, None, None, "Return full output")
127
128
130 """Builds the environment for an external program.
131
132 """
133 if reset:
134 cmd_env = {}
135 else:
136 cmd_env = os.environ.copy()
137 cmd_env["LC_ALL"] = "C"
138
139 if env is not None:
140 cmd_env.update(env)
141
142 return cmd_env
143
144
145 -def RunCmd(cmd, env=None, output=None, cwd="/", reset_env=False,
146 interactive=False, timeout=None, noclose_fds=None,
147 input_fd=None, postfork_fn=None):
148 """Execute a (shell) command.
149
150 The command should not read from its standard input, as it will be
151 closed.
152
153 @type cmd: string or list
154 @param cmd: Command to run
155 @type env: dict
156 @param env: Additional environment variables
157 @type output: str
158 @param output: if desired, the output of the command can be
159 saved in a file instead of the RunResult instance; this
160 parameter denotes the file name (if not None)
161 @type cwd: string
162 @param cwd: if specified, will be used as the working
163 directory for the command; the default will be /
164 @type reset_env: boolean
165 @param reset_env: whether to reset or keep the default os environment
166 @type interactive: boolean
167 @param interactive: whether we pipe stdin, stdout and stderr
168 (default behaviour) or run the command interactive
169 @type timeout: int
170 @param timeout: If not None, timeout in seconds until child process gets
171 killed
172 @type noclose_fds: list
173 @param noclose_fds: list of additional (fd >=3) file descriptors to leave
174 open for the child process
175 @type input_fd: C{file}-like object or numeric file descriptor
176 @param input_fd: File descriptor for process' standard input
177 @type postfork_fn: Callable receiving PID as parameter
178 @param postfork_fn: Callback run after fork but before timeout
179 @rtype: L{RunResult}
180 @return: RunResult instance
181 @raise errors.ProgrammerError: if we call this when forks are disabled
182
183 """
184 if _no_fork:
185 raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
186
187 if output and interactive:
188 raise errors.ProgrammerError("Parameters 'output' and 'interactive' can"
189 " not be provided at the same time")
190
191 if not (output is None or input_fd is None):
192
193
194 raise errors.ProgrammerError("Parameters 'output' and 'input_fd' can"
195 " not be used at the same time")
196
197 if isinstance(cmd, basestring):
198 strcmd = cmd
199 shell = True
200 else:
201 cmd = [str(val) for val in cmd]
202 strcmd = utils_text.ShellQuoteArgs(cmd)
203 shell = False
204
205 if output:
206 logging.info("RunCmd %s, output file '%s'", strcmd, output)
207 else:
208 logging.info("RunCmd %s", strcmd)
209
210 cmd_env = _BuildCmdEnvironment(env, reset_env)
211
212 try:
213 if output is None:
214 out, err, status, timeout_action = _RunCmdPipe(cmd, cmd_env, shell, cwd,
215 interactive, timeout,
216 noclose_fds, input_fd,
217 postfork_fn=postfork_fn)
218 else:
219 if postfork_fn:
220 raise errors.ProgrammerError("postfork_fn is not supported if output"
221 " should be captured")
222 assert input_fd is None
223 timeout_action = _TIMEOUT_NONE
224 status = _RunCmdFile(cmd, cmd_env, shell, output, cwd, noclose_fds)
225 out = err = ""
226 except OSError, err:
227 if err.errno == errno.ENOENT:
228 raise errors.OpExecError("Can't execute '%s': not found (%s)" %
229 (strcmd, err))
230 else:
231 raise
232
233 if status >= 0:
234 exitcode = status
235 signal_ = None
236 else:
237 exitcode = None
238 signal_ = -status
239
240 return RunResult(exitcode, signal_, out, err, strcmd, timeout_action, timeout)
241
242
244 """Setup a daemon's environment.
245
246 This should be called between the first and second fork, due to
247 setsid usage.
248
249 @param cwd: the directory to which to chdir
250 @param umask: the umask to setup
251
252 """
253 os.chdir(cwd)
254 os.umask(umask)
255 os.setsid()
256
257
259 """Setups up a daemon's file descriptors.
260
261 @param output_file: if not None, the file to which to redirect
262 stdout/stderr
263 @param output_fd: if not None, the file descriptor for stdout/stderr
264
265 """
266
267 assert [output_file, output_fd].count(None) >= 1
268
269
270 devnull_fd = os.open(os.devnull, os.O_RDONLY)
271
272 output_close = True
273
274 if output_fd is not None:
275 output_close = False
276 elif output_file is not None:
277
278 try:
279 output_fd = os.open(output_file,
280 os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0600)
281 except EnvironmentError, err:
282 raise Exception("Opening output file failed: %s" % err)
283 else:
284 output_fd = os.open(os.devnull, os.O_WRONLY)
285
286
287 os.dup2(devnull_fd, 0)
288 os.dup2(output_fd, 1)
289 os.dup2(output_fd, 2)
290
291 if devnull_fd > 2:
292 utils_wrapper.CloseFdNoError(devnull_fd)
293
294 if output_close and output_fd > 2:
295 utils_wrapper.CloseFdNoError(output_fd)
296
297
298 -def StartDaemon(cmd, env=None, cwd="/", output=None, output_fd=None,
299 pidfile=None):
300 """Start a daemon process after forking twice.
301
302 @type cmd: string or list
303 @param cmd: Command to run
304 @type env: dict
305 @param env: Additional environment variables
306 @type cwd: string
307 @param cwd: Working directory for the program
308 @type output: string
309 @param output: Path to file in which to save the output
310 @type output_fd: int
311 @param output_fd: File descriptor for output
312 @type pidfile: string
313 @param pidfile: Process ID file
314 @rtype: int
315 @return: Daemon process ID
316 @raise errors.ProgrammerError: if we call this when forks are disabled
317
318 """
319 if _no_fork:
320 raise errors.ProgrammerError("utils.StartDaemon() called with fork()"
321 " disabled")
322
323 if output and not (bool(output) ^ (output_fd is not None)):
324 raise errors.ProgrammerError("Only one of 'output' and 'output_fd' can be"
325 " specified")
326
327 if isinstance(cmd, basestring):
328 cmd = ["/bin/sh", "-c", cmd]
329
330 strcmd = utils_text.ShellQuoteArgs(cmd)
331
332 if output:
333 logging.debug("StartDaemon %s, output file '%s'", strcmd, output)
334 else:
335 logging.debug("StartDaemon %s", strcmd)
336
337 cmd_env = _BuildCmdEnvironment(env, False)
338
339
340 (pidpipe_read, pidpipe_write) = os.pipe()
341 try:
342 try:
343
344 (errpipe_read, errpipe_write) = os.pipe()
345 try:
346 try:
347
348 pid = os.fork()
349 if pid == 0:
350 try:
351
352 _StartDaemonChild(errpipe_read, errpipe_write,
353 pidpipe_read, pidpipe_write,
354 cmd, cmd_env, cwd,
355 output, output_fd, pidfile)
356 finally:
357
358 os._exit(1)
359 finally:
360 utils_wrapper.CloseFdNoError(errpipe_write)
361
362
363
364 errormsg = utils_wrapper.RetryOnSignal(os.read, errpipe_read,
365 100 * 1024)
366 finally:
367 utils_wrapper.CloseFdNoError(errpipe_read)
368 finally:
369 utils_wrapper.CloseFdNoError(pidpipe_write)
370
371
372 pidtext = utils_wrapper.RetryOnSignal(os.read, pidpipe_read, 128)
373 finally:
374 utils_wrapper.CloseFdNoError(pidpipe_read)
375
376
377 try:
378 os.waitpid(pid, 0)
379 except OSError:
380 pass
381
382 if errormsg:
383 raise errors.OpExecError("Error when starting daemon process: %r" %
384 errormsg)
385
386 try:
387 return int(pidtext)
388 except (ValueError, TypeError), err:
389 raise errors.OpExecError("Error while trying to parse PID %r: %s" %
390 (pidtext, err))
391
392
393 -def _StartDaemonChild(errpipe_read, errpipe_write,
394 pidpipe_read, pidpipe_write,
395 args, env, cwd,
396 output, fd_output, pidfile):
397 """Child process for starting daemon.
398
399 """
400 try:
401
402 utils_wrapper.CloseFdNoError(errpipe_read)
403 utils_wrapper.CloseFdNoError(pidpipe_read)
404
405
406 SetupDaemonEnv()
407
408
409 pid = os.fork()
410 if pid != 0:
411
412 os._exit(0)
413
414
415
416 utils_wrapper.SetCloseOnExecFlag(errpipe_write, True)
417
418
419 noclose_fds = [errpipe_write]
420
421
422 if pidfile:
423 fd_pidfile = utils_io.WritePidFile(pidfile)
424
425
426 noclose_fds.append(fd_pidfile)
427
428 utils_wrapper.SetCloseOnExecFlag(fd_pidfile, False)
429 else:
430 fd_pidfile = None
431
432 SetupDaemonFDs(output, fd_output)
433
434
435 utils_wrapper.RetryOnSignal(os.write, pidpipe_write, str(os.getpid()))
436
437
438 CloseFDs(noclose_fds=noclose_fds)
439
440
441 os.chdir(cwd)
442
443 if env is None:
444 os.execvp(args[0], args)
445 else:
446 os.execvpe(args[0], args, env)
447 except:
448 try:
449
450 WriteErrorToFD(errpipe_write, str(sys.exc_info()[1]))
451 except:
452
453 pass
454
455 os._exit(1)
456
457
459 """Possibly write an error message to a fd.
460
461 @type fd: None or int (file descriptor)
462 @param fd: if not None, the error will be written to this fd
463 @param err: string, the error message
464
465 """
466 if fd is None:
467 return
468
469 if not err:
470 err = "<unknown error>"
471
472 utils_wrapper.RetryOnSignal(os.write, fd, err)
473
474
476 """Raises L{utils_retry.RetryAgain} if child is still alive.
477
478 @raises utils_retry.RetryAgain: If child is still alive
479
480 """
481 if child.poll() is None:
482 raise utils_retry.RetryAgain()
483
484
486 """Waits for the child to terminate or until we reach timeout.
487
488 """
489 try:
490 utils_retry.Retry(_CheckIfAlive, (1.0, 1.2, 5.0), max(0, timeout),
491 args=[child])
492 except utils_retry.RetryTimeout:
493 pass
494
495
499 """Run a command and return its output.
500
501 @type cmd: string or list
502 @param cmd: Command to run
503 @type env: dict
504 @param env: The environment to use
505 @type via_shell: bool
506 @param via_shell: if we should run via the shell
507 @type cwd: string
508 @param cwd: the working directory for the program
509 @type interactive: boolean
510 @param interactive: Run command interactive (without piping)
511 @type timeout: int
512 @param timeout: Timeout after the programm gets terminated
513 @type noclose_fds: list
514 @param noclose_fds: list of additional (fd >=3) file descriptors to leave
515 open for the child process
516 @type input_fd: C{file}-like object or numeric file descriptor
517 @param input_fd: File descriptor for process' standard input
518 @type postfork_fn: Callable receiving PID as parameter
519 @param postfork_fn: Function run after fork but before timeout
520 @rtype: tuple
521 @return: (out, err, status)
522
523 """
524 poller = select.poll()
525
526 if interactive:
527 stderr = None
528 stdout = None
529 else:
530 stderr = subprocess.PIPE
531 stdout = subprocess.PIPE
532
533 if input_fd:
534 stdin = input_fd
535 elif interactive:
536 stdin = None
537 else:
538 stdin = subprocess.PIPE
539
540 if noclose_fds:
541 preexec_fn = lambda: CloseFDs(noclose_fds)
542 close_fds = False
543 else:
544 preexec_fn = None
545 close_fds = True
546
547 child = subprocess.Popen(cmd, shell=via_shell,
548 stderr=stderr,
549 stdout=stdout,
550 stdin=stdin,
551 close_fds=close_fds, env=env,
552 cwd=cwd,
553 preexec_fn=preexec_fn)
554
555 if postfork_fn:
556 postfork_fn(child.pid)
557
558 out = StringIO()
559 err = StringIO()
560
561 linger_timeout = None
562
563 if timeout is None:
564 poll_timeout = None
565 else:
566 poll_timeout = utils_algo.RunningTimeout(timeout, True).Remaining
567
568 msg_timeout = ("Command %s (%d) run into execution timeout, terminating" %
569 (cmd, child.pid))
570 msg_linger = ("Command %s (%d) run into linger timeout, killing" %
571 (cmd, child.pid))
572
573 timeout_action = _TIMEOUT_NONE
574
575
576
577 assert (stdin == subprocess.PIPE) ^ (child.stdin is None), \
578 "subprocess' stdin did not behave as documented"
579
580 if not interactive:
581 if child.stdin is not None:
582 child.stdin.close()
583 poller.register(child.stdout, select.POLLIN)
584 poller.register(child.stderr, select.POLLIN)
585 fdmap = {
586 child.stdout.fileno(): (out, child.stdout),
587 child.stderr.fileno(): (err, child.stderr),
588 }
589 for fd in fdmap:
590 utils_wrapper.SetNonblockFlag(fd, True)
591
592 while fdmap:
593 if poll_timeout:
594 pt = poll_timeout() * 1000
595 if pt < 0:
596 if linger_timeout is None:
597 logging.warning(msg_timeout)
598 if child.poll() is None:
599 timeout_action = _TIMEOUT_TERM
600 utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid,
601 signal.SIGTERM)
602 linger_timeout = \
603 utils_algo.RunningTimeout(_linger_timeout, True).Remaining
604 pt = linger_timeout() * 1000
605 if pt < 0:
606 break
607 else:
608 pt = None
609
610 pollresult = utils_wrapper.RetryOnSignal(poller.poll, pt)
611
612 for fd, event in pollresult:
613 if event & select.POLLIN or event & select.POLLPRI:
614 data = fdmap[fd][1].read()
615
616 if not data:
617 poller.unregister(fd)
618 del fdmap[fd]
619 continue
620 fdmap[fd][0].write(data)
621 if (event & select.POLLNVAL or event & select.POLLHUP or
622 event & select.POLLERR):
623 poller.unregister(fd)
624 del fdmap[fd]
625
626 if timeout is not None:
627 assert callable(poll_timeout)
628
629
630 if child.poll() is None:
631 _WaitForProcess(child, poll_timeout())
632
633
634 if child.poll() is None:
635 if linger_timeout is None:
636 logging.warning(msg_timeout)
637 timeout_action = _TIMEOUT_TERM
638 utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid, signal.SIGTERM)
639 lt = _linger_timeout
640 else:
641 lt = linger_timeout()
642 _WaitForProcess(child, lt)
643
644
645 if child.poll() is None:
646 timeout_action = _TIMEOUT_KILL
647 logging.warning(msg_linger)
648 utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid, signal.SIGKILL)
649
650 out = out.getvalue()
651 err = err.getvalue()
652
653 status = child.wait()
654 return out, err, status, timeout_action
655
656
657 -def _RunCmdFile(cmd, env, via_shell, output, cwd, noclose_fds):
658 """Run a command and save its output to a file.
659
660 @type cmd: string or list
661 @param cmd: Command to run
662 @type env: dict
663 @param env: The environment to use
664 @type via_shell: bool
665 @param via_shell: if we should run via the shell
666 @type output: str
667 @param output: the filename in which to save the output
668 @type cwd: string
669 @param cwd: the working directory for the program
670 @type noclose_fds: list
671 @param noclose_fds: list of additional (fd >=3) file descriptors to leave
672 open for the child process
673 @rtype: int
674 @return: the exit status
675
676 """
677 fh = open(output, "a")
678
679 if noclose_fds:
680 preexec_fn = lambda: CloseFDs(noclose_fds + [fh.fileno()])
681 close_fds = False
682 else:
683 preexec_fn = None
684 close_fds = True
685
686 try:
687 child = subprocess.Popen(cmd, shell=via_shell,
688 stderr=subprocess.STDOUT,
689 stdout=fh,
690 stdin=subprocess.PIPE,
691 close_fds=close_fds, env=env,
692 cwd=cwd,
693 preexec_fn=preexec_fn)
694
695 child.stdin.close()
696 status = child.wait()
697 finally:
698 fh.close()
699 return status
700
701
702 -def RunParts(dir_name, env=None, reset_env=False):
703 """Run Scripts or programs in a directory
704
705 @type dir_name: string
706 @param dir_name: absolute path to a directory
707 @type env: dict
708 @param env: The environment to use
709 @type reset_env: boolean
710 @param reset_env: whether to reset or keep the default os environment
711 @rtype: list of tuples
712 @return: list of (name, (one of RUNDIR_STATUS), RunResult)
713
714 """
715 rr = []
716
717 try:
718 dir_contents = utils_io.ListVisibleFiles(dir_name)
719 except OSError, err:
720 logging.warning("RunParts: skipping %s (cannot list: %s)", dir_name, err)
721 return rr
722
723 for relname in sorted(dir_contents):
724 fname = utils_io.PathJoin(dir_name, relname)
725 if not (constants.EXT_PLUGIN_MASK.match(relname) is not None and
726 utils_wrapper.IsExecutable(fname)):
727 rr.append((relname, constants.RUNPARTS_SKIP, None))
728 else:
729 try:
730 result = RunCmd([fname], env=env, reset_env=reset_env)
731 except Exception, err:
732 rr.append((relname, constants.RUNPARTS_ERR, str(err)))
733 else:
734 rr.append((relname, constants.RUNPARTS_RUN, result))
735
736 return rr
737
738
740 """Returns the path for a PID's proc status file.
741
742 @type pid: int
743 @param pid: Process ID
744 @rtype: string
745
746 """
747 return "/proc/%d/status" % pid
748
749
751 """Check if a given pid exists on the system.
752
753 @note: zombie status is not handled, so zombie processes
754 will be returned as alive
755 @type pid: int
756 @param pid: the process ID to check
757 @rtype: boolean
758 @return: True if the process exists
759
760 """
761 def _TryStat(name):
762 try:
763 os.stat(name)
764 return True
765 except EnvironmentError, err:
766 if err.errno in (errno.ENOENT, errno.ENOTDIR):
767 return False
768 elif err.errno == errno.EINVAL:
769 raise utils_retry.RetryAgain(err)
770 raise
771
772 assert isinstance(pid, int), "pid must be an integer"
773 if pid <= 0:
774 return False
775
776
777
778 try:
779 return utils_retry.Retry(_TryStat, (0.01, 1.5, 0.1), 0.5,
780 args=[_GetProcStatusPath(pid)])
781 except utils_retry.RetryTimeout, err:
782 err.RaiseInner()
783
784
786 """Parse a rendered sigset_t value.
787
788 This is the opposite of the Linux kernel's fs/proc/array.c:render_sigset_t
789 function.
790
791 @type sigset: string
792 @param sigset: Rendered signal set from /proc/$pid/status
793 @rtype: set
794 @return: Set of all enabled signal numbers
795
796 """
797 result = set()
798
799 signum = 0
800 for ch in reversed(sigset):
801 chv = int(ch, 16)
802
803
804
805 if chv & 1:
806 result.add(signum + 1)
807 if chv & 2:
808 result.add(signum + 2)
809 if chv & 4:
810 result.add(signum + 3)
811 if chv & 8:
812 result.add(signum + 4)
813
814 signum += 4
815
816 return result
817
818
820 """Retrieves a field from the contents of a proc status file.
821
822 @type pstatus: string
823 @param pstatus: Contents of /proc/$pid/status
824 @type field: string
825 @param field: Name of field whose value should be returned
826 @rtype: string
827
828 """
829 for line in pstatus.splitlines():
830 parts = line.split(":", 1)
831
832 if len(parts) < 2 or parts[0] != field:
833 continue
834
835 return parts[1].strip()
836
837 return None
838
839
841 """Checks whether a process is handling a signal.
842
843 @type pid: int
844 @param pid: Process ID
845 @type signum: int
846 @param signum: Signal number
847 @rtype: bool
848
849 """
850 if status_path is None:
851 status_path = _GetProcStatusPath(pid)
852
853 try:
854 proc_status = utils_io.ReadFile(status_path)
855 except EnvironmentError, err:
856
857 if err.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL, errno.ESRCH):
858 return False
859 raise
860
861 sigcgt = _GetProcStatusField(proc_status, "SigCgt")
862 if sigcgt is None:
863 raise RuntimeError("%s is missing 'SigCgt' field" % status_path)
864
865
866 return signum in _ParseSigsetT(sigcgt)
867
868
870 """Daemonize the current process.
871
872 This detaches the current process from the controlling terminal and
873 runs it in the background as a daemon.
874
875 @type logfile: str
876 @param logfile: the logfile to which we should redirect stdout/stderr
877 @rtype: tuple; (int, callable)
878 @return: File descriptor of pipe(2) which must be closed to notify parent
879 process and a callable to reopen log files
880
881 """
882
883
884
885
886
887
888
889 (rpipe, wpipe) = os.pipe()
890
891
892 pid = os.fork()
893 if (pid == 0):
894 SetupDaemonEnv()
895
896
897 pid = os.fork()
898 if (pid == 0):
899 utils_wrapper.CloseFdNoError(rpipe)
900 else:
901
902 os._exit(0)
903 else:
904 utils_wrapper.CloseFdNoError(wpipe)
905
906
907 errormsg = utils_wrapper.RetryOnSignal(os.read, rpipe, 100 * 1024)
908 if errormsg:
909 sys.stderr.write("Error when starting daemon process: %r\n" % errormsg)
910 rcode = 1
911 else:
912 rcode = 0
913 os._exit(rcode)
914
915 reopen_fn = compat.partial(SetupDaemonFDs, logfile, None)
916
917
918 reopen_fn()
919
920 return (wpipe, reopen_fn)
921
922
925 """Kill a process given by its pid.
926
927 @type pid: int
928 @param pid: The PID to terminate.
929 @type signal_: int
930 @param signal_: The signal to send, by default SIGTERM
931 @type timeout: int
932 @param timeout: The timeout after which, if the process is still alive,
933 a SIGKILL will be sent. If not positive, no such checking
934 will be done
935 @type waitpid: boolean
936 @param waitpid: If true, we should waitpid on this process after
937 sending signals, since it's our own child and otherwise it
938 would remain as zombie
939
940 """
941 def _helper(pid, signal_, wait):
942 """Simple helper to encapsulate the kill/waitpid sequence"""
943 if utils_wrapper.IgnoreProcessNotFound(os.kill, pid, signal_) and wait:
944 try:
945 os.waitpid(pid, os.WNOHANG)
946 except OSError:
947 pass
948
949 if pid <= 0:
950
951 raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
952
953 if not IsProcessAlive(pid):
954 return
955
956 _helper(pid, signal_, waitpid)
957
958 if timeout <= 0:
959 return
960
961 def _CheckProcess():
962 if not IsProcessAlive(pid):
963 return
964
965 try:
966 (result_pid, _) = os.waitpid(pid, os.WNOHANG)
967 except OSError:
968 raise utils_retry.RetryAgain()
969
970 if result_pid > 0:
971 return
972
973 raise utils_retry.RetryAgain()
974
975 try:
976
977 utils_retry.Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
978 except utils_retry.RetryTimeout:
979 pass
980
981 if IsProcessAlive(pid):
982
983 _helper(pid, signal.SIGKILL, waitpid)
984
985
987 """Runs a function in a separate process.
988
989 Note: Only boolean return values are supported.
990
991 @type fn: callable
992 @param fn: Function to be called
993 @rtype: bool
994 @return: Function's result
995
996 """
997 pid = os.fork()
998 if pid == 0:
999
1000 try:
1001
1002 utils_wrapper.ResetTempfileModule()
1003
1004
1005 result = int(bool(fn(*args)))
1006 assert result in (0, 1)
1007 except:
1008 logging.exception("Error while calling function in separate process")
1009
1010 result = 33
1011
1012 os._exit(result)
1013
1014
1015
1016
1017 (_, status) = os.waitpid(pid, 0)
1018
1019 if os.WIFSIGNALED(status):
1020 exitcode = None
1021 signum = os.WTERMSIG(status)
1022 else:
1023 exitcode = os.WEXITSTATUS(status)
1024 signum = None
1025
1026 if not (exitcode in (0, 1) and signum is None):
1027 raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
1028 (exitcode, signum))
1029
1030 return bool(exitcode)
1031
1032
1034 """Close file descriptors.
1035
1036 This closes all file descriptors above 2 (i.e. except
1037 stdin/out/err).
1038
1039 @type noclose_fds: list or None
1040 @param noclose_fds: if given, it denotes a list of file descriptor
1041 that should not be closed
1042
1043 """
1044
1045 if 'SC_OPEN_MAX' in os.sysconf_names:
1046 try:
1047 MAXFD = os.sysconf('SC_OPEN_MAX')
1048 if MAXFD < 0:
1049 MAXFD = 1024
1050 except OSError:
1051 MAXFD = 1024
1052 else:
1053 MAXFD = 1024
1054
1055 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1056 if (maxfd == resource.RLIM_INFINITY):
1057 maxfd = MAXFD
1058
1059
1060 for fd in range(3, maxfd):
1061 if noclose_fds and fd in noclose_fds:
1062 continue
1063 utils_wrapper.CloseFdNoError(fd)
1064