windoze_helper.cpp

Go to the documentation of this file.
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 &parameters)
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 
Generated on Sat Jan 28 04:22:10 2012 for hoople2 project by  doxygen 1.6.3