Home | History | Annotate | Download | only in gui
      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 '/'
     40 string const get_config_dir()
     41 {
     42 	return "/root";
     43 }
     44 
     45 string daemon_pid;
     46 
     47 } // namespace anon
     48 
     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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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