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