extended parseThat to use new addressSpace abstraction
[dyninst.git] / parseThat / src / config.C
1 #include <iostream>
2 #include <cstring>
3 #include <dirent.h>
4 #include <stdlib.h>
5 #include <sys/stat.h>
6
7 #include "strlist.h"
8 #include "config.h"
9 #include "utils.h"
10 #include "log.h"
11
12 Config config;
13
14 bool getNext_SingleBinary();
15 bool getNext_BatchDirectory();
16 bool getNext_BatchFile();
17
18 void configInit()
19 {
20     const char *homedir;
21
22     /* Propogate config structure with default values. */
23     config.transMode = TRANS_NONE;
24
25     config.target[0] = '\0';
26     config.outfd = stdout;
27     
28     config.record_enabled = false;
29     config.no_fork = false;
30     config.recursive = false;
31     config.summary = false;
32     config.include_libs = false;
33     config.use_attach = false;
34     config.use_merge_tramp = false;
35     config.use_save_world = false;
36     config.saved_mutatee = NULL;
37     config.use_process = true;
38
39     config.trace_inst = false;
40     config.trace_count = 0;
41     config.pipefd = -1;
42
43     config.pid = 0;
44     config.grandchildren = set< int >();
45
46     config.verbose = INFO;
47
48     config.parse_level = PARSE_MODULE;
49     config.inst_level = INST_NONE;
50
51     config.time_limit = 0;
52
53     config.state = NORMAL;
54
55     config.dynlib = NULL;
56
57     homedir = getenv("HOME");
58     if (!homedir) {
59         config.record_dir[0] = '\0';
60         config.pipe_filename[0] = '\0';
61     } else {
62         if (homedir[ strlen(homedir) - 1 ] == '/') {
63             snprintf(config.record_dir, sizeof(config.record_dir), "%s%s", homedir, HISTORY_RECORD_DIR_DEFAULT);
64             snprintf(config.pipe_filename, sizeof(config.pipe_filename), "%spipe-%d", homedir, (int)getpid());
65         } else {
66             snprintf(config.record_dir, sizeof(config.record_dir), "%s/%s", homedir, HISTORY_RECORD_DIR_DEFAULT);
67             snprintf(config.pipe_filename, sizeof(config.pipe_filename), "%s/pipe-%d", homedir, (int)getpid());
68         }
69     }
70     config.curr_rec = record_init();
71 }
72
73 bool getNextTarget()
74 {
75     switch (config.runMode) {
76     case SINGLE_BINARY: return getNext_SingleBinary();
77     case BATCH_DIRECTORY: return getNext_BatchDirectory();
78     case BATCH_FILE: return getNext_BatchFile();
79     default:
80         dlog(ERR, "*** parseThat error.  Invalid config.runMode detected in getNextTarget().\n");
81         dlog(ERR, "*** parseThat exiting.\n");
82         exit(-10);
83     }
84
85     // We should never get here.
86     return false;
87 }
88
89 bool validate_file(const char *file)
90 {
91     int retval;
92     char header[2];
93     FILE *fd = fopen(file, "r");
94
95     if (!fd) {
96         dlog(ERR, "Could not open %s for read: %s\n", file, strerror(errno));
97         return false;
98     }
99     errno = 0;
100     retval = fscanf(fd, "%c%c", &header[0], &header[1]);
101     fclose(fd);
102
103     if (errno) {
104         dlog(ERR, "Could not read from target mutatee %s.  Dyninst requires read access to mutatees.\n", file);
105         return false;
106     }
107
108     if (retval == 2 && header[0] == '#' && header[1] == '!') {
109         dlog(ERR, "%s looks like a script.  Dyninst cannot process script files.\n", file);
110         return false;
111     }
112
113     return true;
114 }
115
116 bool getNext_SingleBinary()
117 {
118     static int runOnce = 0;
119
120     if (runOnce > 0) {
121         config.target[0] = '\0';
122         return false;
123     }
124     runOnce = 1;
125     return validate_file(config.target);
126 }
127
128 bool getNext_BatchDirectory()
129 {
130     static strlist *dir_q = NULL;
131     static DIR *dirfd = NULL;
132     static char base_dir[ PATH_MAX ];
133
134     int i;
135     struct dirent *d_entry;
136
137     if (dir_q == NULL) {
138         // Initial call.  Push first directory onto queue.
139         //
140         // config.target should hold directory specified on command line.
141         dir_q = strlist_alloc();
142         if (!dir_q) {
143             dlog(ERR, "Could not allocate memory for strlist in getNext_BatchDirectory()\n");
144             exit(-2);
145         }
146         strlist_push_back(dir_q, config.target);
147     }
148
149     while (dir_q->count > 0 || dirfd != NULL) {
150         if (dirfd == NULL) {
151             char *data = strlist_get(dir_q, 0);
152             if ( data[ strlen(data)-1 ] != '/' )
153                 snprintf(base_dir, sizeof(base_dir), "%s/", data);
154             else
155                 strncpy(base_dir, data, sizeof(base_dir));
156             strlist_pop_front(dir_q);
157
158             dirfd = opendir(base_dir);
159             if (dirfd == NULL) {
160                 dlog(ERR, "Could not open directory %s: %s\n", base_dir, strerror(errno));
161                 break;
162             }
163         }
164
165         while ( (d_entry = readdir(dirfd)) != NULL) {
166             if (strcmp(d_entry->d_name, ".") == 0) continue;
167             if (strcmp(d_entry->d_name, "..") == 0) continue;
168
169             /*
170              * Generate target filename in config.target from base_dir and d_entry->d_name.
171              */
172             snprintf(config.target, sizeof(config.target), "%s%s", base_dir, d_entry->d_name);
173             config.argv[0] = strrchr(config.target, '/') + 1;   /* strrchr() will never return NULL
174                                                                    because we control base_dir. */
175
176             /*
177              * Check if target filename is a directory, or executable via stat().  Ignore if neither.
178              */
179             struct stat file_stat;
180             i = 0;
181             do {
182                 if (i++ > 10) break;
183                 errno = 0;
184             } while (stat(config.target, &file_stat) != 0 && errno == EINTR);
185
186             if (errno == EINTR) {
187                 dlog(ERR, "Skipping %s.  stat() interrupted 10 times.\n", config.target);
188                 continue;
189
190             } else if (errno) {
191                 dlog(ERR, "Skipping %s.  Error on stat(): %s\n", config.target, strerror(errno));
192                 continue;
193             }
194
195             if ((file_stat.st_mode & S_IFMT) == S_IFDIR) {
196                 if (config.recursive)
197                     strlist_push_back(dir_q, config.target);
198                 else
199                     dlog(INFO, "%s is a directory.\n", config.target);
200
201             } else if ((file_stat.st_mode & S_IXOTH) == S_IXOTH ||
202                        (file_stat.st_mode & S_IXGRP) == S_IXGRP ||
203                        (file_stat.st_mode & S_IXUSR) == S_IXUSR) {
204                 if (validate_file(config.target)) return true;
205
206             } else {
207                 dlog(VERB1, "Skipping %s.  Not executable.\n", config.target);
208             }
209         }
210
211         if (errno != 0) {
212             dlog(ERR, "Error on readdir(): %s\n", strerror(errno));
213             dirfd = NULL;
214             continue;
215         }
216
217         /* Done processing directory.  Close directory descriptor. */
218         i = 0;
219         while (closedir(dirfd) != 0) {
220             if (++i > 10) {
221                 dlog(ERR, "Warning: closedir() failed 10 times.  Ignoring open descriptor.\n");
222                 break;
223             }
224         }
225         dirfd = NULL;
226     }
227     return false;
228 }
229
230 bool getNext_BatchFile()
231 {
232     static unsigned maxArgc = 0;
233     static FILE *filefd = NULL;
234     strlist cmdline = STRLIST_INITIALIZER;
235
236     unsigned i = 0;
237     if (filefd == NULL) {
238         errno = 0;
239         filefd = fopen(config.config_file, "r");
240         if (filefd == NULL && errno != EINTR) {
241             dlog(ERR, "Could not open batch config file %s: %s\n", config.config_file, strerror(errno));
242             exit(-2);
243         }
244         if (++i > 10) {
245             dlog(ERR, "open() for %s has been interrupted 10 times.  Try again later.\n", config.config_file);
246             exit(-2);
247         }
248         config.argv = NULL;
249     }
250
251     char *buf;
252     while ( (buf = fgets_static(filefd))) {
253         strlist_clear(&cmdline);
254         cmdline = char2strlist(buf);
255
256         strncpy(config.target, strlist_get(&cmdline, 0), sizeof(config.target));
257
258         if (maxArgc < (cmdline.count + 1)) {
259             char **newargv = (char **)realloc(config.argv, (cmdline.count + 1) * sizeof(char *));
260             if (!newargv) {
261                 dlog(ERR, "Could not allocate memory for argv array in getNext_BatchFile().\n");
262                 exit(-2);
263             }
264             config.argv = newargv;
265         }
266         for (i = 0; i < cmdline.count; ++i)
267             config.argv[i] = strlist_get(&cmdline, i);
268         config.argv[i] = NULL;
269
270         if (strrchr(config.argv[0], '/'))
271             config.argv[0] = strrchr(config.argv[0], '/') + 1;
272
273         if (validate_file(config.target)) return true;
274     }
275     return false;
276 }