Update copyright to LGPL on all files
[dyninst.git] / parseThat / src / config.C
1 /*
2  * Copyright (c) 1996-2009 Barton P. Miller
3  * 
4  * We provide the Paradyn Parallel Performance Tools (below
5  * described as "Paradyn") on an AS IS basis, and do not warrant its
6  * validity or performance.  We reserve the right to update, modify,
7  * or discontinue this software at any time.  We shall have no
8  * obligation to supply such updates or modifications or any other
9  * form of support to you.
10  * 
11  * By your use of Paradyn, you understand and agree that we (or any
12  * other person or entity with proprietary rights in Paradyn) are
13  * under no obligation to provide either maintenance services,
14  * update services, notices of latent defects, or correction of
15  * defects for Paradyn.
16  * 
17  * This library is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU Lesser General Public
19  * License as published by the Free Software Foundation; either
20  * version 2.1 of the License, or (at your option) any later version.
21  * 
22  * This library is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25  * Lesser General Public License for more details.
26  * 
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30  */
31 #include <iostream>
32 #include <cstring>
33 #include <dirent.h>
34 #include <stdlib.h>
35 #include <sys/stat.h>
36
37 #include "strlist.h"
38 #include "config.h"
39 #include "utils.h"
40 #include "log.h"
41
42 Config config;
43
44 bool getNext_SingleBinary();
45 bool getNext_BatchDirectory();
46 bool getNext_BatchFile();
47
48 void configInit()
49 {
50     const char *outdir;
51
52     /* Propogate config structure with default values. */
53     config.transMode = TRANS_NONE;
54
55     config.target[0] = '\0';
56     config.outfd = stdout;
57     config.instType = DEFAULT;
58     
59     config.record_enabled = false;
60     config.no_fork = false;
61     config.recursive = false;
62     config.summary = false;
63     config.include_libs = false;
64     config.use_attach = false;
65     config.use_merge_tramp = false;
66     config.use_save_world = false;
67     config.saved_mutatee = NULL;
68     config.use_process = true;
69
70     config.trace_inst = false;
71     config.trace_count = 0;
72     config.pipefd = -1;
73
74     config.pid = 0;
75     config.grandchildren = set< int >();
76
77     config.verbose = INFO;
78     config.printipc = true;
79
80     config.parse_level = PARSE_MODULE;
81     config.inst_level = INST_NONE;
82
83     config.time_limit = 0;
84
85     config.state = NORMAL;
86
87     config.dynlib = NULL;
88
89     config.hunt_crashes = false;
90     config.hunt_crashed = false;
91     config.hunt_low = -1;
92     config.hunt_high = -1;
93     config.hunt_file = NULL;
94
95     config.binary_args = NULL;
96
97     outdir = "/tmp";
98     snprintf(config.record_dir, sizeof(config.record_dir), "%s/%s", 
99              outdir, HISTORY_RECORD_DIR_DEFAULT);
100     snprintf(config.pipe_filename, sizeof(config.pipe_filename), "%s/pipe-%d", 
101              outdir, (int)getpid());
102     config.curr_rec = record_init();
103 }
104
105 bool getNextTarget()
106 {
107     switch (config.runMode) {
108     case SINGLE_BINARY: return getNext_SingleBinary();
109     case BATCH_DIRECTORY: return getNext_BatchDirectory();
110     case BATCH_FILE: return getNext_BatchFile();
111     default:
112         dlog(ERR, "*** parseThat error.  Invalid config.runMode detected in getNextTarget().\n");
113         dlog(ERR, "*** parseThat exiting.\n");
114         exit(-10);
115     }
116
117     // We should never get here.
118     return false;
119 }
120
121 bool validate_file(const char *file)
122 {
123     int retval;
124     char header[2];
125     return true;
126
127     FILE *fd = fopen(file, "r");
128
129     if (!fd) {
130         dlog(ERR, "Could not open %s for read: %s\n", file, strerror(errno));
131         return false;
132     }
133     errno = 0;
134     retval = fscanf(fd, "%c%c", &header[0], &header[1]);
135     fclose(fd);
136
137     if (errno) {
138         dlog(ERR, "Could not read from target mutatee %s.  Dyninst requires read access to mutatees.\n", file);
139         return false;
140     }
141
142     if (retval == 2 && header[0] == '#' && header[1] == '!') {
143         dlog(ERR, "%s looks like a script.  Dyninst cannot process script files.\n", file);
144         return false;
145     }
146
147     return true;
148 }
149
150 bool getNext_SingleBinary()
151 {
152     static int runOnce = 0;
153
154     if (runOnce > 0) {
155         config.target[0] = '\0';
156         return false;
157     }
158     runOnce = 1;
159     return validate_file(config.target);
160 }
161
162 bool getNext_BatchDirectory()
163 {
164     static strlist *dir_q = NULL;
165     static DIR *dirfd = NULL;
166     static char base_dir[ PATH_MAX ];
167
168     int i;
169     struct dirent *d_entry;
170
171     if (dir_q == NULL) {
172         // Initial call.  Push first directory onto queue.
173         //
174         // config.target should hold directory specified on command line.
175         dir_q = strlist_alloc();
176         if (!dir_q) {
177             dlog(ERR, "Could not allocate memory for strlist in getNext_BatchDirectory()\n");
178             exit(-2);
179         }
180         strlist_push_back(dir_q, config.target);
181     }
182
183     while (dir_q->count > 0 || dirfd != NULL) {
184         if (dirfd == NULL) {
185             char *data = strlist_get(dir_q, 0);
186             if ( data[ strlen(data)-1 ] != '/' )
187                 snprintf(base_dir, sizeof(base_dir), "%s/", data);
188             else
189                 strncpy(base_dir, data, sizeof(base_dir));
190             strlist_pop_front(dir_q);
191
192             dirfd = opendir(base_dir);
193             if (dirfd == NULL) {
194                 dlog(ERR, "Could not open directory %s: %s\n", base_dir, strerror(errno));
195                 break;
196             }
197         }
198
199         while ( (d_entry = readdir(dirfd)) != NULL) {
200             if (strcmp(d_entry->d_name, ".") == 0) continue;
201             if (strcmp(d_entry->d_name, "..") == 0) continue;
202
203             /*
204              * Generate target filename in config.target from base_dir and d_entry->d_name.
205              */
206             snprintf(config.target, sizeof(config.target), "%s%s", base_dir, d_entry->d_name);
207             config.argv[0] = strrchr(config.target, '/') + 1;   /* strrchr() will never return NULL
208                                                                    because we control base_dir. */
209
210             /*
211              * Check if target filename is a directory, or executable via stat().  Ignore if neither.
212              */
213             struct stat file_stat;
214             i = 0;
215             do {
216                 if (i++ > 10) break;
217                 errno = 0;
218             } while (stat(config.target, &file_stat) != 0 && errno == EINTR);
219
220             if (errno == EINTR) {
221                 dlog(ERR, "Skipping %s.  stat() interrupted 10 times.\n", config.target);
222                 continue;
223
224             } else if (errno) {
225                 dlog(ERR, "Skipping %s.  Error on stat(): %s\n", config.target, strerror(errno));
226                 continue;
227             }
228
229             if ((file_stat.st_mode & S_IFMT) == S_IFDIR) {
230                 if (config.recursive)
231                     strlist_push_back(dir_q, config.target);
232                 else
233                     dlog(INFO, "%s is a directory.\n", config.target);
234
235             } else if ((file_stat.st_mode & S_IXOTH) == S_IXOTH ||
236                        (file_stat.st_mode & S_IXGRP) == S_IXGRP ||
237                        (file_stat.st_mode & S_IXUSR) == S_IXUSR) {
238                 if (validate_file(config.target)) return true;
239
240             } else {
241                 dlog(VERB1, "Skipping %s.  Not executable.\n", config.target);
242             }
243         }
244
245         if (errno != 0) {
246             dlog(ERR, "Error on readdir(): %s\n", strerror(errno));
247             dirfd = NULL;
248             continue;
249         }
250
251         /* Done processing directory.  Close directory descriptor. */
252         i = 0;
253         while (closedir(dirfd) != 0) {
254             if (++i > 10) {
255                 dlog(ERR, "Warning: closedir() failed 10 times.  Ignoring open descriptor.\n");
256                 break;
257             }
258         }
259         dirfd = NULL;
260     }
261     return false;
262 }
263
264 bool getNext_BatchFile()
265 {
266     static unsigned maxArgc = 0;
267     static FILE *filefd = NULL;
268     strlist cmdline = STRLIST_INITIALIZER;
269
270     unsigned i = 0;
271     if (filefd == NULL) {
272         errno = 0;
273         filefd = fopen(config.config_file, "r");
274         if (filefd == NULL && errno != EINTR) {
275             dlog(ERR, "Could not open batch config file %s: %s\n", config.config_file, strerror(errno));
276             exit(-2);
277         }
278         if (++i > 10) {
279             dlog(ERR, "open() for %s has been interrupted 10 times.  Try again later.\n", config.config_file);
280             exit(-2);
281         }
282         config.argv = NULL;
283     }
284
285     char *buf;
286     while ( (buf = fgets_static(filefd))) {
287         strlist_clear(&cmdline);
288         cmdline = char2strlist(buf);
289
290         strncpy(config.target, strlist_get(&cmdline, 0), sizeof(config.target));
291
292         if (maxArgc < (cmdline.count + 1)) {
293             char **newargv = (char **)realloc(config.argv, (cmdline.count + 1) * sizeof(char *));
294             if (!newargv) {
295                 dlog(ERR, "Could not allocate memory for argv array in getNext_BatchFile().\n");
296                 exit(-2);
297             }
298             config.argv = newargv;
299         }
300         for (i = 0; i < cmdline.count; ++i)
301             config.argv[i] = strlist_get(&cmdline, i);
302         config.argv[i] = NULL;
303
304         if (strrchr(config.argv[0], '/'))
305             config.argv[0] = strrchr(config.argv[0], '/') + 1;
306
307         if (validate_file(config.target)) return true;
308     }
309     return false;
310 }