portable.cpp

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

Generated on Tue Aug 19 04:29:33 2008 for HOOPLE Libraries by  doxygen 1.5.1