00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "process_entry.h"
00029 #include "process_control.h"
00030
00031 #include <application/windoze_helper.h>
00032 #include <basis/astring.h>
00033 #include <basis/utf_conversion.h>
00034 #include <configuration/application_configuration.h>
00035 #include <filesystem/filename.h>
00036 #include <loggers/program_wide_logger.h>
00037 #include <loggers/standard_log_base.h>
00038 #include <mathematics/chaos.h>
00039 #include <structures/set.h>
00040 #include <structures/version_record.h>
00041
00042 #include <stdio.h>
00043 #include <stdlib.h>
00044 #ifdef __UNIX__
00045 #include <unistd.h>
00046 #endif
00047
00048 using namespace basis;
00049 using namespace configuration;
00050 using namespace filesystem;
00051 using namespace loggers;
00052 using namespace mathematics;
00053 using namespace structures;
00054
00055 namespace processes {
00056
00057 #ifdef __WIN32__
00058 #include <tlhelp32.h>
00059 const astring NTVDM_NAME = "ntvdm.exe";
00060
00061 #ifdef _MSCVER
00062 #include <vdmdbg.h>
00063 #endif
00064 #endif
00065 #ifdef __UNIX__
00066 #include <signal.h>
00067 #include <stdio.h>
00068 #endif
00069
00070
00071
00072
00073 #undef LOG
00074 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
00075
00077
00078 class process_implementation_hider
00079 {
00080 public:
00081 #ifdef __WIN32__
00082
00083 application_instance psapi_dll;
00084 application_instance vdm_dll;
00085 BOOL (WINAPI *enumerate_processes)(basis::un_int *, basis::un_int cb, basis::un_int *);
00086 BOOL (WINAPI *enumerate_modules)(HANDLE, HMODULE *, basis::un_int, basis::un_int *);
00087 basis::un_int (WINAPI *get_module_name)(HANDLE, HMODULE, LPTSTR, basis::un_int);
00088 #ifdef _MSCVER
00089 INT (WINAPI *tasker_16bit)(basis::un_int, TASKENUMPROCEX fp, LPARAM);
00090 #endif
00091
00092
00093 application_instance kernel32_dll;
00094 HANDLE (WINAPI *create_snapshot)(basis::un_int,basis::un_int);
00095 BOOL (WINAPI *first_process)(HANDLE,LPPROCESSENTRY32);
00096 BOOL (WINAPI *next_process)(HANDLE,LPPROCESSENTRY32);
00097
00098
00100
00101 process_implementation_hider()
00102 : psapi_dll(NIL), vdm_dll(NIL), enumerate_processes(NIL),
00103 enumerate_modules(NIL), get_module_name(NIL),
00104 #ifdef _MSCVER
00105 tasker_16bit(NIL),
00106 #endif
00107 kernel32_dll(NIL), create_snapshot(NIL), first_process(NIL),
00108 next_process(NIL) {}
00109
00110 ~process_implementation_hider() {
00111 if (psapi_dll) FreeLibrary(psapi_dll);
00112 if (vdm_dll) FreeLibrary(vdm_dll);
00113 if (kernel32_dll) FreeLibrary(kernel32_dll);
00114 psapi_dll = NIL;
00115 vdm_dll = NIL;
00116 kernel32_dll = NIL;
00117 }
00118 #endif
00119 };
00120
00122
00123 class process_info_clump
00124 {
00125 public:
00126 basis::un_int _process_id;
00127 process_entry_array &_to_fill;
00128
00129 process_info_clump(basis::un_int id, process_entry_array &to_fill)
00130 : _process_id(id), _to_fill(to_fill) {}
00131 };
00132
00134
00135
00136
00137 process_control::process_control()
00138 : _ptrs(new process_implementation_hider),
00139 #ifdef __WIN32__
00140 _use_psapi(true),
00141 #endif
00142 #ifdef __UNIX__
00143 _rando(new chaos),
00144 #endif
00145 _healthy(false)
00146 {
00147
00148 version osver = application_configuration::get_OS_version();
00149
00150 #ifdef __WIN32__
00151 if (osver.v_revision() == VER_PLATFORM_WIN32_WINDOWS) {
00152
00153 _use_psapi = false;
00154 } else if (osver.v_major() >= 5) {
00155
00156 _use_psapi = false;
00157 }
00158 if (_use_psapi)
00159 _healthy = initialize_psapi_support();
00160 else
00161 _healthy = initialize_toolhelp_support();
00162 #endif
00163 #ifdef __UNIX__
00164 _healthy = true;
00165 #endif
00166 }
00167
00168 process_control::~process_control()
00169 {
00170 WHACK(_ptrs);
00171 #ifdef __UNIX__
00172 WHACK(_rando);
00173 #endif
00174 }
00175
00176 void process_control::sort_by_name(process_entry_array &v)
00177 {
00178 process_entry temp;
00179 for (int gap = v.length() / 2; gap > 0; gap /= 2)
00180 for (int i = gap; i < v.length(); i++)
00181 for (int j = i - gap; j >= 0
00182 && (filename(v[j].path()).basename().raw()
00183 > filename(v[j + gap].path()).basename().raw());
00184 j = j - gap)
00185 { temp = v[j]; v[j] = v[j + gap]; v[j + gap] = temp; }
00186 }
00187
00188 void process_control::sort_by_pid(process_entry_array &v)
00189 {
00190 process_entry temp;
00191 for (int gap = v.length() / 2; gap > 0; gap /= 2)
00192 for (int i = gap; i < v.length(); i++)
00193 for (int j = i - gap; j >= 0 && (v[j]._process_id
00194 > v[j + gap]._process_id); j = j - gap)
00195 { temp = v[j]; v[j] = v[j + gap]; v[j + gap] = temp; }
00196 }
00197
00198 bool process_control::query_processes(process_entry_array &to_fill)
00199 {
00200 if (!_healthy) return false;
00201 #ifdef __WIN32__
00202 if (!_use_psapi) {
00203
00204
00205 return get_processes_with_toolhelp(to_fill);
00206 } else {
00207
00208 return get_processes_with_psapi(to_fill);
00209 }
00210 #endif
00211 #ifdef __UNIX__
00212 return get_processes_with_ps(to_fill);
00213 #endif
00214 }
00215
00216 #ifdef __WIN32__
00217 bool process_control::initialize_psapi_support()
00218 {
00219
00220
00221
00222 _ptrs->psapi_dll = LoadLibraryA("psapi.dll");
00223 if (!_ptrs->psapi_dll) return false;
00224 _ptrs->vdm_dll = LoadLibraryA("vdmdbg.dll");
00225 if (!_ptrs->vdm_dll) return false;
00226
00227
00228 _ptrs->enumerate_processes = (BOOL(WINAPI *)(basis::un_int *,basis::un_int,basis::un_int*))
00229 GetProcAddress(_ptrs->psapi_dll, "EnumProcesses");
00230 _ptrs->enumerate_modules
00231 = (BOOL(WINAPI *)(HANDLE, HMODULE *, basis::un_int, basis::un_int *))
00232 GetProcAddress(_ptrs->psapi_dll, "EnumProcessModules");
00233 _ptrs->get_module_name
00234 = (basis::un_int (WINAPI *)(HANDLE, HMODULE, LPTSTR, basis::un_int))
00235 GetProcAddress(_ptrs->psapi_dll, "GetModuleFileNameExA");
00236 #ifdef _MSCVER
00237 _ptrs->tasker_16bit = (INT(WINAPI *)(basis::un_int, TASKENUMPROCEX, LPARAM))
00238 GetProcAddress(_ptrs->vdm_dll, "VDMEnumTaskWOWEx");
00239 #endif
00240 if (!_ptrs->enumerate_processes || !_ptrs->enumerate_modules
00241 || !_ptrs->get_module_name
00242 #ifdef _MSCVER
00243 || !_ptrs->tasker_16bit
00244 #endif
00245 ) return false;
00246
00247 return true;
00248 }
00249
00250 bool process_control::initialize_toolhelp_support()
00251 {
00252
00253 _ptrs->kernel32_dll = LoadLibraryA("Kernel32.DLL");
00254 if (!_ptrs->kernel32_dll) return false;
00255
00256
00257 _ptrs->create_snapshot = (HANDLE(WINAPI *)(basis::un_int,basis::un_int))
00258 GetProcAddress(_ptrs->kernel32_dll, "CreateToolhelp32Snapshot");
00259 _ptrs->first_process = (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
00260 GetProcAddress(_ptrs->kernel32_dll, "Process32First");
00261 _ptrs->next_process = (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
00262 GetProcAddress(_ptrs->kernel32_dll, "Process32Next");
00263 if (!_ptrs->next_process || !_ptrs->first_process
00264 || !_ptrs->create_snapshot) return false;
00265 return true;
00266 }
00267
00268 #endif
00269
00270 bool process_control::zap_process(basis::un_int to_zap)
00271 {
00272 #ifdef DEBUG_PROCESS_CONTROL
00273 FUNCDEF("zap_process");
00274 #endif
00275 if (!_healthy) return false;
00276 #ifdef __UNIX__
00277 int ret = kill(to_zap, 9);
00278
00279 return !ret;
00280 #endif
00281 #ifdef __WIN32__
00282 HANDLE h = OpenProcess(PROCESS_TERMINATE, false, to_zap);
00283 if (!h) {
00284 #ifdef DEBUG_PROCESS_CONTROL
00285 int err = critical_events::system_error();
00286 LOG(a_sprintf("error zapping process %d=", to_zap)
00287 + critical_events::system_error_text(err));
00288 #endif
00289 return false;
00290 }
00291 int exit_code = 0;
00292 BOOL ret = TerminateProcess(h, exit_code);
00293 CloseHandle(h);
00294 return !!ret;
00295 #endif
00296 }
00297
00298 process_entry process_control::query_process(basis::un_int to_query)
00299 {
00300
00301 process_entry to_return;
00302
00303 process_entry_array to_fill;
00304 bool got_em = query_processes(to_fill);
00305 if (!got_em) return to_return;
00306
00307 for (int i = 0; i < to_fill.length(); i++) {
00308 if (to_fill[i]._process_id == to_query)
00309 return to_fill[i];
00310 }
00311
00312
00313 #ifdef __UNIX__
00314
00315 #endif
00316 #ifdef __WIN32__
00317
00318 #endif
00319
00320 return to_return;
00321 }
00322
00323 bool process_control::find_process_in_list(const process_entry_array &processes,
00324 const astring &app_name_in, int_set &pids)
00325 {
00326 #ifdef DEBUG_PROCESS_CONTROL
00327 FUNCDEF("find_process_in_list");
00328 #endif
00329 pids.clear();
00330 astring app_name = app_name_in.lower();
00331
00332 version os_ver = application_configuration::get_OS_version();
00333
00334 bool compare_prefix = (os_ver.v_major() == 5) && (os_ver.v_minor() == 0);
00335
00336
00337
00338
00339 bool found = false;
00340 for (int i = 0; i < processes.length(); i++) {
00341 filename path = processes[i].path();
00342 astring base = path.basename().raw().lower();
00343
00344
00345
00346 if ( (compare_prefix && (base.compare(app_name, 0, 0, 15, false)))
00347 || (base == app_name) ) {
00348 found = true;
00349 pids.add(processes[i]._process_id);
00350 }
00351 }
00352 #ifdef DEBUG_PROCESS_CONTROL
00353 if (!found)
00354 LOG(astring("failed to find the program called ") + app_name);
00355 #endif
00356 return found;
00357 }
00358
00360
00361 #ifdef __WIN32__
00362
00363
00364
00365 BOOL WINAPI process_16bit(basis::un_int dwThreadId, WORD module_handle16, WORD hTask16,
00366 PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined)
00367 {
00368 process_info_clump *to_stuff = (process_info_clump *)lpUserDefined;
00369 process_entry to_add;
00370 to_add._process_id = to_stuff->_process_id;
00371 to_add._module16 = hTask16;
00372 to_add.path(pszFileName);
00373
00374 to_stuff->_to_fill += to_add;
00375 return true;
00376 }
00377
00378 bool process_control::get_processes_with_psapi(process_entry_array &to_fill)
00379 {
00380
00381 to_fill.reset();
00382
00383
00384
00385 bool got_all = false;
00386 basis::un_int *pid_list = NIL;
00387 basis::un_int max_size = 428 * sizeof(basis::un_int);
00388 basis::un_int actual_size = 0;
00389 while (!got_all) {
00390 pid_list = (basis::un_int *)HeapAlloc(GetProcessHeap(), 0, max_size);
00391 if (!pid_list) return false;
00392 if (!_ptrs->enumerate_processes(pid_list, max_size, &actual_size)) {
00393 HeapFree(GetProcessHeap(), 0, pid_list);
00394 return false;
00395 }
00396 if (actual_size == max_size) {
00397
00398 HeapFree(GetProcessHeap(), 0, pid_list);
00399 max_size *= 2;
00400 } else got_all = true;
00401 }
00402
00403
00404 basis::un_int ids = actual_size / sizeof(basis::un_int);
00405
00406
00407 for (basis::un_int i = 0; i < ids; i++) {
00408
00409
00410 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
00411 false, pid_list[i]);
00412 flexichar process_name[MAX_ABS_PATH + 1] = { '\0' };
00413 if (hProcess) {
00414
00415
00416 basis::un_int max_size = 1 * sizeof(HMODULE);
00417
00418 basis::un_int actual_size = 0;
00419 HMODULE *module_handles = new HMODULE[max_size + 1];
00420 if (!module_handles) {
00421 CloseHandle(hProcess);
00422 HeapFree(GetProcessHeap(), 0, pid_list);
00423 return false;
00424 }
00425 if (_ptrs->enumerate_modules(hProcess, module_handles, max_size,
00426 &actual_size)) {
00427
00428 if (!_ptrs->get_module_name(hProcess, *module_handles, process_name,
00429 sizeof(process_name)))
00430 process_name[0] = 0;
00431 }
00432 WHACK(module_handles);
00433 CloseHandle(hProcess);
00434 }
00435
00436
00437 process_entry new_entry;
00438 new_entry._process_id = pid_list[i];
00439 astring converted_name = from_unicode_temp(process_name);
00440 new_entry.path(converted_name);
00441
00442
00444 to_fill += new_entry;
00445
00446
00447
00448 if (new_entry.path().length() >= NTVDM_NAME.length()) {
00449 astring temp = new_entry.path().substring
00450 (new_entry.path().end() - NTVDM_NAME.length() + 1,
00451 new_entry.path().end());
00452 temp.to_lower();
00453 #ifdef _MSCVER
00454
00455 if (temp == NTVDM_NAME) {
00456
00457 process_info_clump info(pid_list[i], to_fill);
00458 _ptrs->tasker_16bit(pid_list[i], (TASKENUMPROCEX)process_16bit,
00459 (LPARAM)&info);
00460 }
00461 #endif
00462 }
00463 }
00464
00465 if (pid_list) HeapFree(GetProcessHeap(), 0, pid_list);
00466 return true;
00467 }
00468
00470
00471
00472
00473 bool process_control::get_processes_with_toolhelp(process_entry_array &to_fill)
00474 {
00475
00476 to_fill.reset();
00477
00478
00479 HANDLE hSnapShot;
00480 hSnapShot = _ptrs->create_snapshot(TH32CS_SNAPPROCESS, 0);
00481 if (hSnapShot == INVALID_HANDLE_VALUE) return false;
00482
00483
00484 PROCESSENTRY32 entry;
00485 entry.dwSize = sizeof(PROCESSENTRY32);
00486 BOOL keep_going = _ptrs->first_process(hSnapShot, &entry);
00487
00488
00489 while (keep_going) {
00490
00491 process_entry new_entry;
00492 new_entry._process_id = entry.th32ProcessID;
00493 new_entry._references = entry.cntUsage;
00494 new_entry._threads = entry.cntThreads;
00495 new_entry._parent_process_id = entry.th32ParentProcessID;
00496 astring exe_file = from_unicode_temp(entry.szExeFile);
00497 new_entry.path(exe_file);
00498 to_fill += new_entry;
00499 entry.dwSize = sizeof(PROCESSENTRY32);
00500 keep_going = _ptrs->next_process(hSnapShot, &entry);
00501 }
00502
00503 CloseHandle(hSnapShot);
00504 return true;
00505 }
00506 #endif // __WIN32__
00507
00508 #ifdef __UNIX__
00509
00510 #define CLOSE_TMP_FILE { \
00511 \
00512 if (output) { \
00513 fclose(output); \
00514 unlink(tmpfile.s()); \
00515 } \
00516 }
00517
00518 bool process_control::get_processes_with_ps(process_entry_array &to_fill)
00519 {
00520 FUNCDEF("get_processes_with_ps");
00521 to_fill.reset();
00522
00523 a_sprintf tmpfile("/tmp/proc_list_%d_%d.txt", application_configuration::process_id(),
00524 _rando->inclusive(1, 400000));
00525 a_sprintf cmd("ps wax --format \"%%p %%a\" >%s", tmpfile.s());
00526
00527 FILE *output = NIL;
00528 int sysret = system(cmd.s());
00529 if (negative(sysret)) {
00530 LOG("got negative return from system()!");
00531 CLOSE_TMP_FILE;
00532 return false;
00533 }
00534 output = fopen(tmpfile.s(), "r");
00535 if (!output) {
00536 LOG("failed to open process list file!");
00537 CLOSE_TMP_FILE;
00538 return false;
00539 }
00540 const int max_buff = 10000;
00541 char buff[max_buff];
00542 size_t size_read = 1;
00543 astring accumulator;
00544 while (size_read > 0) {
00545
00546 size_read = fread(buff, 1, max_buff, output);
00547
00548 if (size_read > 0)
00549 accumulator += astring(astring::UNTERMINATED, buff, size_read);
00550 }
00551 CLOSE_TMP_FILE;
00552
00553 bool first_line = true;
00554 while (accumulator.length()) {
00555
00556 if (first_line) {
00557
00558 int cr_indy = accumulator.find('\n');
00559 accumulator.zap(0, cr_indy);
00560 if (accumulator[accumulator.end()] == '\r')
00561 accumulator.zap(accumulator.end(), accumulator.end());
00562 first_line = false;
00563 continue;
00564 }
00565 while (accumulator.length() && (accumulator[0] == ' '))
00566 accumulator.zap(0, 0);
00567
00568 int num_indy = accumulator.find(' ');
00569 if (negative(num_indy)) break;
00570 basis::un_int p_id = accumulator.substring(0, num_indy).convert(0);
00571 accumulator.zap(0, num_indy);
00572 int cr_indy = accumulator.find('\n');
00573 if (negative(cr_indy))
00574 cr_indy = accumulator.end() + 1;
00575 astring proc_name = accumulator.substring(0, cr_indy - 1);
00576 if (proc_name[proc_name.end()] == '\r')
00577 proc_name.zap(proc_name.end(), proc_name.end());
00578 accumulator.zap(0, cr_indy);
00579 int space_indy = proc_name.find(' ');
00580
00581 if (negative(space_indy))
00582 space_indy = proc_name.end() + 1;
00583 process_entry to_add;
00584 to_add._process_id = p_id;
00585 astring path = proc_name.substring(0, space_indy - 1);
00586
00587 int brackets_in = 0;
00588 for (int i = 0; i < path.length(); i++) {
00589 if (path[i] == '[') brackets_in++;
00590 else if (path[i] == ']') brackets_in--;
00591 if (brackets_in) {
00592
00593
00594 if ( (path[i] == '/') || (path[i] == '\\') )
00595 path[i] = '#';
00596 }
00597 }
00598 to_add.path(path);
00599 to_fill += to_add;
00600 }
00601 return true;
00602 }
00603 #endif // __UNIX__
00604
00605 }
00606