| |
| /*--------------------------------------------------------------------*/ |
| /*--- Command line handling. m_commandline.c ---*/ |
| /*--------------------------------------------------------------------*/ |
| |
| /* |
| This file is part of Valgrind, a dynamic binary instrumentation |
| framework. |
| |
| Copyright (C) 2000-2009 Julian Seward |
| jseward@acm.org |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License as |
| published by the Free Software Foundation; either version 2 of the |
| License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307, USA. |
| |
| The GNU General Public License is contained in the file COPYING. |
| */ |
| |
| #include "pub_core_basics.h" |
| #include "pub_core_vki.h" |
| #include "pub_core_libcassert.h" |
| #include "pub_core_libcbase.h" |
| #include "pub_core_libcfile.h" |
| #include "pub_core_libcprint.h" |
| #include "pub_core_libcproc.h" |
| #include "pub_core_mallocfree.h" |
| #include "pub_core_xarray.h" |
| #include "pub_core_clientstate.h" |
| #include "pub_core_commandline.h" /* self */ |
| |
| |
| /* Add a string to an expandable array of strings. */ |
| |
| static void add_string ( XArray* /* of HChar* */xa, HChar* str ) |
| { |
| (void) VG_(addToXA)( xa, (void*)(&str) ); |
| } |
| |
| |
| /* Read the contents of .valgrindrc in 'dir' into malloc'd memory. */ |
| // Note that we deliberately don't free the malloc'd memory. See |
| // comment at call site. |
| |
| static HChar* read_dot_valgrindrc ( HChar* dir ) |
| { |
| Int n; |
| SysRes fd; |
| struct vg_stat stat_buf; |
| HChar* f_clo = NULL; |
| HChar filename[VKI_PATH_MAX]; |
| |
| VG_(snprintf)(filename, VKI_PATH_MAX, "%s/.valgrindrc", |
| ( NULL == dir ? "" : dir ) ); |
| fd = VG_(open)(filename, 0, VKI_S_IRUSR); |
| if ( !fd.isError ) { |
| Int res = VG_(fstat)( fd.res, &stat_buf ); |
| // Ignore if not owned by current user or world writeable (CVE-2008-4865) |
| if (!res && stat_buf.st_uid == VG_(geteuid)() |
| && (!(stat_buf.st_mode & VKI_S_IWOTH))) { |
| if ( stat_buf.st_size > 0 ) { |
| f_clo = VG_(malloc)("commandline.rdv.1", stat_buf.st_size+1); |
| vg_assert(f_clo); |
| n = VG_(read)(fd.res, f_clo, stat_buf.st_size); |
| if (n == -1) n = 0; |
| vg_assert(n >= 0 && n <= stat_buf.st_size+1); |
| f_clo[n] = '\0'; |
| } |
| } |
| else |
| VG_(message)(Vg_UserMsg, |
| "%s was not read as it is world writeable or not owned by the " |
| "current user", filename); |
| |
| VG_(close)(fd.res); |
| } |
| return f_clo; |
| } |
| |
| |
| // Add args from a string into VG_(args_for_valgrind), splitting the |
| // string at whitespace and adding each component as a separate arg. |
| |
| static void add_args_from_string ( HChar* s ) |
| { |
| HChar* tmp; |
| HChar* cp = s; |
| vg_assert(cp); |
| while (True) { |
| // We have alternating sequences: blanks, non-blanks, blanks... |
| // copy the non-blanks sequences, and add terminating '\0' |
| while (VG_(isspace)(*cp)) cp++; |
| if (*cp == 0) break; |
| tmp = cp; |
| while ( !VG_(isspace)(*cp) && *cp != 0 ) cp++; |
| if ( *cp != 0 ) *cp++ = '\0'; // terminate if not the last |
| add_string( VG_(args_for_valgrind), tmp ); |
| } |
| } |
| |
| |
| /* Split up the args presented by the launcher to m_main.main(), and |
| park them in VG_(args_for_client) and VG_(args_for_valgrind). |
| |
| The resulting arg list is the concatenation of the following: |
| - contents of ~/.valgrindrc |
| - contents of $VALGRIND_OPTS |
| - contents of ./.valgrindrc |
| - args from the command line |
| in the stated order. |
| |
| VG_(args_for_valgrind_noexecpass) is set to be the number of items |
| in the first three categories. They are not passed to child invokations |
| at exec, whereas the last group is. |
| |
| If the last group contains --command-line-only=yes, then the |
| first three groups are left empty. |
| |
| Scheme: first examine the last group (the supplied argc/argv). |
| It should look like this. |
| |
| args-for-v exe_name args-for-c |
| |
| args-for-v are taken until either they don't start with '-' or |
| a "--" is seen. |
| |
| The exe name and args-for-c are recorded without further ado. |
| Note that args-for-c[0] is the first real arg for the client, not |
| its executable name. |
| |
| args-for-v are then copied into tmp_xarray. |
| |
| if args-for-v does not include --command-line-only=yes: |
| contents of ~/.valgrindrc, $VALGRIND_OPTS and ./.valgrindrc |
| are copied into VG_(args_for_valgrind). |
| else |
| VG_(args_for_valgrind) is made empty. |
| |
| Finally, tmp_xarray is copied onto the end of VG_(args_for_valgrind). |
| */ |
| |
| void VG_(split_up_argv)( Int argc, HChar** argv ) |
| { |
| Int i; |
| Bool augment = True; |
| static Bool already_called = False; |
| |
| XArray* /* of HChar* */ tmp_xarray; |
| |
| /* This function should be called once, at startup, and then never |
| again. */ |
| vg_assert(!already_called); |
| already_called = True; |
| |
| tmp_xarray = VG_(newXA)( VG_(malloc), "commandline.sua.1", |
| VG_(free), sizeof(HChar*) ); |
| vg_assert(tmp_xarray); |
| |
| vg_assert( ! VG_(args_for_valgrind) ); |
| VG_(args_for_valgrind) |
| = VG_(newXA)( VG_(malloc), "commandline.sua.2", |
| VG_(free), sizeof(HChar*) ); |
| vg_assert( VG_(args_for_valgrind) ); |
| |
| vg_assert( ! VG_(args_for_client) ); |
| VG_(args_for_client) |
| = VG_(newXA)( VG_(malloc), "commandline.sua.3", |
| VG_(free), sizeof(HChar*) ); |
| vg_assert( VG_(args_for_client) ); |
| |
| /* Collect up the args-for-V. */ |
| i = 1; /* skip the exe (stage2) name. */ |
| for (; i < argc; i++) { |
| vg_assert(argv[i]); |
| if (0 == VG_(strcmp)(argv[i], "--")) { |
| i++; |
| break; |
| } |
| if (0 == VG_(strcmp)(argv[i], "--command-line-only=yes")) |
| augment = False; |
| if (argv[i][0] != '-') |
| break; |
| add_string( tmp_xarray, argv[i] ); |
| } |
| |
| /* Should now be looking at the exe name. */ |
| if (i < argc) { |
| vg_assert(argv[i]); |
| VG_(args_the_exename) = argv[i]; |
| i++; |
| } |
| |
| /* The rest are args for the client. */ |
| for (; i < argc; i++) { |
| vg_assert(argv[i]); |
| add_string( VG_(args_for_client), argv[i] ); |
| } |
| |
| /* Get extra args from ~/.valgrindrc, $VALGRIND_OPTS and |
| ./.valgrindrc into VG_(args_for_valgrind). */ |
| if (augment) { |
| // read_dot_valgrindrc() allocates the return value with |
| // VG_(malloc)(). We do not free f1_clo and f2_clo as they get |
| // put into VG_(args_for_valgrind) and so must persist. |
| HChar* home = VG_(getenv)("HOME"); |
| HChar* f1_clo = home ? read_dot_valgrindrc( home ) : NULL; |
| HChar* env_clo = VG_(strdup)( "commandline.sua.4", |
| VG_(getenv)(VALGRIND_OPTS) ); |
| HChar* f2_clo = NULL; |
| |
| // Don't read ./.valgrindrc if "." is the same as "$HOME", else its |
| // contents will be applied twice. (bug #142488) |
| if (home) { |
| HChar cwd[VKI_PATH_MAX+1]; |
| Bool cwd_ok = VG_(get_startup_wd)(cwd, VKI_PATH_MAX); |
| f2_clo = ( (cwd_ok && VG_STREQ(home, cwd)) |
| ? NULL : read_dot_valgrindrc(".") ); |
| } |
| |
| if (f1_clo) add_args_from_string( f1_clo ); |
| if (env_clo) add_args_from_string( env_clo ); |
| if (f2_clo) add_args_from_string( f2_clo ); |
| } |
| |
| /* .. and record how many extras we got. */ |
| VG_(args_for_valgrind_noexecpass) |
| = VG_(sizeXA)( VG_(args_for_valgrind) ); |
| |
| /* Finally, copy tmp_xarray onto the end. */ |
| for (i = 0; i < VG_(sizeXA)( tmp_xarray ); i++) |
| add_string( VG_(args_for_valgrind), |
| * (HChar**)VG_(indexXA)( tmp_xarray, i ) ); |
| |
| VG_(deleteXA)( tmp_xarray ); |
| } |
| |
| /*--------------------------------------------------------------------*/ |
| /*--- end ---*/ |
| /*--------------------------------------------------------------------*/ |