feisty meow concerns codebase  2.140
launch_process.cpp
Go to the documentation of this file.
1 
2 // Name : launch_process
3 // Author : Chris Koeritz
4 /******************************************************************************
5 * Copyright (c) 1994-$now By Author. This program is free software; you can *
6 * redistribute it and/or modify it under the terms of the GNU General Public *
7 * License as published by the Free Software Foundation; either version 2 of *
8 * the License or (at your option) any later version. This is online at: *
9 * http://www.fsf.org/copyleft/gpl.html *
10 * Please send any updates to: fred@gruntose.com *
11 \*****************************************************************************/
12 
13 #include "launch_process.h"
14 
16 #include <basis/utf_conversion.h>
17 #include <basis/mutex.h>
21 #include <structures/set.h>
22 #include <timely/time_control.h>
23 
24 #include <stdlib.h>
25 //#ifndef _MSC_VER
26  #include <signal.h>
27  #include <sys/types.h>
28  #include <sys/wait.h>
29  #include <unistd.h>
30 /*
31  * #else
32  #include <process.h>
33  #include <shellapi.h>
34  #include <shlobj.h>
35 #endif
36 */
37 
38 //#define DEBUG_LAUNCH_PROCESS
39  // uncomment for noisier debugging info.
40 
41 using namespace basis;
42 using namespace configuration;
43 using namespace loggers;
44 using namespace structures;
45 using namespace timely;
46 
47 #undef LOG
48 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s);
49 
50 namespace processes {
51 
52 //hmmm: some of these should probably be safe statics.
53 
55  static mutex __hidden_synch;
56  return __hidden_synch;
57 }
58 
60  static int_set __hidden_kids;
61  return __hidden_kids;
62 }
63 
64 /*
65 #ifdef _MSC_VER
66 bool launch_process::event_poll(MSG &message)
67 {
68  message.hwnd = 0;
69  message.message = 0;
70  message.wParam = 0;
71  message.lParam = 0;
72  if (!PeekMessage(&message, NULL_POINTER, 0, 0, PM_REMOVE))
73  return false;
74  TranslateMessage(&message);
75  DispatchMessage(&message);
76  return true;
77 }
78 #endif
79 */
80 
81 #define SUPPORT_SHELL_EXECUTE
82  // if this is not commented out, then the ShellExecute version of launch_
83  // -process() is available. when commented out, ShellExecute is turned off.
84  // disabling this support is the most common because the ShellExecute method
85  // in win32 was only supported for wk203 and wxp, that is only after
86  // windows2000 was already available. since nt and w2k don't support this,
87  // we just usually don't mess with it. it didn't answer a single one of our
88  // issues on windows vista (wfista) anyway, so it's not helping.
89 
90 //const int MAXIMUM_COMMAND_LINE = 32 * KILOBYTE;
91  // maximum command line that we'll deal with here.
92 
93 //#ifndef _MSC_VER
94 void launch_process::exiting_child_signal_handler(int sig_num)
95 {
96  FUNCDEF("exiting_child_signal_handler");
97  if (sig_num != SIGCHLD) {
98  // uhhh, this seems wrong.
99  }
101  for (int i = 0; i < __our_kids().length(); i++) {
102  int status;
103  pid_t exited = waitpid(__our_kids()[i], &status, WNOHANG);
104  if ( (exited == -1) || (exited == __our_kids()[i]) ) {
105  // negative one denotes an error, which we are going to assume means the
106  // process has exited via some other method than our wait. if the value
107  // is the same as the process we waited for, that means it exited.
108  __our_kids().zap(i, i);
109  i--;
110  } else if (exited != 0) {
111  // zero would be okay; this result we do not understand.
112 #ifdef DEBUG_LAUNCH_PROCESS
113  LOG(a_sprintf("unknown result %d waiting for process %d", exited, __our_kids()[i]));
114 #endif
115  }
116  }
117 }
118 //#endif
119 
120 //hmmm: this doesn't seem to account for quoting properly at all?
121 char_star_array launch_process::break_line(astring &app, const astring &parameters)
122 {
123  FUNCDEF("break_line");
124  char_star_array to_return;
125  int_array posns;
126  int num = 0;
127  // find the positions of the spaces and count them.
128  for (int j = 0; j < parameters.length(); j++) {
129  if (parameters[j] == ' ') {
130  num++;
131  posns += j;
132  }
133  }
134  // first, add the app name to the list of parms.
135  to_return += new char[app.length() + 1];
136  app.stuff(to_return[0], app.length());
137  int last_posn = 0;
138  // now add each space-separated parameter to the list.
139  for (int i = 0; i < num; i++) {
140  int len = posns[i] - last_posn;
141  to_return += new char[len + 1];
142  parameters.substring(last_posn, posns[i] - 1).stuff(to_return[i + 1], len);
143  last_posn = posns[i] + 1;
144  }
145  // catch anything left after last separator.
146  if (last_posn < parameters.length() - 1) {
147  int len = parameters.length() - last_posn;
148  to_return += new char[len + 1];
149  parameters.substring(last_posn, parameters.length() - 1)
150  .stuff(to_return[to_return.last()], len);
151  }
152  // add the sentinel to the list of strings.
153  to_return += NULL_POINTER;
154 #ifdef DEBUG_LAUNCH_PROCESS
155  for (int q = 0; to_return[q]; q++) {
156  LOG(a_sprintf("%d: %s\n", q, to_return[q]));
157  }
158 #endif
159  // now a special detour; fix the app name to remove quotes, which are
160  // not friendly to pass to exec.
161  if (app[0] == '"') app.zap(0, 0);
162  if (app[app.end()] == '"') app.zap(app.end(), app.end());
163  return to_return;
164 }
165 
166 basis::un_int launch_process::run(const astring &app_name_in, const astring &command_line,
167  int flag, basis::un_int &child_id)
168 {
169 #ifdef DEBUG_LAUNCH_PROCESS
170  FUNCDEF("run");
171 #endif
172  child_id = 0;
173  astring app_name = app_name_in;
174  if (app_name[0] != '"')
175  app_name.insert(0, "\"");
176  if (app_name[app_name.end()] != '"')
177  app_name += "\"";
178 //#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
179  // unix / linux implementation.
180  if (flag & RETURN_IMMEDIATELY) {
181  // they want to get back right away.
182  pid_t kid_pid = fork();
183 #ifdef DEBUG_LAUNCH_PROCESS
184  LOG(a_sprintf("launch fork returned %d\n", kid_pid));
185 #endif
186  if (!kid_pid) {
187  // this is the child; we now need to launch into what we were asked for.
188 #ifdef DEBUG_LAUNCH_PROCESS
189  LOG(a_sprintf("process %d execing ", application_configuration::process_id()) + app_name
190  + " parms " + command_line + "\n");
191 #endif
192  char_star_array parms = break_line(app_name, command_line);
193  execv(app_name.s(), parms.observe());
194  // oops. failed to exec if we got to here.
195 #ifdef DEBUG_LAUNCH_PROCESS
196  LOG(a_sprintf("child of fork (pid %d) failed to exec, error is ",
197  application_configuration::process_id())
198  + critical_events::system_error_text(critical_events::system_error())
199  + "\n");
200 #endif
201  exit(0); // leave since this is a failed child process.
202  } else {
203  // this is the parent. let's see if the launch worked.
204  if (kid_pid == -1) {
205  // failure.
206  basis::un_int to_return = critical_events::system_error();
207 #ifdef DEBUG_LAUNCH_PROCESS
208  LOG(a_sprintf("parent %d is returning after failing to create, "
209  "error is ", application_configuration::process_id())
210  + critical_events::system_error_text(to_return)
211  + "\n");
212 #endif
213  return to_return;
214  } else {
215  // yes, launch worked okay.
216  child_id = kid_pid;
217  {
219  __our_kids() += kid_pid;
220  }
221  // hook in our child exit signal handler.
222  signal(SIGCHLD, exiting_child_signal_handler);
223 
224 #ifdef DEBUG_LAUNCH_PROCESS
225  LOG(a_sprintf("parent %d is returning after successfully "
226  "creating %d ", application_configuration::process_id(), kid_pid) + app_name
227  + " parms " + command_line + "\n");
228 #endif
229  return 0;
230  }
231  }
232  } else {
233  // assume they want to wait.
234  return system((app_name + " " + command_line).s());
235  }
236 /*
237 #elif defined(_MSC_VER)
238 
239 //checking on whether we have admin rights for the launch.
240 //if (IsUserAnAdmin()) {
241 // MessageBox(0, (astring("IS admin with ") + app_name_in + " " + command_line).s(), "launch process", MB_OK);
242 //} else {
243 // MessageBox(0, (astring("NOT admin for ") + app_name_in + " " + command_line).s(), "launch process", MB_OK);
244 //}
245 
246  PROCESS_INFORMATION process_info;
247  ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
248 
249 #ifdef SUPPORT_SHELL_EXECUTE
250  if (flag & SHELL_EXECUTE) {
251  // new code for using shell execute method--required on vista for proper
252  // launching with the right security tokens.
253  int show_cmd = 0;
254  if (flag & HIDE_APP_WINDOW) {
255  // magic that hides a console window for mswindows.
256  show_cmd = SW_HIDE;
257  } else {
258  show_cmd = SW_SHOWNORMAL;
259  }
260 
261  SHELLEXECUTEINFO exec_info;
262  ZeroMemory(&exec_info, sizeof(SHELLEXECUTEINFO));
263  exec_info.cbSize = sizeof(SHELLEXECUTEINFO);
264  exec_info.fMask = SEE_MASK_NOCLOSEPROCESS // get the process info.
265  | SEE_MASK_FLAG_NO_UI; // turn off any visible error dialogs.
266  exec_info.hwnd = GetDesktopWindow();
267 //hmmm: is get desktop window always appropriate?
268  to_unicode_persist(temp_verb, "open");
269 //does "runas" work on xp also? or anywhere?
270  exec_info.lpVerb = temp_verb;
271  to_unicode_persist(temp_file, app_name);
272  exec_info.lpFile = temp_file;
273  to_unicode_persist(temp_parms, command_line);
274  exec_info.lpParameters = temp_parms;
275  exec_info.nShow = show_cmd;
276 // exec_info.hProcess = &process_info;
277 
278  BOOL worked = ShellExecuteEx(&exec_info);
279  if (!worked)
280  return critical_events::system_error();
281  // copy out the returned process handle.
282  process_info.hProcess = exec_info.hProcess;
283  process_info.dwProcessId = GetProcessId(exec_info.hProcess);
284  } else {
285 #endif //shell exec
286  // standard windows implementation using CreateProcess.
287  STARTUPINFO startup_info;
288  ZeroMemory(&startup_info, sizeof(STARTUPINFO));
289  startup_info.cb = sizeof(STARTUPINFO);
290  int create_flag = 0;
291  if (flag & HIDE_APP_WINDOW) {
292  // magic that hides a console window for mswindows.
293 // version ver = portable::get_OS_version();
294 // version vista_version(6, 0);
295 // if (ver < vista_version) {
296 // // we suspect that this flag is hosing us in vista.
297  create_flag = CREATE_NO_WINDOW;
298 // }
299  }
300  astring parms = app_name + " " + command_line;
301  bool success = CreateProcess(NULL_POINTER, to_unicode_temp(parms), NULL_POINTER, NULL_POINTER, false,
302  create_flag, NULL_POINTER, NULL_POINTER, &startup_info, &process_info);
303  if (!success)
304  return critical_events::system_error();
305  // success then, merge back into stream.
306 
307 #ifdef SUPPORT_SHELL_EXECUTE
308  }
309 #endif //shell exec
310 
311  // common handling for CreateProcess and ShellExecuteEx.
312  child_id = process_info.dwProcessId;
313  basis::un_long retval = 0;
314  if (flag & AWAIT_VIA_POLLING) {
315  // this type of waiting is done without blocking on the process.
316  while (true) {
317  MSG msg;
318  event_poll(msg);
319  // check if the process is gone yet.
320  BOOL ret = GetExitCodeProcess(process_info.hProcess, &retval);
321  if (!ret) {
322  break;
323  } else {
324  // if they aren't saying it's still active, then we will leave.
325  if (retval != STILL_ACTIVE)
326  break;
327  }
328  time_control::sleep_ms(14);
329  }
330  } else if (flag & AWAIT_APP_EXIT) {
331  // they want to wait for the process to exit.
332  WaitForInputIdle(process_info.hProcess, INFINITE);
333  WaitForSingleObject(process_info.hProcess, INFINITE);
334  GetExitCodeProcess(process_info.hProcess, &retval);
335  }
336  // drop the process and thread handles.
337  if (process_info.hProcess)
338  CloseHandle(process_info.hProcess);
339  if (process_info.hThread)
340  CloseHandle(process_info.hThread);
341  return (basis::un_int)retval;
342 #else
343  #pragma error("hmmm: launch_process: no implementation for this OS.")
344 #endif
345 */
346  return 0;
347 }
348 
349 } // namespace.
350 
#define execv
Definition: Xos2defs.h:22
a_sprintf is a specialization of astring that provides printf style support.
Definition: astring.h:440
const contents * observe() const
Returns a pointer to the underlying C array of data.
Definition: array.h:172
int length() const
Returns the current reported length of the allocated C array.
Definition: array.h:115
outcome zap(int start, int end)
Deletes from "this" the objects inclusively between "start" and "end".
Definition: array.h:769
int last() const
Returns the last valid element in the array.
Definition: array.h:118
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition: astring.h:113
virtual void zap(int start, int end)
Deletes the characters between "start" and "end" inclusively.
Definition: astring.cpp:521
bool substring(astring &target, int start, int end) const
a version that stores the substring in an existing "target" string.
Definition: astring.cpp:865
void stuff(char *to_stuff, int count) const
a synonym for copy().
Definition: astring.h:216
void insert(int position, const astring &to_insert)
Copies "to_insert" into "this" at the "position".
Definition: astring.cpp:892
int end() const
returns the index of the last (non-null) character in the string.
Definition: astring.h:86
int length() const
Returns the current length of the string.
Definition: astring.cpp:132
auto_synchronizer simplifies concurrent code by automatically unlocking.
Definition: mutex.h:113
A simple object that wraps a templated array of ints.
Definition: array.h:275
a simple wrapper of an array of char *, used by launch_process::break_line().
A simple object that wraps a templated set of ints.
Definition: set.h:156
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:57
#define LOG(s)
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
unsigned int un_int
Abbreviated name for unsigned integers.
Definition: definitions.h:62
A logger that sends to the console screen using the standard output device.
int_set __our_kids()
mutex & __process_synchronizer()
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
#include <time.h>
Definition: earth_time.cpp:37
Support for unicode builds.
Aids in achievement of platform independence.