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 """Parse a rendered sigset_t value.
796
797 This is the opposite of the Linux kernel's fs/proc/array.c:render_sigset_t
798 function.
799
800 @type sigset: string
801 @param sigset: Rendered signal set from /proc/$pid/status
802 @rtype: set
803 @return: Set of all enabled signal numbers
804
805 """
806 result = set()
807
808 signum = 0
809 for ch in reversed(sigset):
810 chv = int(ch, 16)
811
812
813
814 if chv & 1:
815 result.add(signum + 1)
816 if chv & 2:
817 result.add(signum + 2)
818 if chv & 4:
819 result.add(signum + 3)
820 if chv & 8:
821 result.add(signum + 4)
822
823 signum += 4
824
825 return result
826
827
829 """Retrieves a field from the contents of a proc status file.
830
831 @type pstatus: string
832 @param pstatus: Contents of /proc/$pid/status
833 @type field: string
834 @param field: Name of field whose value should be returned
835 @rtype: string
836
837 """
838 for line in pstatus.splitlines():
839 parts = line.split(":", 1)
840
841 if len(parts) < 2 or parts[0] != field:
842 continue
843
844 return parts[1].strip()
845
846 return None
847
848
850 """Checks whether a process is handling a signal.
851
852 @type pid: int
853 @param pid: Process ID
854 @type signum: int
855 @param signum: Signal number
856 @rtype: bool
857
858 """
859 if status_path is None:
860 status_path = _GetProcStatusPath(pid)
861
862 try:
863 proc_status = utils_io.ReadFile(status_path)
864 except EnvironmentError, err:
865
866 if err.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL, errno.ESRCH):
867 return False
868 raise
869
870 sigcgt = _GetProcStatusField(proc_status, "SigCgt")
871 if sigcgt is None:
872 raise RuntimeError("%s is missing 'SigCgt' field" % status_path)
873
874
875 return signum in _ParseSigsetT(sigcgt)
876
877
879 """Daemonize the current process.
880
881 This detaches the current process from the controlling terminal and
882 runs it in the background as a daemon.
883
884 @type logfile: str
885 @param logfile: the logfile to which we should redirect stdout/stderr
886 @rtype: tuple; (int, callable)
887 @return: File descriptor of pipe(2) which must be closed to notify parent
888 process and a callable to reopen log files
889
890 """
891
892
893
894
895
896
897
898 (rpipe, wpipe) = os.pipe()
899
900
901 pid = os.fork()
902 if (pid == 0):
903 SetupDaemonEnv()
904
905
906 pid = os.fork()
907 if (pid == 0):
908 utils_wrapper.CloseFdNoError(rpipe)
909 else:
910
911 os._exit(0)
912 else:
913 utils_wrapper.CloseFdNoError(wpipe)
914
915
916 errormsg = utils_wrapper.RetryOnSignal(os.read, rpipe, 100 * 1024)
917 if errormsg:
918 sys.stderr.write("Error when starting daemon process: %r\n" % errormsg)
919 rcode = 1
920 else:
921 rcode = 0
922 os._exit(rcode)
923
924 reopen_fn = compat.partial(SetupDaemonFDs, logfile, None)
925
926
927 reopen_fn()
928
929 return (wpipe, reopen_fn)
930
931
934 """Kill a process given by its pid.
935
936 @type pid: int
937 @param pid: The PID to terminate.
938 @type signal_: int
939 @param signal_: The signal to send, by default SIGTERM
940 @type timeout: int
941 @param timeout: The timeout after which, if the process is still alive,
942 a SIGKILL will be sent. If not positive, no such checking
943 will be done
944 @type waitpid: boolean
945 @param waitpid: If true, we should waitpid on this process after
946 sending signals, since it's our own child and otherwise it
947 would remain as zombie
948
949 """
950 def _helper(pid, signal_, wait):
951 """Simple helper to encapsulate the kill/waitpid sequence"""
952 if utils_wrapper.IgnoreProcessNotFound(os.kill, pid, signal_) and wait:
953 try:
954 os.waitpid(pid, os.WNOHANG)
955 except OSError:
956 pass
957
958 if pid <= 0:
959
960 raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
961
962 if not IsProcessAlive(pid):
963 return
964
965 _helper(pid, signal_, waitpid)
966
967 if timeout <= 0:
968 return
969
970 def _CheckProcess():
971 if not IsProcessAlive(pid):
972 return
973
974 try:
975 (result_pid, _) = os.waitpid(pid, os.WNOHANG)
976 except OSError:
977 raise utils_retry.RetryAgain()
978
979 if result_pid > 0:
980 return
981
982 raise utils_retry.RetryAgain()
983
984 try:
985
986 utils_retry.Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
987 except utils_retry.RetryTimeout:
988 pass
989
990 if IsProcessAlive(pid):
991
992 _helper(pid, signal.SIGKILL, waitpid)
993
994
996 """Runs a function in a separate process.
997
998 Note: Only boolean return values are supported.
999
1000 @type fn: callable
1001 @param fn: Function to be called
1002 @rtype: bool
1003 @return: Function's result
1004
1005 """
1006 pid = os.fork()
1007 if pid == 0:
1008
1009 try:
1010
1011 utils_wrapper.ResetTempfileModule()
1012
1013
1014 result = int(bool(fn(*args)))
1015 assert result in (0, 1)
1016 except:
1017 logging.exception("Error while calling function in separate process")
1018
1019 result = 33
1020
1021 os._exit(result)
1022
1023
1024
1025
1026 (_, status) = os.waitpid(pid, 0)
1027
1028 if os.WIFSIGNALED(status):
1029 exitcode = None
1030 signum = os.WTERMSIG(status)
1031 else:
1032 exitcode = os.WEXITSTATUS(status)
1033 signum = None
1034
1035 if not (exitcode in (0, 1) and signum is None):
1036 raise errors.GenericError("Child program failed (code=%s, signal=%s)" %
1037 (exitcode, signum))
1038
1039 return bool(exitcode)
1040
1041
1043 """Close file descriptors.
1044
1045 This closes all file descriptors above 2 (i.e. except
1046 stdin/out/err).
1047
1048 @type noclose_fds: list or None
1049 @param noclose_fds: if given, it denotes a list of file descriptor
1050 that should not be closed
1051
1052 """
1053
1054 if 'SC_OPEN_MAX' in os.sysconf_names:
1055 try:
1056 MAXFD = os.sysconf('SC_OPEN_MAX')
1057 if MAXFD < 0:
1058 MAXFD = 1024
1059 except OSError:
1060 MAXFD = 1024
1061 else:
1062 MAXFD = 1024
1063
1064 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1065 if (maxfd == resource.RLIM_INFINITY):
1066 maxfd = MAXFD
1067
1068
1069 for fd in range(3, maxfd):
1070 if noclose_fds and fd in noclose_fds:
1071 continue
1072 utils_wrapper.CloseFdNoError(fd)
1073