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