1 /* 2 * This file is part of gtkD. 3 * 4 * gtkD is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License 6 * as published by the Free Software Foundation; either version 3 7 * of the License, or (at your option) any later version, with 8 * some exceptions, please read the COPYING file. 9 * 10 * gtkD is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public License 16 * along with gtkD; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA 18 */ 19 20 // generated automatically - do not change 21 // find conversion definition on APILookup.txt 22 // implement new conversion functionalities on the wrap.utils pakage 23 24 25 module glib.Spawn; 26 27 private import core.stdc.stdio; 28 private import core.stdc.string; 29 private import core.thread; 30 private import glib.ErrorG; 31 private import glib.GException; 32 private import glib.Str; 33 private import gtkc.glib; 34 public import gtkc.glibtypes; 35 private import std.string; 36 37 38 public class Spawn 39 { 40 //we need fdopen. 41 version(Posix) 42 { 43 private import core.sys.posix.stdio; 44 } 45 //fdopen for Windows is defined in gtkc.glibtypes. 46 47 string workingDirectory = "."; 48 string[] argv; 49 string[] envp; 50 GSpawnFlags flags = SpawnFlags.SEARCH_PATH; 51 GSpawnChildSetupFunc childSetup; 52 void* userData; 53 GPid childPid; 54 FILE* standardInput; 55 FILE* standardOutput; 56 FILE* standardError; 57 GError* error; 58 int stdIn; 59 int stdOut; 60 int stdErr; 61 62 // for commandLineSync 63 int exitStatus; 64 char* strOutput; 65 char* strError; 66 67 alias bool delegate(Spawn) ChildWatch; 68 ChildWatch externalWatch; 69 70 /** 71 * Creates a Spawn for execution. 72 */ 73 public this(string program, string[] envp=null) 74 { 75 argv ~= program; 76 this.envp = envp; 77 } 78 79 /** 80 * Creates a Spawn for execution. 81 */ 82 public this(string[] program, string[] envp=null) 83 { 84 argv = program; 85 this.envp = envp; 86 } 87 88 /** 89 * Adds a delegate to be notified on the end of the child process. 90 * Params: 91 * dlg = 92 */ 93 public void addChildWatch(ChildWatch dlg) 94 { 95 externalWatch = dlg; 96 } 97 98 /** 99 * Closes all open streams and child process. 100 */ 101 public void close() 102 { 103 if (stdIn != 0 ) 104 { 105 fclose(standardInput); 106 stdIn = 0; 107 } 108 if (stdOut != 0 ) 109 { 110 fclose(standardOutput); 111 stdOut = 0; 112 } 113 if (stdErr != 0 ) 114 { 115 fclose(standardError); 116 stdErr = 0; 117 } 118 if ( childPid != 0 ) 119 { 120 closePid(childPid); 121 childPid = 0; 122 } 123 } 124 125 /** 126 * Adds a parameter to the execution program 127 */ 128 public void addParm(string parm) 129 { 130 argv ~= parm; 131 } 132 133 /** 134 * Gets the last error message 135 */ 136 public string getLastError() 137 { 138 if ( error != null ) 139 { 140 return Str.toString(error.message); 141 } 142 return ""; 143 } 144 145 /** 146 * Executes the prepared process 147 */ 148 public int execAsyncWithPipes( 149 ChildWatch externalWatch = null, 150 bool delegate(string) readOutput = null, 151 bool delegate(string) readError = null ) 152 { 153 int result = g_spawn_async_with_pipes( 154 Str.toStringz(workingDirectory), 155 Str.toStringzArray(argv), 156 Str.toStringzArray(envp), 157 flags, 158 childSetup, 159 userData, 160 &childPid, 161 &stdIn, 162 &stdOut, 163 &stdErr, 164 &error 165 ); 166 167 if ( result != 0 ) 168 { 169 this.externalWatch = externalWatch; 170 g_child_watch_add(childPid, cast(GChildWatchFunc)(&childWatchCallback), cast(void*)this); 171 standardInput = fdopen(stdIn, Str.toStringz("w")); 172 standardOutput = fdopen(stdOut, Str.toStringz("r")); 173 standardError = fdopen(stdErr, Str.toStringz("r")); 174 175 if ( readOutput !is null ) 176 { 177 (new ReadFile(standardOutput, readOutput)).start(); 178 } 179 if ( readError !is null ) 180 { 181 (new ReadFile(standardError, readError)).start(); 182 } 183 } 184 185 return result; 186 } 187 188 class ReadFile : Thread 189 { 190 bool delegate(string) read; 191 FILE* file; 192 193 int lineCount; 194 195 this(FILE* file, bool delegate (string) read ) 196 { 197 this.file = file; 198 this.read = read; 199 200 super(&run); 201 } 202 203 public void run() 204 { 205 string line = readLine(file); 206 while( line !is null ) 207 { 208 ++lineCount; 209 if ( read !is null ) 210 { 211 read(line); 212 } 213 line = readLine(file); 214 } 215 } 216 } 217 218 private string readLine(FILE* stream, int max=4096) 219 { 220 if ( feof(stream) ) 221 { 222 if ( externalWatch !is null ) 223 { 224 externalWatch(this); 225 } 226 return null; 227 } 228 string line; 229 line.length = max+1; 230 char* lineP = fgets(Str.toStringz(line), max, stream); 231 if ( lineP is null ) 232 { 233 return ""; 234 } 235 size_t l = strlen(line.ptr); 236 if ( l > 0 ) --l; 237 238 return line[0..l]; 239 } 240 241 extern(C) static void childWatchCallback(int pid, int status, Spawn spawn) 242 { 243 //writefln("Spawn.childWatchCallback %s %s", pid, status); 244 spawn.exitStatus = status; 245 if ( spawn.externalWatch !is null ) 246 { 247 spawn.externalWatch(spawn); 248 } 249 spawn.close(); 250 } 251 252 253 public bool endOfOutput() 254 { 255 if ( standardOutput is null ) return true; 256 return feof(standardOutput) != 0; 257 } 258 259 public bool endOfError() 260 { 261 if ( standardError is null ) return true; 262 return feof(standardError) != 0; 263 } 264 265 string getOutputString() 266 { 267 return Str.toString(strOutput); 268 } 269 270 string getErrorString() 271 { 272 return Str.toString(strError); 273 } 274 275 int getExitStatus() 276 { 277 return exitStatus; 278 } 279 280 /** 281 * Executes a command synchronasly and 282 * optionally calls delegates for sysout, syserr and end of job 283 * 284 */ 285 public int commandLineSync( 286 ChildWatch externalWatch = null, 287 bool delegate(string) readOutput = null, 288 bool delegate(string) readError = null ) 289 { 290 string commandLine; 291 foreach ( int count, string arg; argv) 292 { 293 if ( count > 0 ) 294 { 295 commandLine ~= ' '; 296 } 297 commandLine ~= arg; 298 } 299 int status = g_spawn_command_line_sync( 300 Str.toStringz(commandLine), 301 &strOutput, 302 &strError, 303 &exitStatus, 304 &error); 305 if ( readOutput != null ) 306 { 307 foreach ( string line ; splitLines(Str.toString(strOutput)) ) 308 { 309 readOutput(line); 310 } 311 } 312 if ( readError != null ) 313 { 314 foreach ( string line ; splitLines(Str.toString(strError)) ) 315 { 316 readError(line); 317 } 318 } 319 if ( externalWatch != null ) 320 { 321 externalWatch(this); 322 } 323 return status; 324 } 325 326 /** 327 */ 328 329 /** 330 * See g_spawn_async_with_pipes() for a full description; this function 331 * simply calls the g_spawn_async_with_pipes() without any pipes. 332 * 333 * You should call g_spawn_close_pid() on the returned child process 334 * reference when you don't need it any more. 335 * 336 * If you are writing a GTK+ application, and the program you are 337 * spawning is a graphical application, too, then you may want to 338 * use gdk_spawn_on_screen() instead to ensure that the spawned program 339 * opens its windows on the right screen. 340 * 341 * Note that the returned @child_pid on Windows is a handle to the child 342 * process and not its identifier. Process handles and process identifiers 343 * are different concepts on Windows. 344 * 345 * Params: 346 * workingDirectory = child's current working directory, or %NULL to inherit parent's 347 * argv = child's argument vector 348 * envp = child's environment, or %NULL to inherit parent's 349 * flags = flags from #GSpawnFlags 350 * childSetup = function to run in the child just before exec() 351 * userData = user data for @child_setup 352 * childPid = return location for child process reference, or %NULL 353 * 354 * Return: %TRUE on success, %FALSE if error is set 355 * 356 * Throws: GException on failure. 357 */ 358 public static bool async(string workingDirectory, string[] argv, string[] envp, GSpawnFlags flags, GSpawnChildSetupFunc childSetup, void* userData, out GPid childPid) 359 { 360 GError* err = null; 361 362 auto p = g_spawn_async(Str.toStringz(workingDirectory), Str.toStringzArray(argv), Str.toStringzArray(envp), flags, childSetup, userData, &childPid, &err) != 0; 363 364 if (err !is null) 365 { 366 throw new GException( new ErrorG(err) ); 367 } 368 369 return p; 370 } 371 372 /** 373 * Set @error if @exit_status indicates the child exited abnormally 374 * (e.g. with a nonzero exit code, or via a fatal signal). 375 * 376 * The g_spawn_sync() and g_child_watch_add() family of APIs return an 377 * exit status for subprocesses encoded in a platform-specific way. 378 * On Unix, this is guaranteed to be in the same format waitpid() returns, 379 * and on Windows it is guaranteed to be the result of GetExitCodeProcess(). 380 * 381 * Prior to the introduction of this function in GLib 2.34, interpreting 382 * @exit_status required use of platform-specific APIs, which is problematic 383 * for software using GLib as a cross-platform layer. 384 * 385 * Additionally, many programs simply want to determine whether or not 386 * the child exited successfully, and either propagate a #GError or 387 * print a message to standard error. In that common case, this function 388 * can be used. Note that the error message in @error will contain 389 * human-readable information about the exit status. 390 * 391 * The @domain and @code of @error have special semantics in the case 392 * where the process has an "exit code", as opposed to being killed by 393 * a signal. On Unix, this happens if WIFEXITED() would be true of 394 * @exit_status. On Windows, it is always the case. 395 * 396 * The special semantics are that the actual exit code will be the 397 * code set in @error, and the domain will be %G_SPAWN_EXIT_ERROR. 398 * This allows you to differentiate between different exit codes. 399 * 400 * If the process was terminated by some means other than an exit 401 * status, the domain will be %G_SPAWN_ERROR, and the code will be 402 * %G_SPAWN_ERROR_FAILED. 403 * 404 * This function just offers convenience; you can of course also check 405 * the available platform via a macro such as %G_OS_UNIX, and use 406 * WIFEXITED() and WEXITSTATUS() on @exit_status directly. Do not attempt 407 * to scan or parse the error message string; it may be translated and/or 408 * change in future versions of GLib. 409 * 410 * Params: 411 * exitStatus = An exit code as returned from g_spawn_sync() 412 * 413 * Return: %TRUE if child exited successfully, %FALSE otherwise (and 414 * @error will be set) 415 * 416 * Since: 2.34 417 * 418 * Throws: GException on failure. 419 */ 420 public static bool checkExitStatus(int exitStatus) 421 { 422 GError* err = null; 423 424 auto p = g_spawn_check_exit_status(exitStatus, &err) != 0; 425 426 if (err !is null) 427 { 428 throw new GException( new ErrorG(err) ); 429 } 430 431 return p; 432 } 433 434 /** 435 * On some platforms, notably Windows, the #GPid type represents a resource 436 * which must be closed to prevent resource leaking. g_spawn_close_pid() 437 * is provided for this purpose. It should be used on all platforms, even 438 * though it doesn't do anything under UNIX. 439 * 440 * Params: 441 * pid = The process reference to close 442 */ 443 public static void closePid(GPid pid) 444 { 445 g_spawn_close_pid(pid); 446 } 447 448 /** 449 * A simple version of g_spawn_async() that parses a command line with 450 * g_shell_parse_argv() and passes it to g_spawn_async(). Runs a 451 * command line in the background. Unlike g_spawn_async(), the 452 * %G_SPAWN_SEARCH_PATH flag is enabled, other flags are not. Note 453 * that %G_SPAWN_SEARCH_PATH can have security implications, so 454 * consider using g_spawn_async() directly if appropriate. Possible 455 * errors are those from g_shell_parse_argv() and g_spawn_async(). 456 * 457 * The same concerns on Windows apply as for g_spawn_command_line_sync(). 458 * 459 * Params: 460 * commandLine = a command line 461 * 462 * Return: %TRUE on success, %FALSE if error is set 463 * 464 * Throws: GException on failure. 465 */ 466 public static bool commandLineAsync(string commandLine) 467 { 468 GError* err = null; 469 470 auto p = g_spawn_command_line_async(Str.toStringz(commandLine), &err) != 0; 471 472 if (err !is null) 473 { 474 throw new GException( new ErrorG(err) ); 475 } 476 477 return p; 478 } 479 480 /** 481 * A simple version of g_spawn_sync() with little-used parameters 482 * removed, taking a command line instead of an argument vector. See 483 * g_spawn_sync() for full details. @command_line will be parsed by 484 * g_shell_parse_argv(). Unlike g_spawn_sync(), the %G_SPAWN_SEARCH_PATH flag 485 * is enabled. Note that %G_SPAWN_SEARCH_PATH can have security 486 * implications, so consider using g_spawn_sync() directly if 487 * appropriate. Possible errors are those from g_spawn_sync() and those 488 * from g_shell_parse_argv(). 489 * 490 * If @exit_status is non-%NULL, the platform-specific exit status of 491 * the child is stored there; see the documentation of 492 * g_spawn_check_exit_status() for how to use and interpret this. 493 * 494 * On Windows, please note the implications of g_shell_parse_argv() 495 * parsing @command_line. Parsing is done according to Unix shell rules, not 496 * Windows command interpreter rules. 497 * Space is a separator, and backslashes are 498 * special. Thus you cannot simply pass a @command_line containing 499 * canonical Windows paths, like "c:\\program files\\app\\app.exe", as 500 * the backslashes will be eaten, and the space will act as a 501 * separator. You need to enclose such paths with single quotes, like 502 * "'c:\\program files\\app\\app.exe' 'e:\\folder\\argument.txt'". 503 * 504 * Params: 505 * commandLine = a command line 506 * standardOutput = return location for child output 507 * standardError = return location for child errors 508 * exitStatus = return location for child exit status, as returned by waitpid() 509 * 510 * Return: %TRUE on success, %FALSE if an error was set 511 * 512 * Throws: GException on failure. 513 */ 514 public static bool commandLineSync(string commandLine, out string standardOutput, out string standardError, out int exitStatus) 515 { 516 char* outstandardOutput = null; 517 char* outstandardError = null; 518 GError* err = null; 519 520 auto p = g_spawn_command_line_sync(Str.toStringz(commandLine), &outstandardOutput, &outstandardError, &exitStatus, &err) != 0; 521 522 if (err !is null) 523 { 524 throw new GException( new ErrorG(err) ); 525 } 526 527 standardOutput = Str.toString(outstandardOutput); 528 standardError = Str.toString(outstandardError); 529 530 return p; 531 } 532 533 public static GQuark errorQuark() 534 { 535 return g_spawn_error_quark(); 536 } 537 538 public static GQuark exitErrorQuark() 539 { 540 return g_spawn_exit_error_quark(); 541 } 542 543 /** 544 * Executes a child synchronously (waits for the child to exit before returning). 545 * All output from the child is stored in @standard_output and @standard_error, 546 * if those parameters are non-%NULL. Note that you must set the 547 * %G_SPAWN_STDOUT_TO_DEV_NULL and %G_SPAWN_STDERR_TO_DEV_NULL flags when 548 * passing %NULL for @standard_output and @standard_error. 549 * 550 * If @exit_status is non-%NULL, the platform-specific exit status of 551 * the child is stored there; see the documentation of 552 * g_spawn_check_exit_status() for how to use and interpret this. 553 * Note that it is invalid to pass %G_SPAWN_DO_NOT_REAP_CHILD in 554 * @flags. 555 * 556 * If an error occurs, no data is returned in @standard_output, 557 * @standard_error, or @exit_status. 558 * 559 * This function calls g_spawn_async_with_pipes() internally; see that 560 * function for full details on the other parameters and details on 561 * how these functions work on Windows. 562 * 563 * Params: 564 * workingDirectory = child's current working directory, or %NULL to inherit parent's 565 * argv = child's argument vector 566 * envp = child's environment, or %NULL to inherit parent's 567 * flags = flags from #GSpawnFlags 568 * childSetup = function to run in the child just before exec() 569 * userData = user data for @child_setup 570 * standardOutput = return location for child output, or %NULL 571 * standardError = return location for child error messages, or %NULL 572 * exitStatus = return location for child exit status, as returned by waitpid(), or %NULL 573 * 574 * Return: %TRUE on success, %FALSE if an error was set 575 * 576 * Throws: GException on failure. 577 */ 578 public static bool sync(string workingDirectory, string[] argv, string[] envp, GSpawnFlags flags, GSpawnChildSetupFunc childSetup, void* userData, out string standardOutput, out string standardError, out int exitStatus) 579 { 580 char* outstandardOutput = null; 581 char* outstandardError = null; 582 GError* err = null; 583 584 auto p = g_spawn_sync(Str.toStringz(workingDirectory), Str.toStringzArray(argv), Str.toStringzArray(envp), flags, childSetup, userData, &outstandardOutput, &outstandardError, &exitStatus, &err) != 0; 585 586 if (err !is null) 587 { 588 throw new GException( new ErrorG(err) ); 589 } 590 591 standardOutput = Str.toString(outstandardOutput); 592 standardError = Str.toString(outstandardError); 593 594 return p; 595 } 596 }