00001 /*****************************************************************************\ 00002 * * 00003 * Name : windoze_helper * 00004 * Author : Chris Koeritz * 00005 * * 00006 ******************************************************************************* 00007 * Copyright (c) 1994-$now By Author. This program is free software; you can * 00008 * redistribute it and/or modify it under the terms of the GNU General Public * 00009 * License as published by the Free Software Foundation; either version 2 of * 00010 * the License or (at your option) any later version. This is online at: * 00011 * http://www.fsf.org/copyleft/gpl.html * 00012 * Please send any updates to: fred@gruntose.com * 00013 \*****************************************************************************/ 00014 00015 #include "windoze_helper.h" 00024 #include <configuration/application_configuration.h> 00025 00028 00029 /* 00030 #ifdef __UNIX__ 00031 #include <limits.h> 00032 #include <stdio.h> 00033 #include <string.h> 00034 #include <sys/poll.h> 00035 #include <sys/times.h> 00036 #include <sys/utsname.h> 00037 #include <sys/wait.h> 00038 #include <time.h> 00039 #include <unistd.h> 00040 #endif 00041 #ifdef __XWINDOWS__ 00042 //hmmm: need code for the wait cursor stuff. 00043 #endif 00044 #ifdef __WIN32__ 00045 #include <mmsystem.h> 00046 #include <process.h> 00047 #include <shellapi.h> 00048 #include <shlobj.h> 00049 #endif 00050 */ 00051 00052 using namespace basis; 00053 using namespace structures; 00054 using namespace configuration; 00055 00056 #undef static_class_name 00057 #define static_class_name() "windoze_helper" 00058 00059 /* 00060 //#define DEBUG_PORTABLE 00061 // uncomment for noisy debugging. 00062 00063 //#define DEBUG_UPTIME 00064 // uncomment to get noisier reporting about system and rolling uptime. 00065 00066 //#define JUMP_TIME_49_DAYS 00067 // uncomment to make our uptimes start just before the 32 bit uptime rollover. 00068 //#define JUMP_TIME_497_DAYS 00069 // uncomment to make our uptimes start just before the jiffies rollover. 00070 00071 //#define ENABLE_ROLLOVER_BUG 00072 // uncomment to get old behavior back where the uptime was not rolling 00073 // over properly. this turns off our remainder calculation and leaves the 00074 // conversion as a simple cast, which will fail and get stuck at 2^32-1. 00075 00076 #define SUPPORT_SHELL_EXECUTE 00077 // if this is not commented out, then the ShellExecute version of launch_ 00078 // -process() is available. when commented out, ShellExecute is turned off. 00079 // disabling this support is the most common because the ShellExecute method 00080 // in win32 was only supported for wk203 and wxp, that is only after 00081 // windows2000 was already available. since nt and w2k don't support this, 00082 // we just usually don't mess with it. it didn't answer a single one of our 00083 // issues on windows vista (wfista) anyway, so it's not helping. 00084 00085 // ensure we always have debugging turned on if the jump is enabled. 00086 #ifdef JUMP_TIME_49_DAYS 00087 #undef DEBUG_UPTIME 00088 #define DEBUG_UPTIME 00089 #endif 00090 #ifdef JUMP_TIME_497_DAYS 00091 #undef DEBUG_UPTIME 00092 #define DEBUG_UPTIME 00093 #endif 00094 // the JUMP..DAYS macros are mutually exclusive. use none or one, not both. 00095 #ifdef JUMP_TIME_497_DAYS 00096 #ifdef JUMP_TIME_49_DAYS 00097 #error One cannot use both 497 day and 49 day bug inducers 00098 #endif 00099 #endif 00100 #ifdef JUMP_TIME_49_DAYS 00101 #ifdef JUMP_TIME_497_DAYS 00102 #error One cannot use both 49 day and 497 day bug inducers 00103 #endif 00104 #endif 00105 */ 00106 00107 00108 namespace application { 00109 00110 /* 00111 mutex BASIS_EXTERN &__uptime_synchronizer(); 00112 // used by our low-level uptime methods to protect singleton variables. 00113 mutex BASIS_EXTERN &__process_synchronizer(); 00114 // used for synchronizing our records of child processes. 00115 #ifdef __UNIX__ 00116 int_set BASIS_EXTERN &__our_kids(); 00117 // the static list of processes we've started. 00118 #endif 00119 00120 const int MAXIMUM_COMMAND_LINE = 32 * KILOBYTE; 00121 // maximum command line that we'll deal with here. 00122 00123 #ifdef __UNIX__ 00124 const char *UPTIME_REPORT_FILE = "/tmp/uptime_report.log"; 00125 #endif 00126 #ifdef __WIN32__ 00127 const char *UPTIME_REPORT_FILE = "c:/uptime_report.log"; 00128 #endif 00129 00130 #undef LOG 00131 #define LOG(s) STAMPED_EMERGENCY_LOG(program_wide_logger(), s); 00132 00133 #define COMPLAIN(to_print) { \ 00134 guards::write_to_console((isprintf("basis/portable::%s: ", func) + to_print).s()); \ 00135 } 00136 00137 static const double __rollover_point = 2.0 * double(MAXINT); 00138 // this number is our rollover point for 32 bit integers. 00139 00140 double rolling_uptime() 00141 { 00142 auto_synchronizer l(__uptime_synchronizer()); 00143 // protect our rollover records. 00144 00145 static u_int __last_ticks = 0; 00146 static int __rollovers = 0; 00147 00148 u_int ticks_up = system_uptime(); 00149 // acquire the current uptime as a 32 bit unsigned int. 00150 00151 if (ticks_up < __last_ticks) { 00152 // rollover happened. increment our tracker. 00153 __rollovers++; 00154 } 00155 __last_ticks = ticks_up; 00156 00157 double to_return = double(__rollovers) * __rollover_point + double(ticks_up); 00158 00159 #ifdef DEBUG_UPTIME 00160 #ifdef __WIN32__ 00161 static FILE *__outfile = fopen(UPTIME_REPORT_FILE, "a+b"); 00162 static double old_value = 0; 00163 if (absolute_value(old_value - to_return) > 9.9999) { 00164 // only report when the time changes by more than 10 ms. 00165 fprintf(__outfile, "-> uptime=%.0f\n", to_return); 00166 fflush(__outfile); 00167 old_value = to_return; 00168 if (__rollover_point - to_return <= 40.00001) { 00169 fprintf(__outfile, "---> MAXIMUM UPTIME SOON!\n"); 00170 fflush(__outfile); 00171 } 00172 } 00173 #endif 00174 #endif 00175 00176 return to_return; 00177 } 00178 00179 u_int system_uptime() 00180 { 00181 #ifdef __WIN32__ 00182 return timeGetTime(); 00183 #else 00184 auto_synchronizer l(__uptime_synchronizer()); 00185 00186 static clock_t __ctps = sysconf(_SC_CLK_TCK); // clock ticks per second. 00187 static const double __multiplier = 1000.0 / double(__ctps); 00188 // the multiplier gives us our full range for the tick counter. 00189 00190 #ifdef DEBUG_UPTIME 00191 static FILE *__outfile = fopen(UPTIME_REPORT_FILE, "wb"); 00192 if (__multiplier - u_int(__multiplier) > 0.000001) { 00193 fprintf(__outfile, "uptime multiplier is " 00194 "non-integral (%f)!\n", __multiplier); 00195 fflush(__outfile); 00196 } 00197 #endif 00198 00199 // read uptime info from the OS. 00200 tms uptime; 00201 u_int real_ticks = times(&uptime); 00202 00203 #ifdef JUMP_TIME_497_DAYS 00204 static u_int old_value_497 = 0; 00205 bool report_497 = (absolute_value(real_ticks - old_value_497) > 99); 00206 if (report_497) { 00207 old_value_497 = real_ticks; // update before changing it. 00208 fprintf(__outfile, "pre kludge497 tix=%u\n", real_ticks); 00209 fflush(__outfile); 00210 } 00211 real_ticks += u_int(49.0 * double(DAY_ms) + 17.0 * double(HOUR_ms)); 00212 if (report_497) { 00213 fprintf(__outfile, "post kludge497 tix=%u\n", real_ticks); 00214 fflush(__outfile); 00215 } 00216 #endif 00217 00218 // now turn this into the number of milliseconds. 00219 double ticks_up = (double)real_ticks; 00220 ticks_up = ticks_up * __multiplier; // convert to time here. 00221 00222 #ifdef JUMP_TIME_497_DAYS 00223 #ifndef ENABLE_ROLLOVER_BUG 00224 // add in additional time so we don't have to wait forever. we make the 00225 // big number above rollover, but that's still got 27.5 or so minutes before 00226 // we really rollover. so we add that part of the fraction (lost in the 00227 // haze of the multiplier) in here. we don't add this in unless they are 00228 // not exercising the rollover bug, because we already know that the 497 00229 // day bug will show without the addition. but when we're already fixing 00230 // the uptime, we jump time a bit forward so we only have to wait a couple 00231 // minutes instead of 27. 00232 ticks_up += 25.0 * MINUTE_ms; 00233 #endif 00234 #endif 00235 00236 #ifdef JUMP_TIME_49_DAYS 00237 static u_int old_value_49 = 0; 00238 bool report_49 = (absolute_value(u_int(ticks_up) - old_value_49) > 999); 00239 if (report_49) { 00240 old_value_49 = u_int(ticks_up); // update before changing it. 00241 fprintf(__outfile, "pre kludge49 up=%f\n", ticks_up); 00242 fflush(__outfile); 00243 } 00244 ticks_up += 49.0 * double(DAY_ms) + 17.0 * double(HOUR_ms); 00245 if (report_49) { 00246 fprintf(__outfile, "post kludge49 up=%f\n", ticks_up); 00247 fflush(__outfile); 00248 } 00249 #endif 00250 00251 #ifndef ENABLE_ROLLOVER_BUG 00252 // fix the return value if is has actually gone over the 2^32 limit for uint. 00253 // casting a double larger than 2*MAXINT to a u_int on some platforms does 00254 // not calculate a rolled-over value, but instead leaves the int at 2*MAXINT. 00255 // thus we make sure it will be correct, as long as there are no more than 00256 // 2^32-1 rollovers, which would be about 584,542 millenia. it's unlikely 00257 // earth will last that long, so this calculation seems safe. 00258 u_int divided = u_int(ticks_up / __rollover_point); 00259 double to_return = ticks_up - (double(divided) * __rollover_point); 00260 #else 00261 // we use the previous version of this calculation, which expected a u_int 00262 // to double conversion to provide a modulo operation rather than just leaving 00263 // the u_int at its maximum value (2^32-1). however, that expectation is not 00264 // guaranteed on some platforms (e.g., ARM processor with floating point 00265 // emulation) and thus it becomes a bug around 49 days and 17 hours into 00266 // OS uptime because the value gets stuck at 2^32-1 and never rolls over. 00267 double to_return = ticks_up; 00268 #endif 00269 00270 #ifdef DEBUG_UPTIME 00271 static u_int old_value = 0; 00272 int to_print = int(u_int(to_return)); 00273 if (absolute_value(int(old_value) - to_print) > 9) { 00274 // only report when the time changes by more than 10 ms. 00275 fprintf(__outfile, "-> uptime=%u\n", to_print); 00276 fflush(__outfile); 00277 old_value = u_int(to_print); 00278 if (__rollover_point - to_return <= 40.00001) { 00279 fprintf(__outfile, "---> MAXIMUM UPTIME SOON!\n"); 00280 fflush(__outfile); 00281 } 00282 } 00283 #endif 00284 00285 return u_int(to_return); 00286 #endif 00287 } 00288 00289 void sleep_ms(u_int msec) 00290 { 00291 #ifdef __UNIX__ 00292 usleep(msec * 1000); 00293 #endif 00294 #ifdef __WIN32__ 00295 Sleep(msec); 00296 #endif 00297 } 00298 00299 istring null_device() 00300 { 00301 #ifdef __WIN32__ 00302 return "null:"; 00303 #else 00304 return "/dev/null"; 00305 #endif 00306 } 00307 00308 #ifdef __WIN32__ 00309 bool event_poll(MSG &message) 00310 { 00311 message.hwnd = 0; 00312 message.message = 0; 00313 message.wParam = 0; 00314 message.lParam = 0; 00315 if (!PeekMessage(&message, NIL, 0, 0, PM_REMOVE)) 00316 return false; 00317 TranslateMessage(&message); 00318 DispatchMessage(&message); 00319 return true; 00320 } 00321 #endif 00322 00323 // makes a complaint about a failure. 00324 #ifndef EMBEDDED_BUILD 00325 #define COMPLAIN_QUERY(to_print) { \ 00326 unlink(tmpfile.s()); \ 00327 COMPLAIN(to_print); \ 00328 } 00329 #else 00330 #define COMPLAIN_QUERY(to_print) { \ 00331 COMPLAIN(to_print); \ 00332 } 00333 #endif 00334 00335 #ifdef __UNIX__ 00336 istring get_cmdline_from_proc() 00337 { 00338 FUNCDEF("get_cmdline_from_proc"); 00339 isprintf cmds_filename("/proc/%d/cmdline", portable::process_id()); 00340 FILE *cmds_file = fopen(cmds_filename.s(), "r"); 00341 if (!cmds_file) { 00342 COMPLAIN("failed to open our process's command line file.\n"); 00343 return "unknown"; 00344 } 00345 size_t size = 2000; 00346 char *buff = new char[size + 1]; 00347 ssize_t chars_read = getline(&buff, &size, cmds_file); 00348 // read the first line, giving ample space for how long it might be. 00349 fclose(cmds_file); // drop the file again. 00350 if (!chars_read || negative(chars_read)) { 00351 COMPLAIN("failed to get any characters from our process's cmdline file.\n"); 00352 return "unknown"; 00353 } 00354 istring buffer = buff; 00355 delete [] buff; 00356 // clean out quote characters from the name. 00357 for (int i = buffer.length() - 1; i >= 0; i--) { 00358 if (buffer[i] == '"') buffer.zap(i, i); 00359 } 00360 return buffer; 00361 } 00362 00363 // deprecated; better to use the /proc/pid/cmdline file. 00364 istring query_for_process_info() 00365 { 00366 FUNCDEF("query_for_process_info"); 00367 istring to_return = "unknown"; 00368 // we ask the operating system about our process identifier and store 00369 // the results in a temporary file. 00370 chaos rando; 00371 isprintf tmpfile("/tmp/proc_name_check_%d_%d.txt", portable::process_id(), 00372 rando.inclusive(0, 128000)); 00373 isprintf cmd("ps h --format \"%%a\" %d >%s", portable::process_id(), 00374 tmpfile.s()); 00375 // run the command to locate our process info. 00376 int sysret = system(cmd.s()); 00377 if (negative(sysret)) { 00378 COMPLAIN_QUERY("failed to run ps command to get process info"); 00379 return to_return; 00380 } 00381 // open the output file for reading. 00382 FILE *output = fopen(tmpfile.s(), "r"); 00383 if (!output) { 00384 COMPLAIN_QUERY("failed to open the ps output file"); 00385 return to_return; 00386 } 00387 // read the file's contents into a string buffer. 00388 char buff[MAXIMUM_COMMAND_LINE]; 00389 size_t size_read = fread(buff, 1, MAXIMUM_COMMAND_LINE, output); 00390 if (size_read > 0) { 00391 // success at finding some text in the file at least. 00392 while (size_read > 0) { 00393 const char to_check = buff[size_read - 1]; 00394 if ( !to_check || (to_check == '\r') || (to_check == '\n') 00395 || (to_check == '\t') ) 00396 size_read--; 00397 else break; 00398 } 00399 to_return.reset(istring::UNTERMINATED, buff, size_read); 00400 } else { 00401 // couldn't read anything. 00402 COMPLAIN_QUERY("could not read output of process list"); 00403 } 00404 unlink(tmpfile.s()); 00405 return to_return; 00406 } 00407 #endif 00408 00409 // used as a return value when the name cannot be determined. 00410 #ifndef EMBEDDED_BUILD 00411 #define SET_BOGUS_NAME(error) { \ 00412 COMPLAIN(error); \ 00413 if (output) { \ 00414 fclose(output); \ 00415 unlink(tmpfile.s()); \ 00416 } \ 00417 istring home_dir = env_string("HOME"); \ 00418 to_return = home_dir + "/failed_to_determine.exe"; \ 00419 } 00420 #else 00421 #define SET_BOGUS_NAME(error) { \ 00422 COMPLAIN(error); \ 00423 to_return = "unknown"; \ 00424 } 00425 #endif 00426 00427 istring application_name() 00428 { 00429 FUNCDEF("application_name"); 00430 istring to_return; 00431 #ifdef __UNIX__ 00432 to_return = get_cmdline_from_proc(); 00433 #elif defined(__WIN32__) 00434 flexichar low_buff[MAX_ABS_PATH + 1]; 00435 GetModuleFileName(NIL, low_buff, MAX_ABS_PATH - 1); 00436 istring buff = from_unicode_temp(low_buff); 00437 buff.to_lower(); // we lower-case the name since windows seems to UC it. 00438 to_return = buff; 00439 #elif defined(EMBEDDED_BUILD) 00440 SET_BOGUS_NAME("embedded_exe"); 00441 #else 00442 #pragma error("hmmm: no means of finding app name is implemented.") 00443 SET_BOGUS_NAME("not_implemented_for_this_OS"); 00444 #endif 00445 return to_return; 00446 } 00447 00448 istring module_name(const void *module_handle) 00449 { 00450 #ifdef __UNIX__ 00451 //hmmm: implement module name for linux if that makes sense. 00452 if (module_handle) {} 00453 return application_name(); 00454 #elif defined(__WIN32__) 00455 flexichar low_buff[MAX_ABS_PATH + 1]; 00456 GetModuleFileName((HMODULE)module_handle, low_buff, MAX_ABS_PATH - 1); 00457 istring buff = from_unicode_temp(low_buff); 00458 buff.to_lower(); 00459 return buff; 00460 #else 00461 #pragma message("module_name unknown for this operating system.") 00462 return application_name(); 00463 #endif 00464 } 00465 00466 u_int system_error() 00467 { 00468 #if defined(__UNIX__) 00469 return errno; 00470 #elif defined(__WIN32__) 00471 return GetLastError(); 00472 #else 00473 #pragma error("hmmm: no code for error number for this operating system") 00474 return 0; 00475 #endif 00476 } 00477 00478 istring system_error_text(u_int to_name) 00479 { 00480 #if defined(__UNIX__) 00481 return strerror(to_name); 00482 #elif defined(__WIN32__) 00483 char error_text[1000]; 00484 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NIL, to_name, 00485 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)error_text, 00486 sizeof(error_text) - 1, NIL); 00487 istring to_return = error_text; 00488 // trim off the ridiculous carriage return they add. 00489 while ( (to_return[to_return.end()] == '\r') 00490 || (to_return[to_return.end()] == '\n') ) 00491 to_return.zap(to_return.end(), to_return.end()); 00492 return to_return; 00493 #else 00494 #pragma error("hmmm: no code for error text for this operating system") 00495 return ""; 00496 #endif 00497 } 00498 00499 #ifdef __WIN32__ 00500 00501 bool is_address_valid(const void *address, int size_expected, bool writable) 00502 { 00503 return address && !IsBadReadPtr(address, size_expected) 00504 && !(writable && IsBadWritePtr((void *)address, size_expected)); 00505 } 00506 00507 #endif // win32 00508 00509 version get_OS_version() 00510 { 00511 version to_return; 00512 #ifdef __UNIX__ 00513 utsname kernel_parms; 00514 uname(&kernel_parms); 00515 to_return = version(kernel_parms.release); 00516 #elif defined(__WIN32__) 00517 OSVERSIONINFO info; 00518 info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 00519 ::GetVersionEx(&info); 00520 to_return = version(isprintf("%u.%u.%u.%u", u_short(info.dwMajorVersion), 00521 u_short(info.dwMinorVersion), u_short(info.dwPlatformId), 00522 u_short(info.dwBuildNumber))); 00523 #elif defined(EMBEDDED_BUILD) 00524 // no version support. 00525 #else 00526 #pragma error("hmmm: need version info for this OS!") 00527 #endif 00528 return to_return; 00529 } 00530 00531 // for non-win32 and non-unix OSes, this might not work. 00532 #if defined(__UNIX__) || defined(__WIN32__) 00533 u_int process_id() { return getpid(); } 00534 #else 00535 #pragma error("hmmm: need process id implementation for this OS!") 00536 u_int process_id() { return 0; } 00537 #endif 00538 00539 istring current_directory() 00540 { 00541 istring to_return; 00542 #ifdef __UNIX__ 00543 char buff[MAX_ABS_PATH]; 00544 getcwd(buff, MAX_ABS_PATH - 1); 00545 to_return = buff; 00546 #elif defined(__WIN32__) 00547 flexichar low_buff[MAX_ABS_PATH + 1]; 00548 GetCurrentDirectory(MAX_ABS_PATH, low_buff); 00549 to_return = from_unicode_temp(low_buff); 00550 #else 00551 #pragma error("hmmm: need support for current directory on this OS.") 00552 to_return = "."; 00553 #endif 00554 return to_return; 00555 } 00556 00557 istring env_string(const istring &variable_name) 00558 { 00559 #ifdef __WIN32__ 00560 char *value = getenv(variable_name.upper().observe()); 00561 // dos & os/2 require upper case for the name, so we just do it that way. 00562 #else 00563 char *value = getenv(variable_name.observe()); 00564 // reasonable OSes support mixed-case environment variables. 00565 #endif 00566 istring to_return; 00567 if (value) 00568 to_return = istring(value); 00569 return to_return; 00570 } 00571 00572 bool set_environ(const istring &variable_name, const istring &value) 00573 { 00574 int ret = 0; 00575 #ifdef __WIN32__ 00576 ret = putenv((variable_name + "=" + value).s()); 00577 #else 00578 ret = setenv(variable_name.s(), value.s(), true); 00579 #endif 00580 return !ret; 00581 } 00582 00583 timeval fill_timeval_ms(int duration) 00584 { 00585 timeval time_out; // timeval has tv_sec=seconds, tv_usec=microseconds. 00586 if (!duration) { 00587 // duration is immediate for the check; just a quick poll. 00588 time_out.tv_sec = 0; 00589 time_out.tv_usec = 0; 00590 #ifdef DEBUG_PORTABLE 00591 // LOG("no duration specified"); 00592 #endif 00593 } else { 00594 // a non-zero duration means we need to compute secs and usecs. 00595 time_out.tv_sec = duration / 1000; 00596 // set the number of seconds from the input in milliseconds. 00597 duration -= time_out.tv_sec * 1000; 00598 // now take out the chunk we've already recorded as seconds. 00599 time_out.tv_usec = duration * 1000; 00600 // set the number of microseconds from the remaining milliseconds. 00601 #ifdef DEBUG_PORTABLE 00602 // LOG(isprintf("duration of %d ms went to %d sec and %d usec.", duration, 00603 // time_out.tv_sec, time_out.tv_usec)); 00604 #endif 00605 } 00606 return time_out; 00607 } 00608 00609 //hmmm: this doesn't seem to account for quoting properly at all? 00610 basis::char_star_array break_line(istring &app, const istring ¶meters) 00611 { 00612 basis::char_star_array to_return; 00613 int_array posns; 00614 int num = 0; 00615 // find the positions of the spaces and count them. 00616 for (int j = 0; j < parameters.length(); j++) { 00617 if (parameters[j] == ' ') { 00618 num++; 00619 posns += j; 00620 } 00621 } 00622 // first, add the app name to the list of parms. 00623 to_return += new char[app.length() + 1]; 00624 app.stuff(to_return[0], app.length()); 00625 int last_posn = 0; 00626 // now add each space-separated parameter to the list. 00627 for (int i = 0; i < num; i++) { 00628 int len = posns[i] - last_posn; 00629 to_return += new char[len + 1]; 00630 parameters.substring(last_posn, posns[i] - 1).stuff(to_return[i + 1], len); 00631 last_posn = posns[i] + 1; 00632 } 00633 // catch anything left after last separator. 00634 if (last_posn < parameters.length() - 1) { 00635 int len = parameters.length() - last_posn; 00636 to_return += new char[len + 1]; 00637 parameters.substring(last_posn, parameters.length() - 1) 00638 .stuff(to_return[to_return.last()], len); 00639 } 00640 // add the sentinel to the list of strings. 00641 to_return += NIL; 00642 #ifdef DEBUG_PORTABLE 00643 for (int q = 0; to_return[q]; q++) { 00644 printf("%d: %s\n", q, to_return[q]); 00645 } 00646 #endif 00647 // now a special detour; fix the app name to remove quotes, which are 00648 // not friendly to pass to exec. 00649 if (app[0] == '"') app.zap(0, 0); 00650 if (app[app.end()] == '"') app.zap(app.end(), app.end()); 00651 return to_return; 00652 } 00653 00654 #ifdef __UNIX__ 00655 00656 void exiting_child_signal_handler(int sig_num) 00657 { 00658 FUNCDEF("exiting_child_signal_handler"); 00659 if (sig_num != SIGCHLD) { 00660 // uhhh, this seems wrong. 00661 } 00662 auto_synchronizer l(__process_synchronizer()); 00663 for (int i = 0; i < __our_kids().length(); i++) { 00664 int status; 00665 pid_t exited = waitpid(__our_kids()[i], &status, WNOHANG); 00666 if ( (exited == -1) || (exited == __our_kids()[i]) ) { 00667 // negative one denotes an error, which we are going to assume means the 00668 // process has exited via some other method than our wait. if the value 00669 // is the same as the process we waited for, that means it exited. 00670 __our_kids().zap(i, i); 00671 i--; 00672 } else if (exited != 0) { 00673 // zero would be okay; this result we do not understand. 00674 #ifdef DEBUG_PORTABLE 00675 printf("unknown result %d waiting for process %d", exited, 00676 __our_kids()[i]); 00677 #endif 00678 } 00679 } 00680 } 00681 #endif 00682 00683 u_int launch_process(const istring &app_name_in, const istring &command_line, 00684 int flag, u_int &child_id) 00685 { 00686 child_id = 0; 00687 istring app_name = app_name_in; 00688 if (app_name[0] != '"') 00689 app_name.insert(0, "\""); 00690 if (app_name[app_name.end()] != '"') 00691 app_name += "\""; 00692 #ifdef __UNIX__ 00693 // unix / linux implementation. 00694 if (flag & RETURN_IMMEDIATELY) { 00695 // they want to get back right away. 00696 pid_t kid_pid = fork(); 00697 #ifdef DEBUG_PORTABLE 00698 printf("launch fork returned %d\n", kid_pid); 00699 #endif 00700 if (!kid_pid) { 00701 // this is the child; we now need to launch into what we were asked for. 00702 #ifdef DEBUG_PORTABLE 00703 printf((isprintf("process %d execing ", process_id()) + app_name 00704 + " parms " + command_line + "\n").s()); 00705 #endif 00706 basis::char_star_array parms = break_line(app_name, command_line); 00707 execv(app_name.s(), parms.observe()); 00708 // oops. failed to exec if we got to here. 00709 #ifdef DEBUG_PORTABLE 00710 printf((isprintf("child of fork (pid %d) failed to exec, " 00711 "error is ", process_id()) + system_error_text(system_error()) 00712 + "\n").s()); 00713 #endif 00714 exit(0); // leave since this is a failed child process. 00715 } else { 00716 // this is the parent. let's see if the launch worked. 00717 if (kid_pid == -1) { 00718 // failure. 00719 u_int to_return = system_error(); 00720 #ifdef DEBUG_PORTABLE 00721 printf((isprintf("parent %d is returning after failing to create, " 00722 "error is ", process_id()) + system_error_text(to_return) 00723 + "\n").s()); 00724 #endif 00725 return to_return; 00726 } else { 00727 // yes, launch worked okay. 00728 child_id = kid_pid; 00729 { 00730 auto_synchronizer l(__process_synchronizer()); 00731 __our_kids() += kid_pid; 00732 } 00733 // hook in our child exit signal handler. 00734 signal(SIGCHLD, exiting_child_signal_handler); 00735 00736 #ifdef DEBUG_PORTABLE 00737 printf((isprintf("parent %d is returning after successfully " 00738 "creating %d ", process_id(), kid_pid) + app_name 00739 + " parms " + command_line + "\n").s()); 00740 #endif 00741 return 0; 00742 } 00743 } 00744 } else { 00745 // assume they want to wait. 00746 return system((app_name + " " + command_line).s()); 00747 } 00748 #elif defined(__WIN32__) 00749 00750 //checking on whether we have admin rights for the launch. 00751 //if (IsUserAnAdmin()) { 00752 // MessageBox(0, (istring("IS admin with ") + app_name_in + " " + command_line).s(), "launch process", MB_OK); 00753 //} else { 00754 // MessageBox(0, (istring("NOT admin for ") + app_name_in + " " + command_line).s(), "launch process", MB_OK); 00755 //} 00756 00757 PROCESS_INFORMATION process_info; 00758 ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION)); 00759 00760 #ifdef SUPPORT_SHELL_EXECUTE 00761 if (flag & SHELL_EXECUTE) { 00762 // new code for using shell execute method--required on vista for proper 00763 // launching with the right security tokens. 00764 int show_cmd = 0; 00765 if (flag & HIDE_APP_WINDOW) { 00766 // magic that hides a console window for mswindows. 00767 show_cmd = SW_HIDE; 00768 } else { 00769 show_cmd = SW_SHOWNORMAL; 00770 } 00771 00772 SHELLEXECUTEINFO exec_info; 00773 ZeroMemory(&exec_info, sizeof(SHELLEXECUTEINFO)); 00774 exec_info.cbSize = sizeof(SHELLEXECUTEINFO); 00775 exec_info.fMask = SEE_MASK_NOCLOSEPROCESS // get the process info. 00776 | SEE_MASK_FLAG_NO_UI; // turn off any visible error dialogs. 00777 exec_info.hwnd = GetDesktopWindow(); 00778 //hmmm: is get desktop window always appropriate? 00779 to_unicode_persist(temp_verb, "open"); 00780 //does "runas" work on xp also? or anywhere? 00781 exec_info.lpVerb = temp_verb; 00782 to_unicode_persist(temp_file, app_name); 00783 exec_info.lpFile = temp_file; 00784 to_unicode_persist(temp_parms, command_line); 00785 exec_info.lpParameters = temp_parms; 00786 exec_info.nShow = show_cmd; 00787 // exec_info.hProcess = &process_info; 00788 00789 BOOL worked = ShellExecuteEx(&exec_info); 00790 if (!worked) 00791 return system_error(); 00792 // copy out the returned process handle. 00793 process_info.hProcess = exec_info.hProcess; 00794 process_info.dwProcessId = GetProcessId(exec_info.hProcess); 00795 } else { 00796 #endif //shell exec 00797 // standard windows implementation using CreateProcess. 00798 STARTUPINFO startup_info; 00799 ZeroMemory(&startup_info, sizeof(STARTUPINFO)); 00800 startup_info.cb = sizeof(STARTUPINFO); 00801 int create_flag = 0; 00802 if (flag & HIDE_APP_WINDOW) { 00803 // magic that hides a console window for mswindows. 00804 // version ver = portable::get_OS_version(); 00805 // version vista_version(6, 0); 00806 // if (ver < vista_version) { 00807 // // we suspect that this flag is hosing us in vista. 00808 create_flag = CREATE_NO_WINDOW; 00809 // } 00810 } 00811 istring parms = app_name + " " + command_line; 00812 bool success = CreateProcess(NIL, to_unicode_temp(parms), NIL, NIL, false, 00813 create_flag, NIL, NIL, &startup_info, &process_info); 00814 if (!success) 00815 return system_error(); 00816 // success then, merge back into stream. 00817 00818 #ifdef SUPPORT_SHELL_EXECUTE 00819 } 00820 #endif //shell exec 00821 00822 // common handling for CreateProcess and ShellExecuteEx. 00823 child_id = process_info.dwProcessId; 00824 u_long retval = 0; 00825 if (flag & AWAIT_VIA_POLLING) { 00826 // this type of waiting is done without blocking on the process. 00827 while (true) { 00828 MSG msg; 00829 event_poll(msg); 00830 // check if the process is gone yet. 00831 BOOL ret = GetExitCodeProcess(process_info.hProcess, &retval); 00832 if (!ret) { 00833 break; 00834 } else { 00835 // if they aren't saying it's still active, then we will leave. 00836 if (retval != STILL_ACTIVE) 00837 break; 00838 } 00839 sleep_ms(14); 00840 } 00841 } else if (flag & AWAIT_APP_EXIT) { 00842 // they want to wait for the process to exit. 00843 WaitForInputIdle(process_info.hProcess, INFINITE); 00844 WaitForSingleObject(process_info.hProcess, INFINITE); 00845 GetExitCodeProcess(process_info.hProcess, &retval); 00846 } 00847 // drop the process and thread handles. 00848 if (process_info.hProcess) 00849 CloseHandle(process_info.hProcess); 00850 if (process_info.hThread) 00851 CloseHandle(process_info.hThread); 00852 return (u_int)retval; 00853 #else 00854 #pragma error("hmmm: launch_process: no implementation for this OS.") 00855 #endif 00856 return 0; 00857 } 00858 00860 00861 #ifdef __UNIX__ 00862 00863 char *itoa(int to_convert, char *buffer, int radix) 00864 { 00865 // slow implementation; may need enhancement for serious speed. 00866 // ALSO: only supports base 10 and base 16 currently. 00867 const char *formatter = "%d"; 00868 if (radix == 16) formatter = "%x"; 00869 isprintf printed(formatter, to_convert); 00870 printed.stuff(buffer, printed.length() + 1); 00871 return buffer; 00872 } 00873 00874 #endif 00875 */ 00876 00877 #ifdef __WIN32__ 00878 00879 /* 00880 void show_wait_cursor() { SetCursor(LoadCursor(NULL, IDC_WAIT)); } 00881 00882 void show_normal_cursor() { SetCursor(LoadCursor(NULL, IDC_ARROW)); } 00883 00884 istring rc_string(UINT id, application_instance instance) 00885 { 00886 flexichar temp[MAX_ABS_PATH + 1]; 00887 int ret = LoadString(instance, id, temp, MAX_ABS_PATH); 00888 if (!ret) return istring(); 00889 return istring(from_unicode_temp(temp)); 00890 } 00891 00892 istring rc_string(u_int id) { return rc_string(id, GET_INSTANCE_HANDLE()); } 00893 */ 00894 00895 const char *opsystem_name(known_operating_systems which) 00896 { 00897 switch (which) { 00898 case WIN_95: return "WIN_95"; 00899 case WIN_NT: return "WIN_NT"; 00900 case WIN_2K: return "WIN_2K"; 00901 case WIN_XP: return "WIN_XP"; 00902 case WIN_SRV2K3: return "WIN_SRV2K3"; 00903 case WIN_VISTA: return "WIN_VISTA"; 00904 case WIN_SRV2K8: return "WIN_SRV2K8"; 00905 default: return "UNKNOWN_OS"; 00906 } 00907 } 00908 00909 known_operating_systems determine_OS() 00910 { 00911 version osver = application_configuration::get_OS_version(); 00912 if ( (osver.v_major() == 4) && (osver.v_minor() == 0) ) { 00913 if (osver.v_revision() == VER_PLATFORM_WIN32_WINDOWS) return WIN_95; 00914 if (osver.v_revision() == VER_PLATFORM_WIN32_NT) return WIN_NT; 00915 } else if ( (osver.v_major() == 5) && (osver.v_minor() == 0) ) { 00916 return WIN_2K; 00917 } else if ( (osver.v_major() == 5) && (osver.v_minor() == 1) ) { 00918 return WIN_XP; 00919 } else if ( (osver.v_major() == 5) && (osver.v_minor() == 2) ) { 00920 return WIN_SRV2K3; 00921 } else if ( (osver.v_major() == 6) && (osver.v_minor() == 0) ) { 00922 return WIN_VISTA; 00923 } else if ( (osver.v_major() == 6) && (osver.v_minor() == 1) ) { 00924 return WIN_SRV2K8; 00925 } 00926 return UNKNOWN_OS; 00927 } 00928 00929 #endif // win32 00930 00931 } // namespace. 00932 00933 #undef static_class_name 00934
1.6.3