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