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