/**
* @file oprof_start_util.cpp
* Miscellaneous helpers for the GUI start
*
* @remark Copyright 2002 OProfile authors
* @remark Read the file COPYING
*
* @author Philippe Elie
* @author John Levon
*/
#include <dirent.h>
#include <unistd.h>
#include <glob.h>
#include <cerrno>
#include <vector>
#include <cmath>
#include <sstream>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <qfiledialog.h>
#include <qmessagebox.h>
#include "op_file.h"
#include "file_manip.h"
#include "child_reader.h"
#include "op_libiberty.h"
#include "oprof_start.h"
#include "oprof_start_util.h"
using namespace std;
namespace {
// return the ~ expansion suffixed with a '/'
string const get_config_dir()
{
return "/root";
}
string daemon_pid;
} // namespace anon
daemon_status::daemon_status()
: running(false),
nr_interrupts(0)
{
int HZ;
if (!daemon_pid.empty()) {
string proc_filename = string("/proc/") + daemon_pid + "/exe";
string const exec = op_realpath(proc_filename);
if (exec == proc_filename)
daemon_pid.erase();
else
running = true;
}
if (daemon_pid.empty()) {
DIR * dir;
struct dirent * dirent;
if (!(dir = opendir("/proc"))) {
perror("oprofiled: /proc directory could not be opened. ");
exit(EXIT_FAILURE);
}
while ((dirent = readdir(dir))) {
string const exec =
op_realpath(string("/proc/")
+ dirent->d_name + "/exe");
string const name = op_basename(exec);
if (name != "oprofiled")
continue;
daemon_pid = dirent->d_name;
running = true;
}
closedir(dir);
}
HZ = sysconf(_SC_CLK_TCK);
if (HZ == -1) {
perror("oprofiled: Unable to determine clock ticks per second. ");
exit(EXIT_FAILURE);
}
if (daemon_pid.empty())
return;
nr_interrupts = 0;
switch (op_get_interface()) {
case OP_INTERFACE_24:
{
ifstream ifs3("/proc/sys/dev/oprofile/nr_interrupts");
if (ifs3)
ifs3 >> nr_interrupts;
}
break;
case OP_INTERFACE_26:
{
static unsigned int old_sum_interrupts;
unsigned int sum_interrupts = 0;
glob_t file_names;
file_names.gl_offs = 0;
glob("/dev/oprofile/stats/cpu*/sample_received",
GLOB_DOOFFS, NULL, &file_names);
for (size_t i = 0; i < file_names.gl_pathc; ++i) {
ifstream ifs3(file_names.gl_pathv[i]);
if (ifs3) {
unsigned int file_interrupts;
ifs3 >> file_interrupts;
sum_interrupts += file_interrupts;
}
}
if (old_sum_interrupts > sum_interrupts)
// occur if we stop/restart daemon.
old_sum_interrupts = 0;
nr_interrupts = sum_interrupts - old_sum_interrupts;
old_sum_interrupts = sum_interrupts;
globfree(&file_names);
}
break;
default:
break;
}
}
/**
* get_config_filename - get absolute filename of file in user $HOME
* @param filename the relative filename
*
* Get the absolute path of a file in a user's home directory.
*/
string const get_config_filename(string const & filename)
{
return get_config_dir() + "/" + filename;
}
/**
* check_and_create_config_dir - make sure config dir is accessible
*
* Returns %true if the dir is accessible.
*/
bool check_and_create_config_dir()
{
string dir = get_config_filename(".oprofile");
char * name = xstrdup(dir.c_str());
if (create_dir(name)) {
ostringstream out;
out << "unable to create " << dir << " directory ";
out << "cause: " << strerror(errno);
QMessageBox::warning(0, 0, out.str().c_str());
free(name);
return false;
}
free(name);
return true;
}
/**
* format - re-format a string
* @param orig string to format
* @param maxlen width of line
*
* Re-formats a string to fit into a certain width,
* breaking lines at spaces between words.
*
* Returns the formatted string
*/
string const format(string const & orig, uint const maxlen)
{
string text(orig);
istringstream ss(text);
vector<string> lines;
string oline;
string line;
while (getline(ss, oline)) {
if (line.size() + oline.size() < maxlen) {
lines.push_back(line + oline);
line.erase();
} else {
lines.push_back(line);
line.erase();
string s;
string word;
istringstream oss(oline);
while (oss >> word) {
if (line.size() + word.size() > maxlen) {
lines.push_back(line);
line.erase();
}
line += word + " ";
}
}
}
if (line.size())
lines.push_back(line);
string ret;
for(vector<string>::const_iterator it = lines.begin(); it != lines.end(); ++it)
ret += *it + "\n";
return ret;
}
/**
* do_exec_command - execute a command
* @param cmd command name
* @param args arguments to command
*
* Execute a command synchronously. An error message is shown
* if the command returns a non-zero status, which is also returned.
*
* The arguments are verified and will refuse to execute if they contain
* shell metacharacters.
*/
int do_exec_command(string const & cmd, vector<string> const & args)
{
ostringstream err;
bool ok = true;
// verify arguments
for (vector<string>::const_iterator cit = args.begin();
cit != args.end(); ++cit) {
if (verify_argument(*cit))
continue;
QMessageBox::warning(0, 0,
string(
"Could not execute: Argument \"" + *cit +
"\" contains shell metacharacters.\n").c_str());
return EINVAL;
}
child_reader reader(cmd, args);
if (reader.error())
ok = false;
if (ok)
reader.get_data(cout, err);
int ret = reader.terminate_process();
if (ret) {
string error = reader.error_str() + "\n";
error += "Failed: \n" + err.str() + "\n";
string cmdline = cmd;
for (vector<string>::const_iterator cit = args.begin();
cit != args.end(); ++cit) {
cmdline += " " + *cit + " ";
}
error += "\n\nCommand was :\n\n" + cmdline + "\n";
QMessageBox::warning(0, 0, format(error, 50).c_str());
}
return ret;
}
/**
* do_open_file_or_dir - open file/directory
* @param base_dir directory to start at
* @param dir_only directory or filename to select
*
* Select a file or directory. The selection is returned;
* an empty string if the selection was cancelled.
*/
string const do_open_file_or_dir(string const & base_dir, bool dir_only)
{
QString result;
if (dir_only) {
result = QFileDialog::getExistingDirectory(base_dir.c_str(), 0,
"open_file_or_dir", "Get directory name", true);
} else {
result = QFileDialog::getOpenFileName(base_dir.c_str(), 0, 0,
"open_file_or_dir", "Get filename");
}
if (result.isNull())
return string();
else
return result.latin1();
}
/**
* verify_argument - check string for potentially dangerous characters
*
* This function returns false if the string contains dangerous shell
* metacharacters.
*
* WWW Security FAQ dangerous chars:
*
* & ; ` ' \ " | * ? ~ < > ^ ( ) [ ] { } $ \n \r
*
* David Wheeler: ! #
*
* We allow '-' because we disallow whitespace. We allow ':' and '='
*/
bool verify_argument(string const & str)
{
if (str.find_first_not_of(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789_:=-+%,./")
!= string::npos)
return false;
return true;
}