1 
2 /*--------------------------------------------------------------------*/
3 /*--- Command line handling.                       m_commandline.c ---*/
4 /*--------------------------------------------------------------------*/
5 
6 /*
7    This file is part of Valgrind, a dynamic binary instrumentation
8    framework.
9 
10    Copyright (C) 2000-2013 Julian Seward
11       jseward@acm.org
12 
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License as
15    published by the Free Software Foundation; either version 2 of the
16    License, or (at your option) any later version.
17 
18    This program is distributed in the hope that it will be useful, but
19    WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21    General Public License for more details.
22 
23    You should have received a copy of the GNU General Public License
24    along with this program; if not, write to the Free Software
25    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26    02111-1307, USA.
27 
28    The GNU General Public License is contained in the file COPYING.
29 */
30 
31 #include "pub_core_basics.h"
32 #include "pub_core_vki.h"
33 #include "pub_core_libcassert.h"
34 #include "pub_core_libcbase.h"
35 #include "pub_core_libcfile.h"
36 #include "pub_core_libcprint.h"
37 #include "pub_core_libcproc.h"
38 #include "pub_core_mallocfree.h"
39 #include "pub_core_xarray.h"
40 #include "pub_core_clientstate.h"
41 #include "pub_core_commandline.h" /* self */
42 
43 
44 /* Add a string to an expandable array of strings. */
45 
add_string(XArray * xa,HChar * str)46 static void add_string ( XArray* /* of HChar* */xa, HChar* str )
47 {
48    (void) VG_(addToXA)( xa, (void*)(&str) );
49 }
50 
51 
52 /* Read the contents of .valgrindrc in 'dir' into malloc'd memory. */
53 // Note that we deliberately don't free the malloc'd memory.  See
54 // comment at call site.
55 
read_dot_valgrindrc(const HChar * dir)56 static HChar* read_dot_valgrindrc ( const HChar* dir )
57 {
58    Int    n;
59    SysRes fd;
60    struct vg_stat stat_buf;
61    HChar* f_clo = NULL;
62    const  HChar dot_valgrindrc[] = ".valgrindrc";
63 
64    vg_assert(dir != NULL);
65 
66    HChar filename[VG_(strlen)(dir) + 1 + VG_(strlen)(dot_valgrindrc) + 1];
67    VG_(sprintf)(filename, "%s/%s", dir, dot_valgrindrc);
68 
69    fd = VG_(open)(filename, 0, VKI_S_IRUSR);
70    if ( !sr_isError(fd) ) {
71       Int res = VG_(fstat)( sr_Res(fd), &stat_buf );
72       // Ignore if not owned by current user or world writeable (CVE-2008-4865)
73       if (!res && stat_buf.uid == VG_(geteuid)()
74           && (!(stat_buf.mode & VKI_S_IWOTH))) {
75          if ( stat_buf.size > 0 ) {
76             f_clo = VG_(malloc)("commandline.rdv.1", stat_buf.size+1);
77             n = VG_(read)(sr_Res(fd), f_clo, stat_buf.size);
78             if (n == -1) n = 0;
79             vg_assert(n >= 0 && n <= stat_buf.size+1);
80             f_clo[n] = '\0';
81          }
82       }
83       else
84          VG_(message)(Vg_UserMsg,
85                "%s was not read as it is either world writeable or not "
86                "owned by the current user\n", filename);
87 
88       VG_(close)(sr_Res(fd));
89    }
90    return f_clo;
91 }
92 
93 
94 // Add args from a string into VG_(args_for_valgrind), splitting the
95 // string at whitespace and adding each component as a separate arg.
96 
add_args_from_string(HChar * s)97 static void add_args_from_string ( HChar* s )
98 {
99    HChar* tmp;
100    HChar* cp = s;
101    vg_assert(cp);
102    while (True) {
103       // We have alternating sequences: blanks, non-blanks, blanks...
104       // copy the non-blanks sequences, and add terminating '\0'
105       while (VG_(isspace)(*cp)) cp++;
106       if (*cp == 0) break;
107       tmp = cp;
108       while ( !VG_(isspace)(*cp) && *cp != 0 ) cp++;
109       if ( *cp != 0 ) *cp++ = '\0';       // terminate if not the last
110       add_string( VG_(args_for_valgrind), tmp );
111    }
112 }
113 
114 
115 /* Split up the args presented by the launcher to m_main.main(), and
116    park them in VG_(args_for_client) and VG_(args_for_valgrind).
117 
118    The resulting arg list is the concatenation of the following:
119    - contents of ~/.valgrindrc
120    - contents of $VALGRIND_OPTS
121    - contents of ./.valgrindrc
122    - args from the command line
123    in the stated order.
124 
125    VG_(args_for_valgrind_noexecpass) is set to be the number of items
126    in the first three categories.  They are not passed to child invokations
127    at exec, whereas the last group is.
128 
129    If the last group contains --command-line-only=yes, then the
130    first three groups are left empty.
131 
132    Scheme: first examine the last group (the supplied argc/argv).
133    It should look like this.
134 
135       args-for-v  exe_name  args-for-c
136 
137    args-for-v are taken until either they don't start with '-' or
138    a "--" is seen.
139 
140    The exe name and args-for-c are recorded without further ado.
141    Note that args-for-c[0] is the first real arg for the client, not
142    its executable name.
143 
144    args-for-v are then copied into tmp_xarray.
145 
146    if args-for-v does not include --command-line-only=yes:
147       contents of ~/.valgrindrc, $VALGRIND_OPTS and ./.valgrindrc
148       are copied into VG_(args_for_valgrind).
149    else
150       VG_(args_for_valgrind) is made empty.
151 
152    Finally, tmp_xarray is copied onto the end of VG_(args_for_valgrind).
153 */
154 
VG_(split_up_argv)155 void VG_(split_up_argv)( Int argc, HChar** argv )
156 {
157           Int  i;
158           Bool augment = True;
159    static Bool already_called = False;
160 
161    XArray* /* of HChar* */ tmp_xarray;
162 
163    /* This function should be called once, at startup, and then never
164       again. */
165    vg_assert(!already_called);
166    already_called = True;
167 
168    tmp_xarray = VG_(newXA)( VG_(malloc), "commandline.sua.1",
169                             VG_(free), sizeof(HChar*) );
170 
171    vg_assert( ! VG_(args_for_valgrind) );
172    VG_(args_for_valgrind)
173       = VG_(newXA)( VG_(malloc), "commandline.sua.2",
174                     VG_(free), sizeof(HChar*) );
175 
176    vg_assert( ! VG_(args_for_client) );
177    VG_(args_for_client)
178       = VG_(newXA)( VG_(malloc), "commandline.sua.3",
179                     VG_(free), sizeof(HChar*) );
180 
181    /* Collect up the args-for-V. */
182    i = 1; /* skip the exe (stage2) name. */
183    for (; i < argc; i++) {
184       vg_assert(argv[i]);
185       if (0 == VG_(strcmp)(argv[i], "--")) {
186          i++;
187          break;
188       }
189       if (0 == VG_(strcmp)(argv[i], "--command-line-only=yes"))
190          augment = False;
191       if (argv[i][0] != '-')
192 	break;
193       add_string( tmp_xarray, argv[i] );
194    }
195 
196    /* Should now be looking at the exe name. */
197    if (i < argc) {
198       vg_assert(argv[i]);
199       VG_(args_the_exename) = argv[i];
200       i++;
201    }
202 
203    /* The rest are args for the client. */
204    for (; i < argc; i++) {
205       vg_assert(argv[i]);
206       add_string( VG_(args_for_client), argv[i] );
207    }
208 
209    /* Get extra args from ~/.valgrindrc, $VALGRIND_OPTS and
210       ./.valgrindrc into VG_(args_for_valgrind). */
211    if (augment) {
212       // read_dot_valgrindrc() allocates the return value with
213       // VG_(malloc)().  We do not free f1_clo and f2_clo as they get
214       // put into VG_(args_for_valgrind) and so must persist.
215       HChar* home    = VG_(getenv)("HOME");
216       HChar* f1_clo  = home ? read_dot_valgrindrc( home ) : NULL;
217       HChar* env_clo = VG_(strdup)( "commandline.sua.4",
218                                     VG_(getenv)(VALGRIND_OPTS) );
219       HChar* f2_clo  = NULL;
220 
221       // Don't read ./.valgrindrc if "." is the same as "$HOME", else its
222       // contents will be applied twice. (bug #142488)
223       if (home) {
224          const HChar *cwd = VG_(get_startup_wd)();
225          f2_clo = ( VG_STREQ(home, cwd)
226                        ? NULL : read_dot_valgrindrc(".") );
227       }
228 
229       if (f1_clo)  add_args_from_string( f1_clo );
230       if (env_clo) add_args_from_string( env_clo );
231       if (f2_clo)  add_args_from_string( f2_clo );
232    }
233 
234    /* .. and record how many extras we got. */
235    VG_(args_for_valgrind_noexecpass)
236       = VG_(sizeXA)( VG_(args_for_valgrind) );
237 
238    /* Finally, copy tmp_xarray onto the end. */
239    for (i = 0; i < VG_(sizeXA)( tmp_xarray ); i++)
240       add_string( VG_(args_for_valgrind),
241                   * (HChar**)VG_(indexXA)( tmp_xarray, i ) );
242 
243    VG_(deleteXA)( tmp_xarray );
244 }
245 
246 /*--------------------------------------------------------------------*/
247 /*--- end                                                          ---*/
248 /*--------------------------------------------------------------------*/
249