Line data Source code
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 34 : 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 34 : if (exitstatus == -1) 43 : { 44 0 : snprintf(str, sizeof(str), "%m"); 45 : } 46 34 : 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 34 : switch (WEXITSTATUS(exitstatus)) 53 : { 54 0 : case 126: 55 0 : snprintf(str, sizeof(str), _("command not executable")); 56 0 : break; 57 : 58 0 : case 127: 59 0 : snprintf(str, sizeof(str), _("command not found")); 60 0 : break; 61 : 62 34 : default: 63 34 : snprintf(str, sizeof(str), 64 34 : _("child process exited with exit code %d"), 65 34 : WEXITSTATUS(exitstatus)); 66 : } 67 : } 68 0 : 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 0 : snprintf(str, sizeof(str), 76 0 : _("child process was terminated by signal %d: %s"), 77 : WTERMSIG(exitstatus), pg_strsignal(WTERMSIG(exitstatus))); 78 : #endif 79 : } 80 : else 81 0 : snprintf(str, sizeof(str), 82 0 : _("child process exited with unrecognized status %d"), 83 : exitstatus); 84 : 85 34 : 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 278 : wait_result_is_signal(int exit_status, int signum) 103 : { 104 278 : if (WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum) 105 0 : return true; 106 278 : if (WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 128 + signum) 107 0 : return true; 108 278 : 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 284 : wait_result_is_any_signal(int exit_status, bool include_command_not_found) 122 : { 123 284 : if (WIFSIGNALED(exit_status)) 124 2 : return true; 125 564 : if (WIFEXITED(exit_status) && 126 282 : WEXITSTATUS(exit_status) > (include_command_not_found ? 125 : 128)) 127 0 : return true; 128 282 : 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 8 : wait_result_to_exit_code(int exit_status) 139 : { 140 8 : if (exit_status == -1) 141 0 : return -1; /* failure of pclose() or system() */ 142 8 : if (WIFEXITED(exit_status)) 143 8 : return WEXITSTATUS(exit_status); 144 0 : if (WIFSIGNALED(exit_status)) 145 0 : return 128 + WTERMSIG(exit_status); 146 : /* On many systems, this is unreachable */ 147 0 : return -1; 148 : }