a580e12000-09-27Fredrik Hübinette (Hubbe) #pike __REAL_VERSION__
a20af62000-09-26Fredrik Hübinette (Hubbe) 
8f6e2e2001-01-19Per Hedbor constant create_process = __builtin.create_process;
dad69b2003-03-01Henrik Grubbström (Grubba) #if constant(__builtin.TraceProcess) constant TraceProcess = __builtin.TraceProcess; #endif
1813e82013-03-16Chris Angelico #if defined(__NT__) || defined(__amigaos__) || defined(__OS2__)
f9935c2013-03-05Henrik Grubbström (Grubba) constant path_separator = ";"; #else constant path_separator = ":"; #endif
d0344c2011-09-08Henrik Grubbström (Grubba) #if constant(Stdio.__HAVE_SEND_FD__) protected Stdio.File forkd_pipe; protected create_process forkd_pid; //! Encoder for data to be sent to @[Tools.Standalone.forkd]. //! //! @seealso //! @[ForkdDecoder] class ForkdEncoder(Stdio.File remote_fd) { int fd_counter; mixed nameof(mixed what) { if (objectp(what)) { if (what->_fd) { remote_fd->send_fd(what->_fd); return fd_counter++; } } error("Unsupported argument for RemoteProcess.\n"); } } //! Decoder for data received by @[Tools.Standalone.forkd]. //! //! @seealso //! @[ForkdEncoder] class ForkdDecoder(array(Stdio.Fd) fds) { object objectof(mixed code) { return fds[code]; } } // Ensure that there's a live forkd running. protected int assert_forkd() { if (!forkd_pid || !forkd_pid->kill(0)) { forkd_pipe = Stdio.File(); forkd_pid = spawn_pike(({ "-x", "forkd" }), ([ "forkd":0, "uid":getuid(), "gid":getgid(), "noinitgroups":1, "fds":({ forkd_pipe->pipe(Stdio.PROP_BIDIRECTIONAL| Stdio.PROP_SEND_FD), }), ]));
63e0892011-09-20Henrik Grubbström (Grubba)  if (forkd_pipe->read(1) != "\0") { // werror("Failed to start forkd.\n"); return 0; }
d0344c2011-09-08Henrik Grubbström (Grubba)  } return forkd_pid && forkd_pid->kill(0); } #endif
5eeb5f2011-09-12Henrik Grubbström (Grubba) protected int(0..1) forkd_default = 0; //! Set the default value for the @expr{"forkd"@} modifier //! to @[Process]. //! //! @note //! The default default value is @expr{0@} (zero). //! //! @seealso //! @[get_forkd_default()], @[Process()->create()] void set_forkd_default(int(0..1) mode) { forkd_default = mode; } //! Get the default value for the @expr{"forkd"@} modifier //! to @[Process]. //! //! @note //! The default default value is @expr{0@} (zero). //! //! @seealso //! @[set_forkd_default()], @[Process()->create()] int(0..1) get_forkd_default() { return forkd_default; }
7f12112003-01-02Martin Nilsson //! Slightly polished version of @[create_process].
d0344c2011-09-08Henrik Grubbström (Grubba) //! //! In addition to the features supported by @[create_process], //! it also supports: //! //! @ul //! @item //! Callbacks on timeout and process termination. //! @item //! Using @[Tools.Standalone.forkd] via RPC to //! spawn the new process. //! @endul //!
7f12112003-01-02Martin Nilsson //! @seealso
d0344c2011-09-08Henrik Grubbström (Grubba) //! @[create_process], @[Tools.Standalone.forkd]
8f6e2e2001-01-19Per Hedbor class Process {
d0344c2011-09-08Henrik Grubbström (Grubba)  //! Based on @[create_process].
4d18352003-11-11Henrik Grubbström (Grubba)  inherit create_process;
d0344c2011-09-08Henrik Grubbström (Grubba) 
9eaf1d2008-06-28Martin Nilsson  protected function(Process:void) read_cb; protected function(Process:void) timeout_cb;
8f6e2e2001-01-19Per Hedbor 
d0344c2011-09-08Henrik Grubbström (Grubba)  protected Stdio.File process_fd; protected Pike.Backend process_backend;
7d0bcc2011-09-15Henrik Grubbström (Grubba)  protected mixed process_poll;
d0344c2011-09-08Henrik Grubbström (Grubba)  //! @param command_args
7f12112003-01-02Martin Nilsson  //! Either a command line array, as the command_args
e65ef02003-09-24Henrik Grubbström (Grubba)  //! argument to @[create_process()], or a string that
7f12112003-01-02Martin Nilsson  //! will be splitted into a command line array by
e65ef02003-09-24Henrik Grubbström (Grubba)  //! calling @[split_quoted_string()] in an operating //! system dependant mode.
d0344c2011-09-08Henrik Grubbström (Grubba)  //! @param modifiers
7448562003-09-20David Gourdelier  //! In addition to the modifiers that @[create_process] accepts, //! this object also accepts //! @mapping //! @member function(Process:void) "read_callback" //! This callback is called when there is data to be read //! from the process. //! @member function(Process:void) "timeout_callback" //! This callback is called if the process times out. //! @member int "timeout" //! The time it takes for the process to time out. Default is //! 15 seconds.
d0344c2011-09-08Henrik Grubbström (Grubba)  //! @member int(0..1) "forkd" //! Use @[Tools.Standalone.forkd] to actually spawn the process.
7448562003-09-20David Gourdelier  //! @endmapping
5eeb5f2011-09-12Henrik Grubbström (Grubba)  //! //! @note //! The default value for the @expr{"forkd"@} modifier may be //! set via @[set_forkd_default()]. //!
7f12112003-01-02Martin Nilsson  //! @seealso
d0344c2011-09-08Henrik Grubbström (Grubba)  //! @[create_process], @[create_process()->create()],
5eeb5f2011-09-12Henrik Grubbström (Grubba)  //! @[split_quoted_string()], @[Tools.Standalone.forkd], //! @[set_forkd_default()], @[get_forkd_default()]
d0344c2011-09-08Henrik Grubbström (Grubba)  protected void create( string|array(string) command_args, void|mapping(string:mixed) modifiers )
8f6e2e2001-01-19Per Hedbor  {
d0344c2011-09-08Henrik Grubbström (Grubba)  if( stringp( command_args ) ) { command_args = split_quoted_string( [string]command_args
e65ef02003-09-24Henrik Grubbström (Grubba) #ifdef __NT__ ,1 #endif /* __NT__ */ ); }
7448562003-09-20David Gourdelier 
18ff362011-09-22Henrik Grubbström (Grubba)  mapping(string:mixed) new_modifiers = (modifiers || ([])) + ([]);
d0344c2011-09-08Henrik Grubbström (Grubba)  if (new_modifiers->keep_signals) { // This option is currently not supported with forkd. m_delete(new_modifiers, "forkd"); } #if constant(Stdio.__HAVE_SEND_FD__) // Forkd mode requires send_fd().
5eeb5f2011-09-12Henrik Grubbström (Grubba)  if (zero_type(new_modifiers->forkd)) new_modifiers->forkd = forkd_default;
d0344c2011-09-08Henrik Grubbström (Grubba)  if (new_modifiers->forkd && assert_forkd()) { process_fd = Stdio.File(); forkd_pipe-> send_fd(process_fd->pipe(Stdio.PROP_BIDIRECTIONAL|Stdio.PROP_SEND_FD));
63e0892011-09-20Henrik Grubbström (Grubba)  while (forkd_pipe->write("\0") < 0) ;
d0344c2011-09-08Henrik Grubbström (Grubba)  m_delete(new_modifiers, "forkd"); __callback = m_delete(new_modifiers, "callback"); if (zero_type(new_modifiers->uid)) { new_modifiers->uid = geteuid(); if (zero_type(new_modifiers->gid)) { new_modifiers->gid = getegid(); } if (!new_modifiers->setgroups) { new_modifiers->setgroups = getgroups(); } } else if (new_modifiers->noinitgroups) { if (!new_modifiers->setgroups) { new_modifiers->setgroups = getgroups(); } } m_delete(new_modifiers, "noinitgroups"); if (!new_modifiers->env) { new_modifiers->env = getenv(); } if (new_modifiers->cwd) { new_modifiers->cwd = combine_path(getcwd(), new_modifiers->cwd); } else { new_modifiers->cwd = getcwd(); } if (!new_modifiers->stdin) new_modifiers->stdin = Stdio.stdin; if (!new_modifiers->stdout) new_modifiers->stdout = Stdio.stdout; if (!new_modifiers->stderr) new_modifiers->stderr = Stdio.stderr; string data = encode_value(({ command_args, new_modifiers }), ForkdEncoder(process_fd)); data = sprintf("%4c%s", sizeof(data), data);
63e0892011-09-20Henrik Grubbström (Grubba)  // Wait for forkd to acknowledge the process_fd. if (process_fd->read(4) != "\0\0\0\0") { process_fd->close(); process_fd = UNDEFINED; error("Failed to create spawn request.\n"); }
d0344c2011-09-08Henrik Grubbström (Grubba)  int bytes = process_fd->write(data); if (bytes != sizeof(data)) { process_fd->close(); process_fd = UNDEFINED; error("Failed to write spawn request (%d != %d).\n", bytes, sizeof(data)); } process_backend = Pike.SmallBackend(); process_backend->add_file(process_fd); process_fd->set_nonblocking(got_data, UNDEFINED, got_close); // Wait for the process to fork (or fail). while (__status == -1) { process_backend(3600.0); }
7d0bcc2011-09-15Henrik Grubbström (Grubba)  if (process_fd) { process_poll = call_out(do_poll_loop, 0.1); }
d0344c2011-09-08Henrik Grubbström (Grubba)  } else #endif { ::create( [array(string)]command_args, new_modifiers ); } if (modifiers) { if(read_cb = modifiers->read_callback)
b7f61b2005-04-09Martin Nilsson  call_out(watcher, 0.1);
7448562003-09-20David Gourdelier 
d0344c2011-09-08Henrik Grubbström (Grubba)  if( (timeout_cb = modifiers->timeout_callback) || modifiers->timeout ) call_out(killer, modifiers->timeout||15); } } #if constant(Stdio.__HAVE_SEND_FD__) protected void do_close() { if (process_fd) { process_fd->set_blocking(); process_fd->close(); } process_fd = UNDEFINED; process_backend = UNDEFINED;
7d0bcc2011-09-15Henrik Grubbström (Grubba)  remove_call_out(process_poll);
d0344c2011-09-08Henrik Grubbström (Grubba)  } protected string recv_buf = ""; protected void got_data(mixed ignored, string data) { recv_buf += data; while (sizeof(recv_buf) > 3) { while (has_prefix(recv_buf, "\0\0\0\0")) recv_buf = recv_buf[4..]; if (sizeof(recv_buf) < 5) return; int len = 0; sscanf(recv_buf, "%04c", len); if (sizeof(recv_buf) < len + 4) return; data = recv_buf[4..len+3]; recv_buf = recv_buf[len+4..]; [string tag, mixed packet] = decode_value(data); switch(tag) { case "ERROR": __result = 255; __status = 2; do_close(); error(packet); break; case "PID": __pid = packet; if (__status == -1) __status = 0; if (__callback) __callback(this_object()); break; case "SIGNAL": __last_signal = packet; break; case "START": __status = 0; if (__callback) __callback(this_object()); break; case "STOP": __status = 1; if (__callback) __callback(this_object()); break; case "EXIT": __result = packet; __status = 2; do_close(); if (__callback) __callback(this_object()); break; default: __result = 255; __status = 2; do_close(); error("Unsupported packet from forkd: %O\n", tag); break; } } } protected void got_close(mixed ignored) { do_close(); if (__status == -1) { // Early close. __result = 255; __status = 2; } }
7d0bcc2011-09-15Henrik Grubbström (Grubba)  protected void do_poll(float t) { mixed err = catch { process_backend && process_backend(t); return; }; // Filter errors about the backend already running. if (arrayp(err) && sizeof(err) && stringp(err[0]) && has_prefix(err[0], "Backend already ")) return; throw(err); } protected void do_poll_loop() { process_poll = UNDEFINED; if (!process_backend) return; do_poll(0.0); process_poll = call_out(do_poll_loop, 0.1); }
d0344c2011-09-08Henrik Grubbström (Grubba)  int last_signal() {
7d0bcc2011-09-15Henrik Grubbström (Grubba)  do_poll(0.0);
d0344c2011-09-08Henrik Grubbström (Grubba)  return ::last_signal(); } int(-1..2) status() {
7d0bcc2011-09-15Henrik Grubbström (Grubba)  do_poll(0.0);
d0344c2011-09-08Henrik Grubbström (Grubba)  return ::status(); } int wait() { if (process_backend) {
7d0bcc2011-09-15Henrik Grubbström (Grubba)  do_poll(0.0);
d0344c2011-09-08Henrik Grubbström (Grubba)  while (__status <= 0) {
7d0bcc2011-09-15Henrik Grubbström (Grubba)  do_poll(3600.0);
d0344c2011-09-08Henrik Grubbström (Grubba)  } return __result;
b7f61b2005-04-09Martin Nilsson  }
d0344c2011-09-08Henrik Grubbström (Grubba)  return ::wait();
7448562003-09-20David Gourdelier  }
d0344c2011-09-08Henrik Grubbström (Grubba) #endif /* __HAVE_SEND_FD__ */
7448562003-09-20David Gourdelier 
9eaf1d2008-06-28Martin Nilsson  protected void destroy() {
7448562003-09-20David Gourdelier  remove_call_out(watcher); remove_call_out(killer);
7d0bcc2011-09-15Henrik Grubbström (Grubba) #if constant(Stdio.__HAVE_SEND_FD__) remove_call_out(process_poll); #endif
7448562003-09-20David Gourdelier  }
9eaf1d2008-06-28Martin Nilsson  protected void watcher() {
7448562003-09-20David Gourdelier  // It was another sigchld, but not one from our process.
d0344c2011-09-08Henrik Grubbström (Grubba)  if(status()==0)
7448562003-09-20David Gourdelier  call_out(watcher, 0.1); else { remove_call_out(killer); if(read_cb) read_cb(this); } }
9eaf1d2008-06-28Martin Nilsson  protected void killer() {
7448562003-09-20David Gourdelier  remove_call_out(watcher); #if constant(kill) ::kill(signum("SIGKILL")); #endif if(timeout_cb) timeout_cb(this);
8f6e2e2001-01-19Per Hedbor  }
8c2b7c2005-08-18Henrik Grubbström (Grubba) } // FIXME: Should probably be located elsewhere. string locate_binary(array(string) path, string name) {
7bac782012-07-12Martin Nilsson #ifdef __NT__ if( !has_suffix(lower_case(name), ".exe") ) name += ".exe"; #endif foreach(path, string dir)
8c2b7c2005-08-18Henrik Grubbström (Grubba)  {
7bac782012-07-12Martin Nilsson  string fname = combine_path(dir, name); Stdio.Stat info = file_stat(fname); if (info && (info->mode & 0111))
8c2b7c2005-08-18Henrik Grubbström (Grubba)  return fname; } return 0; }
9eaf1d2008-06-28Martin Nilsson protected array(string) runpike;
8c2b7c2005-08-18Henrik Grubbström (Grubba)  //! Spawn a new pike process similar to the current. //! //! @param argv //! Arguments for the new process. //! //! @param options
3630b82012-07-15Bill Welliver //! Process creation options. See @[Process.Process] for details. May also //! specify "add_predefines", "add_program_path", or "add_include_path" in //! order to include these components in command path (module path is //! included by default.)
8c2b7c2005-08-18Henrik Grubbström (Grubba) //! //! @seealso //! @[Process.Process] Process spawn_pike(array(string) argv, void|mapping(string:mixed) options) { if (!runpike) { array(string) res = ({ master()->_pike_file_name, }); if (master()->_master_file_name) res+=({"-m"+master()->_master_file_name}); foreach (master()->pike_module_path;;string path)
3630b82012-07-15Bill Welliver  res+=({"-M" + path});
1f97002012-07-16Bill Welliver  if(options && options->add_predefines)
3630b82012-07-15Bill Welliver  { foreach (master()->predefines; string key; string value)
dbadff2013-11-03Per Hedbor  if( stringp( value ) ) res+=({"-D" + key + "=" + value}); else if( intp( value ) ) res+=({"-D" + key });
3630b82012-07-15Bill Welliver  }
1f97002012-07-16Bill Welliver  if(options && options->add_program_path)
3630b82012-07-15Bill Welliver  {
1f97002012-07-16Bill Welliver  foreach (master()->pike_program_path;; string value) res+=({"-P" + value});
3630b82012-07-15Bill Welliver  }
1f97002012-07-16Bill Welliver  if(options && options->add_include_path)
3630b82012-07-15Bill Welliver  {
1f97002012-07-16Bill Welliver  foreach (master()->pike_include_path;; string value)
0ba85a2012-07-16Bill Welliver  res+=({"-I" + value});
3630b82012-07-15Bill Welliver  }
8c2b7c2005-08-18Henrik Grubbström (Grubba)  if (sizeof(res) && !has_value(res[0],"/") #ifdef __NT__ && !has_value(res[0], "\\") #endif /* __NT__ */ )
f9935c2013-03-05Henrik Grubbström (Grubba)  res[0] = search_path(res[0]);
8c2b7c2005-08-18Henrik Grubbström (Grubba)  runpike = res; } return Process(runpike + argv, options);
e5d92a2006-02-12Peter Bortas } //! Easy and lazy way of using @[Process.Process] that runs a process //! and returns a mapping with the output and exit code without //! having to make sure you read nonblocking yourself. //! //! @param args //! Either a command line array, as the command_args //! argument to @[create_process()], or a string that //! will be splitted into a command line array by //! calling @[split_quoted_string()] in an operating //! system dependant mode. //! @param modifiers //! It takes all the modifiers @[Process.Process] accepts, with //! the exception of stdout and stderr. Since the point of this //! function is to handle those you can not supply your own. //!
bb7bd92008-07-15Henrik Grubbström (Grubba) //! If @expr{modifiers->stdin@} is set to a string it will automaticly be
0ee0992008-07-14Peter Bortas //! converted to a pipe that is fed to stdin of the started process. //!
e5d92a2006-02-12Peter Bortas //! @seealso //! @[Process.Process] @[create_process] //! //! @returns //! @mapping //! @member string "stdout" //! Everything the process wrote on stdout. //! @member string "stderr" //! Everything the process wrote on stderr. //! @member int "exitcode" //! The process' exitcode. //! @endmapping //! //! @note //! As the entire output of stderr and stdout is stored in the //! returned mapping it could potentially grow until memory runs out. //! It is therefor adviceable to set up rlimits if the output has a
cade4f2009-08-28Peter Bortas //! potential to be very large.
e5d92a2006-02-12Peter Bortas //! //! @example //! Process.run( ({ "ls", "-l" }) ); //! Process.run( ({ "ls", "-l" }), ([ "cwd":"/etc" ]) );
0a0c5e2008-07-14Peter Bortas //! Process.run( "ls -l" ); //! Process.run( "awk -F: '{print $2}'", ([ "stdin":"foo:2\nbar:17\n" ]) );
e5d92a2006-02-12Peter Bortas mapping run(string|array(string) cmd, void|mapping modifiers) {
0a0c5e2008-07-14Peter Bortas  string gotstdout="", gotstderr="", stdin_str;
e5d92a2006-02-12Peter Bortas  int exitcode; if(!modifiers) modifiers = ([]); if(modifiers->stdout || modifiers->stderr)
60f7be2011-01-13Peter Bortas  throw( ({ "Can not redirect stdout or stderr in Process.run, "
e5d92a2006-02-12Peter Bortas  "please use Process.Process instead.", backtrace() }) ); Stdio.File mystdout = Stdio.File(); Stdio.File mystderr = Stdio.File();
bb7bd92008-07-15Henrik Grubbström (Grubba)  Stdio.File mystdin;
e5d92a2006-02-12Peter Bortas 
0a0c5e2008-07-14Peter Bortas  object p;
bb7bd92008-07-15Henrik Grubbström (Grubba)  if(stringp(modifiers->stdin))
0a0c5e2008-07-14Peter Bortas  {
bb7bd92008-07-15Henrik Grubbström (Grubba)  mystdin = Stdio.File();
0a0c5e2008-07-14Peter Bortas  stdin_str = modifiers->stdin; p = Process(cmd, modifiers + ([ "stdout":mystdout->pipe(), "stderr":mystderr->pipe(), "stdin":mystdin->pipe(Stdio.PROP_IPC|Stdio.PROP_REVERSE) ])); } else p = Process(cmd, modifiers + ([ "stdout":mystdout->pipe(), "stderr":mystderr->pipe(), ]));
e5d92a2006-02-12Peter Bortas 
12d9d42010-11-05Martin Stjernholm #if 0 //constant(Thread.Thread) // This is disabled by default since the callback alternative is // much more lightweight - creating threads isn't cheap.
bb7bd92008-07-15Henrik Grubbström (Grubba)  array threads = ({
e5d92a2006-02-12Peter Bortas  thread_create( lambda() { gotstdout = mystdout->read(); } ), thread_create( lambda() { gotstderr = mystderr->read(); } ) });
bb7bd92008-07-15Henrik Grubbström (Grubba)  if (mystdin) { threads += ({
121cd12010-11-19Henrik Grubbström (Grubba)  thread_create(lambda(Stdio.File f) { f->write(stdin_str); f->close(); }, mystdin )
bb7bd92008-07-15Henrik Grubbström (Grubba)  });
9181bd2008-09-03Marcus Comstedt  mystdin = 0;
bb7bd92008-07-15Henrik Grubbström (Grubba)  }
e5d92a2006-02-12Peter Bortas  exitcode = p->wait();
bb7bd92008-07-15Henrik Grubbström (Grubba)  threads->wait();
12d9d42010-11-05Martin Stjernholm #else
2e328a2010-11-02Martin Stjernholm  Pike.SmallBackend backend = Pike.SmallBackend(); mystdout->set_backend (backend); mystderr->set_backend (backend);
e5d92a2006-02-12Peter Bortas  mystdout->set_read_callback( lambda( mixed i, string data) { gotstdout += data; } ); mystderr->set_read_callback( lambda( mixed i, string data) { gotstderr += data; } );
bd69712010-11-02Marcus Comstedt  mystdout->set_close_callback( lambda () { mystdout->set_read_callback(0);
f3df822011-11-12Henrik Grubbström (Grubba)  catch { mystdout->close(); };
bd69712010-11-02Marcus Comstedt  mystdout = 0; }); mystderr->set_close_callback( lambda () { mystderr->set_read_callback(0);
f3df822011-11-12Henrik Grubbström (Grubba)  catch { mystderr->close(); };
bd69712010-11-02Marcus Comstedt  mystderr = 0; });
e5d92a2006-02-12Peter Bortas 
bb7bd92008-07-15Henrik Grubbström (Grubba)  if (mystdin) {
2e328a2010-11-02Martin Stjernholm  Shuffler.Shuffler sfr = Shuffler.Shuffler(); sfr->set_backend (backend); Shuffler.Shuffle sf = sfr->shuffle( mystdin );
bb7bd92008-07-15Henrik Grubbström (Grubba)  sf->add_source(stdin_str);
bd69712010-11-02Marcus Comstedt  sf->set_done_callback (lambda () {
f3df822011-11-12Henrik Grubbström (Grubba)  catch { mystdin->close(); };
bd69712010-11-02Marcus Comstedt  mystdin = 0; });
bb7bd92008-07-15Henrik Grubbström (Grubba)  sf->start(); }
bd69712010-11-02Marcus Comstedt  while( mystdout || mystderr || mystdin )
2e328a2010-11-02Martin Stjernholm  backend( 1.0 );
e5d92a2006-02-12Peter Bortas  exitcode = p->wait(); #endif return ([ "stdout" : gotstdout, "stderr" : gotstderr, "exitcode": exitcode ]);
8f6e2e2001-01-19Per Hedbor }
c2a4061997-02-06Fredrik Hübinette (Hubbe) 
dd9b5e2003-12-19Marcus Comstedt #if constant(exece)
970edb2002-08-09Martin Nilsson //!
8d49101998-01-21Fredrik Hübinette (Hubbe) int exec(string file,string ... foo)
c2a4061997-02-06Fredrik Hübinette (Hubbe) {
bcfe4f1997-06-09John W. Pierce  if (sizeof(file)) { string path;
c2a4061997-02-06Fredrik Hübinette (Hubbe) 
8a98bb2002-11-15Martin Nilsson  if(has_value(file,"/"))
ecbfe12003-04-27Martin Nilsson  return exece(combine_path(getcwd(),file),foo, [mapping(string:string)]getenv());
c2a4061997-02-06Fredrik Hübinette (Hubbe) 
ecbfe12003-04-27Martin Nilsson  path=[string]getenv("PATH");
c2a4061997-02-06Fredrik Hübinette (Hubbe) 
b6277d1997-06-10Fredrik Hübinette (Hubbe)  foreach(path ? path/":" : ({}) , path)
bcfe4f1997-06-09John W. Pierce  if(file_stat(path=combine_path(path,file)))
ecbfe12003-04-27Martin Nilsson  return exece(path, foo, [mapping(string:string)]getenv());
bcfe4f1997-06-09John W. Pierce  }
c2a4061997-02-06Fredrik Hübinette (Hubbe)  return 69; }
dd9b5e2003-12-19Marcus Comstedt #endif
c2a4061997-02-06Fredrik Hübinette (Hubbe) 
f9935c2013-03-05Henrik Grubbström (Grubba) protected string search_path_raw;
9eaf1d2008-06-28Martin Nilsson protected array(string) search_path_entries=0;
970edb2002-08-09Martin Nilsson 
f9935c2013-03-05Henrik Grubbström (Grubba) //! Search for the path to an executable. //! //! @param command //! Executable to search for. //! //! Searches for @[command] in the directories listed in the //! environment variable @tt{$PATH@}.
970edb2002-08-09Martin Nilsson //!
f9935c2013-03-05Henrik Grubbström (Grubba) //! @returns //! Returns the path to @[command] if found, and //! @expr{0@} (zero) on failure. //! //! @note //! This function is NOT thread safe if the environment //! variable @tt{$PATH@} is being changed concurrently. //! //! @note //! In Pike 7.8.752 and earlier the environment //! variable @tt{$PATH@} was only read once.
476ff22000-12-18Mirar (Pontus Hagland) string search_path(string command) { if (command=="" || command[0]=='/') return command;
f9935c2013-03-05Henrik Grubbström (Grubba)  string path = getenv("PATH")||""; if ((path != search_path_raw) || !search_path_entries)
476ff22000-12-18Mirar (Pontus Hagland)  {
f9935c2013-03-05Henrik Grubbström (Grubba)  string raw_path = path;
9bd2fd2009-06-03郭雪松 (Xuesong Guo) #ifdef __NT__
f9935c2013-03-05Henrik Grubbström (Grubba)  path = replace(path, "\\", "/");
9bd2fd2009-06-03郭雪松 (Xuesong Guo) #endif
f9935c2013-03-05Henrik Grubbström (Grubba)  array(string) e = path/path_separator - ({""});
476ff22000-12-18Mirar (Pontus Hagland)  multiset(string) filter=(<>);
f9935c2013-03-05Henrik Grubbström (Grubba)  array(string) entries=({});
476ff22000-12-18Mirar (Pontus Hagland)  foreach (e,string s) { string t; if (s[0]=='~') // some shells allow ~-expansion in PATH {
3630b82012-07-15Bill Welliver  if (s[0..1]=="~/" && (t=System.get_home()))
476ff22000-12-18Mirar (Pontus Hagland)  s=t+s[1..]; else { // expand user? }
f9935c2013-03-05Henrik Grubbström (Grubba) #ifdef __NT__ } else if (sizeof(s) && (s[0] == '"') && s[-1] == '"') { // Windows doesn't seem to strip quotation marks from PATH // components that contain them so we need to do that here, // otherwise we'll end up with a bogus path to stat on. s = s[1..<1]; #endif /* __NT__ */
476ff22000-12-18Mirar (Pontus Hagland)  }
f9935c2013-03-05Henrik Grubbström (Grubba)  if (!filter[s] /* && directory exist */ )
476ff22000-12-18Mirar (Pontus Hagland)  {
f9935c2013-03-05Henrik Grubbström (Grubba)  entries += ({s});
476ff22000-12-18Mirar (Pontus Hagland)  filter[s]=1; } }
f9935c2013-03-05Henrik Grubbström (Grubba)  // FIXME: This is NOT thread safe! search_path_entries = entries; search_path_raw = raw_path;
476ff22000-12-18Mirar (Pontus Hagland)  }
f9935c2013-03-05Henrik Grubbström (Grubba)  return locate_binary(search_path_entries, command);
476ff22000-12-18Mirar (Pontus Hagland) }
970edb2002-08-09Martin Nilsson //!
98bfd81998-04-16Fredrik Hübinette (Hubbe) string sh_quote(string s) { return replace(s, ({"\\", "'", "\"", " "}), ({"\\\\", "\\'", "\\\"","\\ "})); }
a656b22003-09-24Henrik Grubbström (Grubba) array(string) split_quoted_string(string s, int(0..1)|void nt_mode)
b219522009-07-11Martin Stjernholm //! Splits the given string into a list of arguments, according to
2f3d7f2009-07-11Martin Stjernholm //! common (i.e. @expr{/bin/sh@}-based) command line quoting rules:
b219522009-07-11Martin Stjernholm //! //! @ul //! @item
74de3b2009-07-11Martin Stjernholm //! Sequences of whitespace, i.e. space, tab, @expr{\n@} or //! @expr{\r@}, are treated as argument separators by default.
b219522009-07-11Martin Stjernholm //! //! @item //! Single or double quotes (@expr{'@} or @expr{"@}) can be used //! around an argument to avoid whitespace splitting inside it. If //! such quoted strings are used next to each other then they get //! concatenated to one argument; e.g. @expr{a"b"'c'@} becomes a //! single argument @expr{abc@}. //! //! @item //! Backslash (@expr{\@}) can be used in front of one of the space //! or quote characters mentioned above to treat them literally. //! E.g. @expr{x\ y@} is a single argument with a space in the //! middle, and @expr{x\"y@} is a single argument with a double //! quote in the middle. //! //! @item //! A backslash can also be used to quote itself; i.e. @expr{\\@} //! becomes @expr{\@}. //! //! @item //! Backslashes in front of other characters are removed by default. //! However, if the optional @[nt_mode] flag is set then they are //! retained as-is, to work better with Windows style paths.
2f3d7f2009-07-11Martin Stjernholm //! //! @item //! Backslashes are treated literally inside quoted strings, with //! the exception that @expr{\"@} is treated as a literal @expr{"@} //! inside a @expr{"@}-quoted string. It's therefore possible to //! include a literal @expr{"@} in a @expr{"@}-quoted string, as //! opposed to @expr{'@}-quoted strings which cannot contain a //! @expr{'@}.
b219522009-07-11Martin Stjernholm //! @endul
c2a4061997-02-06Fredrik Hübinette (Hubbe) {
a656b22003-09-24Henrik Grubbström (Grubba)  // Remove initial white-space.
74de3b2009-07-11Martin Stjernholm  sscanf(s,"%*[ \t\n\r]%s",s);
a656b22003-09-24Henrik Grubbström (Grubba) 
b219522009-07-11Martin Stjernholm  // Prefix interesting characters with NUL,
a656b22003-09-24Henrik Grubbström (Grubba)  // and split on NUL.
be5bb72000-07-11Fredrik Hübinette (Hubbe)  s=replace(s,
74de3b2009-07-11Martin Stjernholm  ({"\"", "'", "\\", " ", "\t", "\n", "\r", "\0"}), ({"\0\"","\0'","\0\\","\0 ","\0\t","\0\n", "\0\r", "\0\0"}));
4755b72000-02-18Henrik Grubbström (Grubba)  array(string) x=s/"\0";
08443e2009-07-11Martin Stjernholm  array(string) ret = ({}); string last = x[0]; int always_keep_last;
8392231998-01-08Fredrik Hübinette (Hubbe) 
08443e2009-07-11Martin Stjernholm piece_loop:
8392231998-01-08Fredrik Hübinette (Hubbe)  for(int e=1;e<sizeof(x);e++) {
f51c772009-07-11Martin Stjernholm  string piece = x[e];
d960892009-07-11Martin Stjernholm  if (piece == "") { // Escaped NUL. There should always be another element in x. last += "\0" + x[++e];
a656b22003-09-24Henrik Grubbström (Grubba)  continue; }
f51c772009-07-11Martin Stjernholm  switch(piece[0])
8392231998-01-08Fredrik Hübinette (Hubbe)  { case '"':
d960892009-07-11Martin Stjernholm  always_keep_last = 1;
08443e2009-07-11Martin Stjernholm  last+=piece[1..];
d960892009-07-11Martin Stjernholm  while (1) { if (++e == sizeof (x)) break piece_loop; if (has_prefix (piece = x[e], "\"")) break; if (piece == "") { // Escaped NUL. last += "\0" + x[++e]; } else { if(piece == "\\" && sizeof (x) > e + 1 && has_prefix (x[e+1], "\"")) piece = x[++e]; last+=piece; }
be5bb72000-07-11Fredrik Hübinette (Hubbe)  }
08443e2009-07-11Martin Stjernholm  last+=piece[1..];
8392231998-01-08Fredrik Hübinette (Hubbe)  break; case '\'':
d960892009-07-11Martin Stjernholm  always_keep_last = 1;
08443e2009-07-11Martin Stjernholm  last+=piece[1..];
d960892009-07-11Martin Stjernholm  while (1) { if (++e == sizeof (x)) break piece_loop; if (has_prefix (piece = x[e], "'")) break; if (piece == "") { // Escaped NUL. last += "\0" + x[++e]; } else last+=piece; }
08443e2009-07-11Martin Stjernholm  last+=piece[1..];
8392231998-01-08Fredrik Hübinette (Hubbe)  break; case '\\':
f51c772009-07-11Martin Stjernholm  if(sizeof(piece)>1)
8392231998-01-08Fredrik Hübinette (Hubbe)  {
a656b22003-09-24Henrik Grubbström (Grubba)  if (nt_mode) { // On NT we only escape special characters with \; // other \'s we keep verbatim.
08443e2009-07-11Martin Stjernholm  last+=piece;
a656b22003-09-24Henrik Grubbström (Grubba)  } else {
08443e2009-07-11Martin Stjernholm  last+=piece[1..];
a656b22003-09-24Henrik Grubbström (Grubba)  }
aa1e5f2009-07-11Martin Stjernholm  }else if (sizeof (x) > e + 1) {
a656b22003-09-24Henrik Grubbström (Grubba)  // Escaped special character.
08443e2009-07-11Martin Stjernholm  last+=x[++e];
8392231998-01-08Fredrik Hübinette (Hubbe)  } break; case ' ':
be5bb72000-07-11Fredrik Hübinette (Hubbe)  case '\t': case '\n':
74de3b2009-07-11Martin Stjernholm  case '\r':
f51c772009-07-11Martin Stjernholm  while(sizeof(piece)==1)
f43b942000-07-23Fredrik Hübinette (Hubbe)  { if(e+1 < sizeof(x)) {
74de3b2009-07-11Martin Stjernholm  if((<' ','\t','\n', '\r'>) [x[e+1][0]])
f51c772009-07-11Martin Stjernholm  piece = x[++e];
f43b942000-07-23Fredrik Hübinette (Hubbe)  else break; }else{
08443e2009-07-11Martin Stjernholm  break piece_loop;
f43b942000-07-23Fredrik Hübinette (Hubbe)  } }
08443e2009-07-11Martin Stjernholm  if (sizeof (last) || always_keep_last) ret += ({last}); last = piece[1..];
8392231998-01-08Fredrik Hübinette (Hubbe)  break; } }
08443e2009-07-11Martin Stjernholm  if (sizeof (last) || always_keep_last) ret += ({last});
8392231998-01-08Fredrik Hübinette (Hubbe)  return ret; }
4ec92d2005-02-10Martin Stjernholm Process spawn(string command, void|Stdio.Stream stdin, void|Stdio.Stream stdout, void|Stdio.Stream stderr, // These aren't used. Seems to be part of something unfinished. /mast //function|void cleanup, mixed ... args ) //! Spawns a process that executes @[command] as a command shell //! statement ("@expr{/bin/sh -c @[command]@}" for Unix, "@expr{cmd /c //! @[command]@}" for Windows).
970edb2002-08-09Martin Nilsson //!
4ec92d2005-02-10Martin Stjernholm //! @param stdin //! @param stdout //! @param stderr //! Stream objects to use as standard input, standard output and //! standard error, respectively, for the created process. The //! corresponding streams for this process are used for those that //! are left out. //! //! @returns //! Returns a @[Process.Process] object for the created process. //! //! @seealso //! @[system], @[popen]
8392231998-01-08Fredrik Hübinette (Hubbe) {
ecbfe12003-04-27Martin Nilsson  mapping(string:mixed) data=(["env":getenv()]);
8392231998-01-08Fredrik Hübinette (Hubbe)  if(stdin) data->stdin=stdin; if(stdout) data->stdout=stdout; if(stderr) data->stderr=stderr;
dc10f22001-10-12Tomas Nilsson #if defined(__NT__)
4ec92d2005-02-10Martin Stjernholm  // if the command string command is not quoted, add double quotes to
dc10f22001-10-12Tomas Nilsson  // make sure it is not modified by create_process
4ec92d2005-02-10Martin Stjernholm  if (sizeof(command) <= 1 || command[0] != '\"' || command[sizeof(command)-1] != '\"') command = "\"" + command + "\"";
9baf362011-09-12Henrik Grubbström (Grubba)  return Process(({ "cmd", "/c", command }),data);
dc10f22001-10-12Tomas Nilsson #elif defined(__amigaos__)
9baf362011-09-12Henrik Grubbström (Grubba)  return Process(split_quoted_string(command),data);
bf2b451998-11-23Marcus Comstedt #else /* !__NT__||__amigaos__ */
9baf362011-09-12Henrik Grubbström (Grubba)  return Process(({ "/bin/sh", "-c", command }),data);
bf2b451998-11-23Marcus Comstedt #endif /* __NT__||__amigaos__ */
c2a4061997-02-06Fredrik Hübinette (Hubbe) }
eb77b92004-05-10Martin Nilsson //! @decl string popen(string command)
4ec92d2005-02-10Martin Stjernholm //! Executes @[command] as a shell statement ("@expr{/bin/sh -c //! @[command]@}" for Unix, "@expr{cmd /c @[command]@}" for Windows), //! waits until it has finished and returns the result as a string. //! //! @seealso //! @[system], @[spawn]
eb77b92004-05-10Martin Nilsson  //! @decl Stdio.FILE popen(string command, string mode)
cb960a2004-05-01Dan Nelson //! Open a "process" for reading or writing. The @[command] is executed
4ec92d2005-02-10Martin Stjernholm //! as a shell statement ("@expr{/bin/sh -c @[command]@}" for Unix, //! "@expr{cmd /c @[command]@}" for Windows). The parameter @[mode] //! should be one of the following letters:
cb960a2004-05-01Dan Nelson //! @string
4ec92d2005-02-10Martin Stjernholm //! @value "r"
cb960a2004-05-01Dan Nelson //! Open for reading. Data written by the process to stdout //! is available for read.
4ec92d2005-02-10Martin Stjernholm //! @value "w"
cb960a2004-05-01Dan Nelson //! Open for writing. Data written to the file is available //! to the process on stdin. //! @endstring
4ec92d2005-02-10Martin Stjernholm //! //! @seealso //! @[system], @[spawn]
eb77b92004-05-10Martin Nilsson 
225f062013-11-02Per Hedbor variant string popen(string s) { return fpopen(s)->read(); } variant Stdio.FILE popen(string s, string mode) { return fpopen(s,mode);
eb77b92004-05-10Martin Nilsson }
9eaf1d2008-06-28Martin Nilsson protected Stdio.FILE fpopen(string s, string|void mode)
c2a4061997-02-06Fredrik Hübinette (Hubbe) {
cb960a2004-05-01Dan Nelson  Stdio.FILE f = Stdio.FILE(); if (!f) error("Popen failed. (couldn't create file)\n");
c2a4061997-02-06Fredrik Hübinette (Hubbe) 
cb960a2004-05-01Dan Nelson  Stdio.File p = f->pipe();
c2a4061997-02-06Fredrik Hübinette (Hubbe)  if(!p) error("Popen failed. (couldn't create pipe)\n");
cb960a2004-05-01Dan Nelson  if (mode == "w")
4ec92d2005-02-10Martin Stjernholm  spawn(s, p, 0, 0, /*destruct, f*/);
cb960a2004-05-01Dan Nelson  else
4ec92d2005-02-10Martin Stjernholm  spawn(s, 0, p, 0, /*destruct, f*/);
ef293f1997-05-07Henrik Grubbström (Grubba)  p->close();
c2a4061997-02-06Fredrik Hübinette (Hubbe)  destruct(p);
cb960a2004-05-01Dan Nelson  return f; }
4ec92d2005-02-10Martin Stjernholm int system(string command, void|Stdio.Stream stdin, void|Stdio.Stream stdout, void|Stdio.Stream stderr) //! Executes @[command] as a shell statement ("@expr{/bin/sh -c //! @[command]@}" for Unix, "@expr{cmd /c @[command]@}" for Windows), //! waits until it has finished and returns its return value.
970edb2002-08-09Martin Nilsson //!
4ec92d2005-02-10Martin Stjernholm //! @param stdin //! @param stdout //! @param stderr //! Stream objects to use as standard input, standard output and //! standard error, respectively, for the created process. The //! corresponding streams for this process are used for those that //! are left out. //! //! @seealso //! @[spawn], @[popen]
c2a4061997-02-06Fredrik Hübinette (Hubbe) {
4ec92d2005-02-10Martin Stjernholm  return spawn(command, stdin, stdout, stderr)->wait();
c2a4061997-02-06Fredrik Hübinette (Hubbe) }
cb119a1997-04-07Fredrik Hübinette (Hubbe) 
782bca2000-09-16Per Hedbor #ifndef __NT__
8392231998-01-08Fredrik Hübinette (Hubbe) #if constant(fork)
04c34c1998-01-01Fredrik Hübinette (Hubbe) constant fork = predef::fork; #endif
cb119a1997-04-07Fredrik Hübinette (Hubbe) 
04c34c1998-01-01Fredrik Hübinette (Hubbe) #if constant(exece) constant exece = predef::exece; #endif
df67f71997-12-14Mirar (Pontus Hagland) 
17f8722004-01-05H. William Welliver III #if constant(fork) && constant(exece)
970edb2002-08-09Martin Nilsson  //!
df67f71997-12-14Mirar (Pontus Hagland) class Spawn { object stdin; object stdout; object stderr; array(object) fd;
8392231998-01-08Fredrik Hübinette (Hubbe)  object pid;
df67f71997-12-14Mirar (Pontus Hagland) 
ecbfe12003-04-27Martin Nilsson  private object low_spawn(array(Stdio.File) fdp, array(Stdio.File) fd_to_close,
4755b72000-02-18Henrik Grubbström (Grubba)  string cmd, void|array(string) args,
8392231998-01-08Fredrik Hübinette (Hubbe)  void|mapping(string:string) env, string|void cwd)
df67f71997-12-14Mirar (Pontus Hagland)  {
ecbfe12003-04-27Martin Nilsson  Stdio.File pie, pied; // interprocess communication
8392231998-01-08Fredrik Hübinette (Hubbe)  object pid;
df67f71997-12-14Mirar (Pontus Hagland)  pie=Stdio.File(); pied=pie->pipe(); if(!(pid=fork())) { mixed err=catch { if(cwd && !cd(cwd)) {
a1c95e2001-12-20Martin Nilsson  error( "pike: cannot change cwd to "+cwd+ ": "+strerror(errno())+"\n" );
df67f71997-12-14Mirar (Pontus Hagland)  } if (sizeof(fdp)>0 && fdp[0]) fdp[0]->dup2(Stdio.File("stdin")); if (sizeof(fdp)>1 && fdp[1]) fdp[1]->dup2(Stdio.File("stdout")); if (sizeof(fdp)>2 && fdp[2]) fdp[2]->dup2(Stdio.File("stderr")); /* dup2 fdd[3..] here FIXME FIXME */
ecbfe12003-04-27Martin Nilsson  foreach (fd_to_close, Stdio.File f)
df67f71997-12-14Mirar (Pontus Hagland)  if (objectp(f)) { f->close(); destruct(f); } pie->close(); destruct(pie); pied->set_close_on_exec(1); if (env) exece(cmd,args||({}),env); else exece(cmd,args||({}));
a1c95e2001-12-20Martin Nilsson  error( "pike: failed to exece "+cmd+ ": "+strerror(errno())+"\n" );
df67f71997-12-14Mirar (Pontus Hagland)  }; pied->write(encode_value(err)); exit(1); } foreach (fdp,object f) if (objectp(f)) { f->close(); destruct(f); } pied->close(); destruct(pied); mixed err=pie->read(); if (err && err!="") throw(decode_value(err)); pie->close(); destruct(pie); return pid; }
8a98bb2002-11-15Martin Nilsson  //!
df67f71997-12-14Mirar (Pontus Hagland)  void create(string cmd, void|array(string) args, void|mapping(string:string) env, string|void cwd, void|array(object(Stdio.File)|void) ownpipes, void|array(object(Stdio.File)|void) fds_to_close) { if (!ownpipes) { stdin=Stdio.File(); stdout=Stdio.File(); stderr=Stdio.File(); fd=({stdin->pipe(),stdout->pipe(),stderr->pipe()}); fds_to_close=({stdin,stdout,stderr}); } else { fd=ownpipes; if (sizeof(fd)>0) stdin=fd[0]; else stdin=0; if (sizeof(fd)>1) stdout=fd[1]; else stdout=0; if (sizeof(fd)>2) stderr=fd[2]; else stderr=0; } pid=low_spawn(fd,fds_to_close||({}),cmd,args,env,cwd); }
8392231998-01-08Fredrik Hübinette (Hubbe) #if constant(kill)
8a98bb2002-11-15Martin Nilsson  //! int kill(int signal)
df67f71997-12-14Mirar (Pontus Hagland)  { return predef::kill(pid,signal); }
8392231998-01-08Fredrik Hübinette (Hubbe) #endif
df67f71997-12-14Mirar (Pontus Hagland) 
8a98bb2002-11-15Martin Nilsson  //!
8392231998-01-08Fredrik Hübinette (Hubbe)  int wait()
df67f71997-12-14Mirar (Pontus Hagland)  {
8392231998-01-08Fredrik Hübinette (Hubbe)  return pid->wait();
df67f71997-12-14Mirar (Pontus Hagland)  } // void set_done_callback(function foo,mixed ... args); // int result(); // array rusage(); }
04c34c1998-01-01Fredrik Hübinette (Hubbe) #endif
782bca2000-09-16Per Hedbor #endif
76c3bc2013-05-13Andreas Petersson 
ab45292013-11-03Tobias S. Josefowitz #if constant(fork) || constant(System.daemon)
bd3aad2013-05-31Andreas Petersson private int low_daemon(int nochdir, int noclose)
76c3bc2013-05-13Andreas Petersson {
ab45292013-11-03Tobias S. Josefowitz #if constant(System.daemon)
76c3bc2013-05-13Andreas Petersson  return System.daemon(nochdir, noclose); #else if (fork()) exit(0);
ab45292013-11-03Tobias S. Josefowitz #if constant(System.setsid)
76c3bc2013-05-13Andreas Petersson  System.setsid(); #endif /* System.setsid */ if (!nochdir) cd("/"); Stdio.File fd; if (!noclose && (fd = Stdio.File("/dev/null", "rw"))) { fd->dup2(Stdio.stdin); fd->dup2(Stdio.stdout); fd->dup2(Stdio.stderr); if (fd->query_fd() > 2) fd->close(); }
bd3aad2013-05-31Andreas Petersson  return 0;
76c3bc2013-05-13Andreas Petersson #endif /* !System.daemon */ } void daemon(int nochdir, int noclose, void|mapping(string:string|Stdio.File) modifiers) //! A function to run current program in the background. //! //! @param nochdir //! If 0 the process will continue to run in / or the directory //! dictadet by modifiers. //! @param noclose //! If this is not 0 the process will keep current file descriptors //! open. //! @param modifiers //! Optional extra arguments. The parameters passed in this mapping //! will override the arguments nochdir and noclose. //! @mapping //! @member string "cwd" //! Change current working directory to this directory. //! @member string|Stdio.File "stdin" //! If this is a string this will be interpreted as a filename //! pointing out a file to be used as stdandard input to the process. //! If this is a Stdio.File object the process will use this as //! standard input. //! @member string|Stdio.File "stdout" //! If this is a string this will be interpreted as a filename //! pointing out a file to be used as stdandard output to the process. //! If this is a Stdio.File object the process will use this as //! standard output. //! @member string|Stdio.File "stderr" //! If this is a string this will be interpreted as a filename //! pointing out a file to be used as stdandard error to the process. //! If this is a Stdio.File object the process will use this as //! standard error. //! @endmapping //! //! @seealso //! @[System.daemon] //! //! @note //! This function only works on UNIX-like operating systems. //! //! @example //! /* close all fd:s and cd to '/' */ //! Process.daemon(0, 0); //! //! /* Do not change working directory. Write stdout to a file called //! access.log and stderr to error.log. */ //! Process.daemon(1, 0, ([ "stdout": "access.log", "stderr": "error.log" ]) ); { array(Stdio.File) opened = ({}); Stdio.File getfd(string|object f) { if (stringp(f)) { Stdio.File ret = Stdio.File(f, "crw"); opened += ({ ret }); return ret; }
bd3aad2013-05-31Andreas Petersson  else if (objectp(f))
76c3bc2013-05-13Andreas Petersson  return f;
bd3aad2013-05-31Andreas Petersson  else return 0;
76c3bc2013-05-13Andreas Petersson  };
bd3aad2013-05-31Andreas Petersson  if (low_daemon(nochdir, noclose) == -1) error("Failed to daemonize: " + strerror(errno())+"\n"); if (!modifiers)
76c3bc2013-05-13Andreas Petersson  return; if (modifiers["cwd"]) cd(modifiers["cwd"]); if (modifiers["stdin"]) getfd(modifiers["stdin"])->dup2(Stdio.stdin); if (modifiers["stdout"]) getfd(modifiers["stdout"])->dup2(Stdio.stdout); if (modifiers["stderr"]) getfd(modifiers["stderr"])->dup2(Stdio.stderr); opened->close(); }
ab45292013-11-03Tobias S. Josefowitz #endif