libexplain
1.4.D001
|
00001 /* 00002 * libexplain - Explain errno values returned by libc functions 00003 * Copyright (C) 2008-2011, 2013 Peter Miller 00004 * 00005 * This program is free software; you can redistribute it and/or modify it 00006 * under the terms of the GNU Lesser General Public License as published by 00007 * the Free Software Foundation; either version 3 of the License, or (at 00008 * your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Lesser General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Lesser General Public License 00016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00017 */ 00018 00019 #include <libexplain/ac/errno.h> 00020 #include <libexplain/ac/limits.h> /* for PATH_MAX on Solaris */ 00021 #include <libexplain/ac/stdio.h> 00022 #include <libexplain/ac/string.h> 00023 #include <libexplain/ac/sys/param.h> /* for PATH_MAX except Solaris */ 00024 #include <libexplain/ac/unistd.h> 00025 00026 #include <libexplain/buffer/efault.h> 00027 #include <libexplain/buffer/eintr.h> 00028 #include <libexplain/buffer/eio.h> 00029 #include <libexplain/buffer/eloop.h> 00030 #include <libexplain/buffer/emfile.h> 00031 #include <libexplain/buffer/enametoolong.h> 00032 #include <libexplain/buffer/enfile.h> 00033 #include <libexplain/buffer/enoent.h> 00034 #include <libexplain/buffer/enotdir.h> 00035 #include <libexplain/buffer/enomem.h> 00036 #include <libexplain/buffer/errno/generic.h> 00037 #include <libexplain/buffer/errno/execve.h> 00038 #include <libexplain/buffer/errno/path_resolution.h> 00039 #include <libexplain/buffer/path_to_pid.h> 00040 #include <libexplain/buffer/pathname.h> 00041 #include <libexplain/buffer/pointer.h> 00042 #include <libexplain/explanation.h> 00043 #include <libexplain/option.h> 00044 #include <libexplain/is_efault.h> 00045 00046 00047 static int 00048 count(char *const *p) 00049 { 00050 int result; 00051 00052 result = 0; 00053 while (*p) 00054 { 00055 ++result; 00056 ++p; 00057 } 00058 return result; 00059 } 00060 00061 00062 static void 00063 explain_buffer_errno_execve_system_call(explain_string_buffer_t *sb, 00064 int errnum, const char *pathname, char *const *argv, char *const *envp) 00065 { 00066 explain_string_buffer_puts(sb, "execve(pathname = "); 00067 explain_buffer_pathname(sb, pathname); 00068 if (errnum == EFAULT) 00069 { 00070 /* FIXME: expand this more, print the arguments that you can. */ 00071 explain_string_buffer_puts(sb, ", argv = "); 00072 explain_buffer_pointer(sb, argv); 00073 explain_string_buffer_puts(sb, ", envp = "); 00074 explain_buffer_pointer(sb, envp); 00075 } 00076 else 00077 { 00078 int n; 00079 int argsize; 00080 00081 /* 00082 * produce output similar to strace 00083 */ 00084 explain_string_buffer_puts(sb, ", argv = ["); 00085 argsize = 0; 00086 for (n = 0; ; ++n) 00087 { 00088 const char *s; 00089 00090 s = argv[n]; 00091 if (!s) 00092 break; 00093 argsize += strlen(s); 00094 if (n) 00095 { 00096 explain_string_buffer_puts(sb, ", "); 00097 if (argsize >= 1000) 00098 { 00099 explain_string_buffer_printf 00100 ( 00101 sb, 00102 /* FIXME: i18n */ 00103 "... plus another %d command line arguments", 00104 count(argv) - n 00105 ); 00106 break; 00107 } 00108 } 00109 explain_string_buffer_puts_quoted(sb, s); 00110 } 00111 explain_string_buffer_puts(sb, "], envp = [/*"); 00112 if (explain_option_dialect_specific()) 00113 explain_string_buffer_printf(sb, " %d", count(envp)); 00114 explain_string_buffer_puts(sb, " vars */]"); 00115 } 00116 explain_string_buffer_putc(sb, ')'); 00117 } 00118 00119 00120 static int 00121 wonky_pointer(explain_string_buffer_t *sb, char *const *array, 00122 const char *array_caption) 00123 { 00124 int n; 00125 00126 /* This isn't quite right */ 00127 if (explain_is_efault_pointer(array, sizeof(*array))) 00128 { 00129 explain_buffer_efault(sb, array_caption); 00130 return 0; 00131 } 00132 for (n = 0; array[n]; ++n) 00133 { 00134 if (explain_is_efault_path(array[n])) 00135 { 00136 char temp[20]; 00137 00138 snprintf(temp, sizeof(temp), "%s[%d]", array_caption, n); 00139 explain_buffer_efault(sb, temp); 00140 return 0; 00141 } 00142 } 00143 return -1; 00144 } 00145 00146 00159 static void 00160 explain_buffer_file1(explain_string_buffer_t *sb, const char *pathname) 00161 { 00162 FILE *fp; 00163 char buffer[PATH_MAX + 20]; 00164 00165 if (!explain_option_dialect_specific()) 00166 return; 00167 00168 /* FIXME: need shell quoting */ 00169 snprintf(buffer, sizeof(buffer), "file -b %s 2> /dev/null", pathname); 00170 00171 fp = popen(buffer, "r"); 00172 if (fp) 00173 { 00174 char *bp; 00175 00176 bp = buffer; 00177 for (;;) 00178 { 00179 int c = getc(fp); 00180 if (c == EOF || c == '\n') 00181 break; 00182 *bp++ = c; 00183 if (bp >= buffer + sizeof(buffer) - 1) 00184 break; 00185 } 00186 pclose(fp); 00187 *bp = '\0'; 00188 if (buffer[0]) 00189 { 00190 explain_string_buffer_puts(sb, " ("); 00191 explain_string_buffer_puts(sb, buffer); 00192 explain_string_buffer_putc(sb, ')'); 00193 } 00194 } 00195 } 00196 00197 00198 void 00199 explain_buffer_errno_execve_explanation(explain_string_buffer_t *sb, 00200 int errnum, const char *syscall_name, const char *pathname, 00201 char *const *argv, char *const *envp) 00202 { 00203 explain_final_t final_component; 00204 00205 explain_final_init(&final_component); 00206 final_component.want_to_execute = 1; 00207 00208 switch (errnum) 00209 { 00210 case E2BIG: 00211 explain_string_buffer_puts 00212 ( 00213 sb, 00214 /* FIXME: i18n */ 00215 "the total number of bytes in the argument list (argv) plus " 00216 "the environment (envp) is too large" 00217 ); 00218 if (explain_option_dialect_specific()) 00219 { 00220 long total; 00221 long arg_max; 00222 00223 /* 00224 * Count the total size. 00225 */ 00226 total = 0; 00227 if (argv) 00228 { 00229 int argc; 00230 00231 for (argc = 0; ; ++argc) 00232 { 00233 const char *p; 00234 00235 p = argv[argc]; 00236 if (!p) 00237 break; 00238 total += strlen(p) + 1; 00239 } 00240 total += (argc + 1) * sizeof(argv[0]); 00241 } 00242 if (envp) 00243 { 00244 int envc; 00245 00246 for (envc = 0; ; ++envc) 00247 { 00248 const char *p; 00249 00250 p = envp[envc]; 00251 if (!p) 00252 break; 00253 total += strlen(p) + 1; 00254 } 00255 total += (envc + 1) * sizeof(envp[0]); 00256 } 00257 00258 /* 00259 * Get the system limit. 00260 */ 00261 arg_max = sysconf(_SC_ARG_MAX); 00262 #ifdef ARG_MAX 00263 if (arg_max <= 0) 00264 arg_max = ARG_MAX; 00265 #endif 00266 00267 /* 00268 * Print the dialect-specific part of the explanation. 00269 */ 00270 explain_string_buffer_printf(sb, " (%ld", total); 00271 if (arg_max > 0) 00272 explain_string_buffer_printf(sb, " > %ld", arg_max); 00273 explain_string_buffer_putc(sb, ')'); 00274 } 00275 break; 00276 00277 case EACCES: 00278 if 00279 ( 00280 explain_buffer_errno_path_resolution 00281 ( 00282 sb, 00283 errnum, 00284 pathname, 00285 "pathname", 00286 &final_component 00287 ) 00288 ) 00289 { 00290 explain_string_buffer_puts 00291 ( 00292 sb, 00293 /* FIXME: i18n */ 00294 "search permission is denied on a component of the path prefix " 00295 "of pathname or the name of a script interpreter; or, the " 00296 "file or a script interpreter is not a regular file; or, " 00297 "execute permission is denied for the file or a script or ELF " 00298 "interpreter; or, the file system is mounted noexec" 00299 ); 00300 } 00301 break; 00302 00303 case EFAULT: 00304 /* 00305 * FIXME: Some historical systems return [EFAULT] rather than [ENOEXEC] 00306 * when the new process image file is corrupted. They are 00307 * non-conforming. Only add code to handle that case if anyone 00308 * ever gripes. 00309 */ 00310 if (explain_is_efault_path(pathname)) 00311 { 00312 explain_buffer_efault(sb, "pathname"); 00313 } 00314 else if 00315 ( 00316 wonky_pointer(sb, argv, "argv") 00317 && 00318 wonky_pointer(sb, envp, "envp") 00319 ) 00320 { 00321 /* 00322 * When wonky_pointer finds a problem it prints something and 00323 * returns zero. To get here, nothing has been printed yet. 00324 */ 00325 explain_buffer_efault(sb, "pathname or argv or envp"); 00326 } 00327 break; 00328 00329 case EINTR: 00330 explain_buffer_eintr(sb, "exec"); 00331 break; 00332 00333 case EINVAL: 00334 if 00335 ( 00336 explain_buffer_errno_path_resolution 00337 ( 00338 sb, 00339 errnum, 00340 pathname, 00341 "pathname", 00342 &final_component 00343 ) 00344 ) 00345 { 00346 explain_string_buffer_puts 00347 ( 00348 sb, 00349 /* FIXME: i18n */ 00350 "an ELF executable had more than one PT_INTERP segment " 00351 "(tried to name more than one interpreter); or, the new " 00352 "process image file has the appropriate permission and has " 00353 "a recognized executable binary format, but the system does " 00354 "not support execution of a file with this format" 00355 ); 00356 explain_buffer_file1(sb, pathname); 00357 } 00358 break; 00359 00360 case EIO: 00361 explain_buffer_eio_path(sb, pathname); 00362 break; 00363 00364 case EISDIR: 00365 if 00366 ( 00367 explain_buffer_errno_path_resolution 00368 ( 00369 sb, 00370 errnum, 00371 pathname, 00372 "pathname", 00373 &final_component 00374 ) 00375 ) 00376 { 00377 explain_string_buffer_puts 00378 ( 00379 sb, 00380 /* 00381 * xgettext: This message is used when explaining an EISDIR 00382 * error from an execve system call, in the case where an ELF 00383 * interpreter was a directory. 00384 */ 00385 i18n("an ELF interpreter was a directory, and it is not " 00386 "possible to execute a directory") 00387 ); 00388 } 00389 break; 00390 00391 #ifdef ELIBBAD 00392 case ELIBBAD: 00393 explain_string_buffer_puts 00394 ( 00395 sb, 00396 /* FIXME: i18n */ 00397 "an ELF interpreter was not in a recognized format" 00398 ); 00399 break; 00400 #endif 00401 00402 case ELOOP: 00403 explain_buffer_eloop(sb, pathname, "pathname", &final_component); 00404 break; 00405 00406 case EMFILE: 00407 explain_buffer_emfile(sb); 00408 break; 00409 00410 case ENAMETOOLONG: 00411 explain_buffer_enametoolong 00412 ( 00413 sb, 00414 pathname, 00415 "pathname", 00416 &final_component 00417 ); 00418 break; 00419 00420 case ENFILE: 00421 explain_buffer_enfile(sb); 00422 break; 00423 00424 case ENOENT: 00425 explain_buffer_enoent(sb, pathname, "pathname", &final_component); 00426 break; 00427 00428 case ENOEXEC: 00429 if 00430 ( 00431 explain_buffer_errno_path_resolution 00432 ( 00433 sb, 00434 errnum, 00435 pathname, 00436 "pathname", 00437 &final_component 00438 ) 00439 ) 00440 { 00441 explain_string_buffer_puts 00442 ( 00443 sb, 00444 /* FIXME: i18n */ 00445 "an executable is not in a recognized format, is for the wrong " 00446 "architecture, or has some other format error that means it " 00447 "cannot be executed" 00448 ); 00449 explain_buffer_file1(sb, pathname); 00450 } 00451 break; 00452 00453 case ENOMEM: 00454 explain_buffer_enomem_kernel(sb); 00455 break; 00456 00457 case ENOTDIR: 00458 explain_buffer_enotdir(sb, pathname, "pathname", &final_component); 00459 break; 00460 00461 case EPERM: 00462 /* FIXME: say which one */ 00463 explain_string_buffer_puts 00464 ( 00465 sb, 00466 /* FIXME: i18n */ 00467 "the file system is mounted nosuid; or, the pocess is being " 00468 "traced; or, the user is not the superuser, and the file " 00469 "has the set-user-ID or set-group-ID bit set" 00470 ); 00471 break; 00472 00473 case ETXTBSY: 00474 explain_string_buffer_puts 00475 ( 00476 sb, 00477 /* FIXME: i18n */ 00478 "pathname is open for writing by one or more processes" 00479 ); 00480 explain_buffer_path_to_pid(sb, pathname); 00481 break; 00482 00483 default: 00484 explain_buffer_errno_generic(sb, errnum, syscall_name); 00485 break; 00486 } 00487 } 00488 00489 00490 void 00491 explain_buffer_errno_execve(explain_string_buffer_t *sb, int errnum, 00492 const char *pathname, char *const *argv, char *const *envp) 00493 { 00494 explain_explanation_t exp; 00495 00496 explain_explanation_init(&exp, errnum); 00497 explain_buffer_errno_execve_system_call 00498 ( 00499 &exp.system_call_sb, 00500 errnum, 00501 pathname, 00502 argv, 00503 envp 00504 ); 00505 explain_buffer_errno_execve_explanation 00506 ( 00507 &exp.explanation_sb, 00508 errnum, 00509 "execve", 00510 pathname, 00511 argv, 00512 envp 00513 ); 00514 explain_explanation_assemble(&exp, sb); 00515 } 00516 00517 00518 /* vim: set ts=8 sw=4 et : */