1 /**
2 * @file oprof_start_util.cpp
3 * Miscellaneous helpers for the GUI start
4 *
5 * @remark Copyright 2002 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author Philippe Elie
9 * @author John Levon
10 */
11
12 #include <dirent.h>
13 #include <unistd.h>
14 #include <glob.h>
15
16 #include <cerrno>
17 #include <vector>
18 #include <cmath>
19 #include <sstream>
20 #include <iostream>
21 #include <fstream>
22 #include <cstdlib>
23
24 #include <qfiledialog.h>
25 #include <qmessagebox.h>
26
27 #include "op_file.h"
28 #include "file_manip.h"
29 #include "child_reader.h"
30 #include "op_libiberty.h"
31
32 #include "oprof_start.h"
33 #include "oprof_start_util.h"
34
35 using namespace std;
36
37 namespace {
38
39 // return the ~ expansion suffixed with a '/'
get_config_dir()40 string const get_config_dir()
41 {
42 return "/root";
43 }
44
45 string daemon_pid;
46
47 } // namespace anon
48
daemon_status()49 daemon_status::daemon_status()
50 : running(false),
51 nr_interrupts(0)
52 {
53 int HZ;
54 if (!daemon_pid.empty()) {
55 string proc_filename = string("/proc/") + daemon_pid + "/exe";
56 string const exec = op_realpath(proc_filename);
57 if (exec == proc_filename)
58 daemon_pid.erase();
59 else
60 running = true;
61 }
62
63 if (daemon_pid.empty()) {
64 DIR * dir;
65 struct dirent * dirent;
66
67 if (!(dir = opendir("/proc"))) {
68 perror("oprofiled: /proc directory could not be opened. ");
69 exit(EXIT_FAILURE);
70 }
71
72 while ((dirent = readdir(dir))) {
73 string const exec =
74 op_realpath(string("/proc/")
75 + dirent->d_name + "/exe");
76 string const name = op_basename(exec);
77 if (name != "oprofiled")
78 continue;
79
80 daemon_pid = dirent->d_name;
81 running = true;
82 }
83
84 closedir(dir);
85 }
86
87 HZ = sysconf(_SC_CLK_TCK);
88 if (HZ == -1) {
89 perror("oprofiled: Unable to determine clock ticks per second. ");
90 exit(EXIT_FAILURE);
91 }
92
93 if (daemon_pid.empty())
94 return;
95
96 nr_interrupts = 0;
97
98 switch (op_get_interface()) {
99 case OP_INTERFACE_24:
100 {
101 ifstream ifs3("/proc/sys/dev/oprofile/nr_interrupts");
102 if (ifs3)
103 ifs3 >> nr_interrupts;
104 }
105 break;
106 case OP_INTERFACE_26:
107 {
108 static unsigned int old_sum_interrupts;
109 unsigned int sum_interrupts = 0;
110 glob_t file_names;
111
112 file_names.gl_offs = 0;
113 glob("/dev/oprofile/stats/cpu*/sample_received",
114 GLOB_DOOFFS, NULL, &file_names);
115
116 for (size_t i = 0; i < file_names.gl_pathc; ++i) {
117 ifstream ifs3(file_names.gl_pathv[i]);
118 if (ifs3) {
119 unsigned int file_interrupts;
120 ifs3 >> file_interrupts;
121 sum_interrupts += file_interrupts;
122 }
123 }
124 if (old_sum_interrupts > sum_interrupts)
125 // occur if we stop/restart daemon.
126 old_sum_interrupts = 0;
127 nr_interrupts = sum_interrupts - old_sum_interrupts;
128 old_sum_interrupts = sum_interrupts;
129 globfree(&file_names);
130 }
131 break;
132 default:
133 break;
134 }
135 }
136
137
138 /**
139 * get_config_filename - get absolute filename of file in user $HOME
140 * @param filename the relative filename
141 *
142 * Get the absolute path of a file in a user's home directory.
143 */
get_config_filename(string const & filename)144 string const get_config_filename(string const & filename)
145 {
146 return get_config_dir() + "/" + filename;
147 }
148
149
150 /**
151 * check_and_create_config_dir - make sure config dir is accessible
152 *
153 * Returns %true if the dir is accessible.
154 */
check_and_create_config_dir()155 bool check_and_create_config_dir()
156 {
157 string dir = get_config_filename(".oprofile");
158
159 char * name = xstrdup(dir.c_str());
160
161 if (create_dir(name)) {
162 ostringstream out;
163 out << "unable to create " << dir << " directory ";
164 out << "cause: " << strerror(errno);
165 QMessageBox::warning(0, 0, out.str().c_str());
166
167 free(name);
168
169 return false;
170 }
171
172 free(name);
173 return true;
174 }
175
176
177 /**
178 * format - re-format a string
179 * @param orig string to format
180 * @param maxlen width of line
181 *
182 * Re-formats a string to fit into a certain width,
183 * breaking lines at spaces between words.
184 *
185 * Returns the formatted string
186 */
format(string const & orig,uint const maxlen)187 string const format(string const & orig, uint const maxlen)
188 {
189 string text(orig);
190
191 istringstream ss(text);
192 vector<string> lines;
193
194 string oline;
195 string line;
196
197 while (getline(ss, oline)) {
198 if (line.size() + oline.size() < maxlen) {
199 lines.push_back(line + oline);
200 line.erase();
201 } else {
202 lines.push_back(line);
203 line.erase();
204 string s;
205 string word;
206 istringstream oss(oline);
207 while (oss >> word) {
208 if (line.size() + word.size() > maxlen) {
209 lines.push_back(line);
210 line.erase();
211 }
212 line += word + " ";
213 }
214 }
215 }
216
217 if (line.size())
218 lines.push_back(line);
219
220 string ret;
221
222 for(vector<string>::const_iterator it = lines.begin(); it != lines.end(); ++it)
223 ret += *it + "\n";
224
225 return ret;
226 }
227
228
229 /**
230 * do_exec_command - execute a command
231 * @param cmd command name
232 * @param args arguments to command
233 *
234 * Execute a command synchronously. An error message is shown
235 * if the command returns a non-zero status, which is also returned.
236 *
237 * The arguments are verified and will refuse to execute if they contain
238 * shell metacharacters.
239 */
do_exec_command(string const & cmd,vector<string> const & args)240 int do_exec_command(string const & cmd, vector<string> const & args)
241 {
242 ostringstream err;
243 bool ok = true;
244
245 // verify arguments
246 for (vector<string>::const_iterator cit = args.begin();
247 cit != args.end(); ++cit) {
248 if (verify_argument(*cit))
249 continue;
250
251 QMessageBox::warning(0, 0,
252 string(
253 "Could not execute: Argument \"" + *cit +
254 "\" contains shell metacharacters.\n").c_str());
255 return EINVAL;
256 }
257
258 child_reader reader(cmd, args);
259 if (reader.error())
260 ok = false;
261
262 if (ok)
263 reader.get_data(cout, err);
264
265 int ret = reader.terminate_process();
266 if (ret) {
267 string error = reader.error_str() + "\n";
268 error += "Failed: \n" + err.str() + "\n";
269 string cmdline = cmd;
270 for (vector<string>::const_iterator cit = args.begin();
271 cit != args.end(); ++cit) {
272 cmdline += " " + *cit + " ";
273 }
274 error += "\n\nCommand was :\n\n" + cmdline + "\n";
275
276 QMessageBox::warning(0, 0, format(error, 50).c_str());
277 }
278
279 return ret;
280 }
281
282
283 /**
284 * do_open_file_or_dir - open file/directory
285 * @param base_dir directory to start at
286 * @param dir_only directory or filename to select
287 *
288 * Select a file or directory. The selection is returned;
289 * an empty string if the selection was cancelled.
290 */
do_open_file_or_dir(string const & base_dir,bool dir_only)291 string const do_open_file_or_dir(string const & base_dir, bool dir_only)
292 {
293 QString result;
294
295 if (dir_only) {
296 result = QFileDialog::getExistingDirectory(base_dir.c_str(), 0,
297 "open_file_or_dir", "Get directory name", true);
298 } else {
299 result = QFileDialog::getOpenFileName(base_dir.c_str(), 0, 0,
300 "open_file_or_dir", "Get filename");
301 }
302
303 if (result.isNull())
304 return string();
305 else
306 return result.latin1();
307 }
308
309 /**
310 * verify_argument - check string for potentially dangerous characters
311 *
312 * This function returns false if the string contains dangerous shell
313 * metacharacters.
314 *
315 * WWW Security FAQ dangerous chars:
316 *
317 * & ; ` ' \ " | * ? ~ < > ^ ( ) [ ] { } $ \n \r
318 *
319 * David Wheeler: ! #
320 *
321 * We allow '-' because we disallow whitespace. We allow ':' and '='
322 */
verify_argument(string const & str)323 bool verify_argument(string const & str)
324 {
325 if (str.find_first_not_of(
326 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
327 "abcdefghijklmnopqrstuvwxyz0123456789_:=-+%,./")
328 != string::npos)
329 return false;
330 return true;
331 }
332