PostgreSQL Source Code  git master
wait_error.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * wait_error.c
4  * Convert a wait/waitpid(2) result code to a human-readable string
5  *
6  *
7  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  * src/common/wait_error.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 
17 #ifndef FRONTEND
18 #include "postgres.h"
19 #else
20 #include "postgres_fe.h"
21 #endif
22 
23 #include <signal.h>
24 #include <sys/wait.h>
25 
26 /*
27  * Return a human-readable string explaining the reason a child process
28  * terminated. The argument is a return code returned by wait(2) or
29  * waitpid(2), which also applies to pclose(3) and system(3). The result is a
30  * translated, palloc'd or malloc'd string.
31  */
32 char *
33 wait_result_to_str(int exitstatus)
34 {
35  char str[512];
36 
37  /*
38  * To simplify using this after pclose() and system(), handle status -1
39  * first. In that case, there is no wait result but some error indicated
40  * by errno.
41  */
42  if (exitstatus == -1)
43  {
44  snprintf(str, sizeof(str), "%m");
45  }
46  else if (WIFEXITED(exitstatus))
47  {
48  /*
49  * Give more specific error message for some common exit codes that
50  * have a special meaning in shells.
51  */
52  switch (WEXITSTATUS(exitstatus))
53  {
54  case 126:
55  snprintf(str, sizeof(str), _("command not executable"));
56  break;
57 
58  case 127:
59  snprintf(str, sizeof(str), _("command not found"));
60  break;
61 
62  default:
63  snprintf(str, sizeof(str),
64  _("child process exited with exit code %d"),
65  WEXITSTATUS(exitstatus));
66  }
67  }
68  else if (WIFSIGNALED(exitstatus))
69  {
70 #if defined(WIN32)
71  snprintf(str, sizeof(str),
72  _("child process was terminated by exception 0x%X"),
73  WTERMSIG(exitstatus));
74 #else
75  snprintf(str, sizeof(str),
76  _("child process was terminated by signal %d: %s"),
77  WTERMSIG(exitstatus), pg_strsignal(WTERMSIG(exitstatus)));
78 #endif
79  }
80  else
81  snprintf(str, sizeof(str),
82  _("child process exited with unrecognized status %d"),
83  exitstatus);
84 
85  return pstrdup(str);
86 }
87 
88 /*
89  * Return true if a wait(2) result indicates that the child process
90  * died due to the specified signal.
91  *
92  * The reason this is worth having a wrapper function for is that
93  * there are two cases: the signal might have been received by our
94  * immediate child process, or there might've been a shell process
95  * between us and the child that died. The shell will, per POSIX,
96  * report the child death using exit code 128 + signal number.
97  *
98  * If there is no possibility of an intermediate shell, this function
99  * need not (and probably should not) be used.
100  */
101 bool
102 wait_result_is_signal(int exit_status, int signum)
103 {
104  if (WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum)
105  return true;
106  if (WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 128 + signum)
107  return true;
108  return false;
109 }
110 
111 /*
112  * Return true if a wait(2) result indicates that the child process
113  * died due to any signal. We consider either direct child death
114  * or a shell report of child process death as matching the condition.
115  *
116  * If include_command_not_found is true, also return true for shell
117  * exit codes indicating "command not found" and the like
118  * (specifically, exit codes 126 and 127; see above).
119  */
120 bool
121 wait_result_is_any_signal(int exit_status, bool include_command_not_found)
122 {
123  if (WIFSIGNALED(exit_status))
124  return true;
125  if (WIFEXITED(exit_status) &&
126  WEXITSTATUS(exit_status) > (include_command_not_found ? 125 : 128))
127  return true;
128  return false;
129 }
130 
131 /*
132  * Return the shell exit code (normally 0 to 255) that corresponds to the
133  * given wait status. The argument is a wait status as returned by wait(2)
134  * or waitpid(2), which also applies to pclose(3) and system(3). To support
135  * the latter two cases, we pass through "-1" unchanged.
136  */
137 int
138 wait_result_to_exit_code(int exit_status)
139 {
140  if (exit_status == -1)
141  return -1; /* failure of pclose() or system() */
142  if (WIFEXITED(exit_status))
143  return WEXITSTATUS(exit_status);
144  if (WIFSIGNALED(exit_status))
145  return 128 + WTERMSIG(exit_status);
146  /* On many systems, this is unreachable */
147  return -1;
148 }
#define _(x)
Definition: elog.c:90
const char * str
char * pstrdup(const char *in)
Definition: mcxt.c:1695
const char * pg_strsignal(int signum)
Definition: pgstrsignal.c:39
#define snprintf
Definition: port.h:238
int wait_result_to_exit_code(int exit_status)
Definition: wait_error.c:138
char * wait_result_to_str(int exitstatus)
Definition: wait_error.c:33
bool wait_result_is_signal(int exit_status, int signum)
Definition: wait_error.c:102
bool wait_result_is_any_signal(int exit_status, bool include_command_not_found)
Definition: wait_error.c:121
#define WIFEXITED(w)
Definition: win32_port.h:152
#define WIFSIGNALED(w)
Definition: win32_port.h:153
#define WTERMSIG(w)
Definition: win32_port.h:155
#define WEXITSTATUS(w)
Definition: win32_port.h:154