libexplain
1.4.D001
|
00001 /* 00002 * libexplain - Explain errno values returned by libc functions 00003 * Copyright (C) 2008-2010, 2012, 2013 Peter Miller 00004 * Written by Peter Miller <pmiller@opensource.org.au> 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as 00008 * published by the Free Software Foundation; either version 3 of the 00009 * License, or (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00018 */ 00019 00020 #include <libexplain/ac/assert.h> 00021 #include <libexplain/ac/ctype.h> 00022 #include <libexplain/ac/errno.h> 00023 #include <libexplain/ac/limits.h> /* for PATH_MAX on Solaris */ 00024 #include <libexplain/ac/stdlib.h> 00025 #include <libexplain/ac/string.h> 00026 #include <libexplain/ac/sys/param.h> /* for PATH_MAX except Solaris */ 00027 #include <libexplain/ac/sys/wait.h> 00028 #include <libexplain/ac/unistd.h> 00029 00030 #include <libexplain/buffer/errno/access.h> 00031 #include <libexplain/buffer/errno/fork.h> 00032 #include <libexplain/buffer/errno/system.h> 00033 #include <libexplain/buffer/errno/wait.h> 00034 #include <libexplain/buffer/wait_status.h> 00035 #include <libexplain/common_message_buffer.h> 00036 #include <libexplain/string_buffer.h> 00037 #include <libexplain/system.h> 00038 #include <libexplain/output.h> 00039 00040 00041 static int 00042 extract_command(const char *src, char *dst, size_t dst_size) 00043 { 00044 char *dst_end = dst + dst_size - 1; 00045 char *dp; 00046 00047 for (;;) 00048 { 00049 if (!*src) 00050 break; 00051 if (isspace((unsigned char)*src)) 00052 { 00053 ++src; 00054 continue; 00055 } 00056 dp = dst; 00057 for (;;) 00058 { 00059 if (dp < dst_end) 00060 *dp++ = *src; 00061 ++src; 00062 if (!*src) 00063 break; 00064 if (isspace((unsigned char)*src) || strchr("<>^|", *src)) 00065 { 00066 ++src; 00067 break; 00068 } 00069 } 00070 *dp = '\0'; 00071 00072 /* 00073 * If there is an equals sign, it is probably an environment 00074 * variable setting before the command word. 00075 */ 00076 if (strchr(dst, '=')) 00077 continue; 00078 00079 /* 00080 * Probably found the command, so long as it contains no shell 00081 * special characters. 00082 */ 00083 return !strpbrk(dst, "!\"#$&'()*:;<>?[\\]^`{|}"); 00084 } 00085 return 0; 00086 } 00087 00088 00089 static int 00090 command_on_path(const char *cmd) 00091 { 00092 char combo[PATH_MAX * 2 + 3]; 00093 explain_string_buffer_t combo_sb; 00094 const char *path; 00095 const char *start; 00096 00097 path = getenv("PATH"); 00098 if (!path) 00099 path = "."; 00100 for (;;) 00101 { 00102 start = path; 00103 for (;;) 00104 { 00105 if (*path == '\0') 00106 break; 00107 if (*path == ':') 00108 break; 00109 ++path; 00110 } 00111 explain_string_buffer_init(&combo_sb, combo, sizeof(combo)); 00112 if (start == path) 00113 explain_string_buffer_putc(&combo_sb, '.'); 00114 else 00115 explain_string_buffer_write(&combo_sb, start, path - start); 00116 explain_string_buffer_path_join(&combo_sb, cmd); 00117 if (access(combo, X_OK) >= 0) 00118 return 1; 00119 if (*path == '\0') 00120 return 0; 00121 assert(*path == ':'); 00122 ++path; 00123 } 00124 } 00125 00126 00127 int 00128 explain_system_success(const char *command) 00129 { 00130 int status; 00131 00132 status = system(command); 00133 if (status < 0) 00134 { 00135 explain_output_error("%s", explain_system(command)); 00136 } 00137 else if (status != 0) 00138 { 00139 explain_string_buffer_t sb; 00140 explain_string_buffer_init 00141 ( 00142 &sb, 00143 explain_common_message_buffer, 00144 explain_common_message_buffer_size 00145 ); 00146 explain_buffer_errno_system(&sb, 0, command); 00147 00148 if (!command) 00149 command = "/bin/sh"; 00150 00151 /* FIXME: i18n */ 00152 explain_string_buffer_puts(&sb, ", but "); 00153 explain_buffer_wait_status(&sb, status); 00154 if (WIFEXITED(status) && WEXITSTATUS(status) == 127) 00155 { 00156 char cmd[PATH_MAX + 1]; 00157 00158 /* 00159 * It is possible that execl(2) used by system(3) failed, 00160 * and this could have caused the 127 exit status. On the 00161 * other hand, the command itself could return this. 00162 */ 00163 if (extract_command(command, cmd, sizeof(cmd))) 00164 { 00165 if (strchr(cmd, '/')) 00166 { 00167 if (access(cmd, X_OK) < 0) 00168 { 00169 explain_string_buffer_puts(&sb, ", "); 00170 explain_buffer_errno_access_explanation 00171 ( 00172 &sb, 00173 errno, 00174 "system", 00175 cmd, 00176 X_OK 00177 ); 00178 } 00179 } 00180 else if (!command_on_path(cmd)) 00181 { 00182 explain_string_buffer_puts(&sb, ", "); 00183 explain_string_buffer_puts_quoted(&sb, cmd); 00184 explain_string_buffer_puts 00185 ( 00186 &sb, 00187 " command not found on $PATH" 00188 ); 00189 } 00190 } 00191 } 00192 explain_output_error("%s", explain_common_message_buffer); 00193 } 00194 return status; 00195 } 00196 00197 00198 void 00199 explain_system_success_or_die(const char *command) 00200 { 00201 if (explain_system_success(command)) 00202 explain_output_exit_failure(); 00203 } 00204 00205 00206 /* vim: set ts=8 sw=4 et : */