pike.git / lib / modules / Process.pmod

version» Context lines:

pike.git/lib/modules/Process.pmod:1:   #pike __REAL_VERSION__      constant create_process = __builtin.create_process;      #if constant(__builtin.TraceProcess)   constant TraceProcess = __builtin.TraceProcess;   #endif    + #if defined(__NT__) || defined(__amigaos__) || defined(__OS2__) + constant path_separator = ";"; + #else + constant path_separator = ":"; + #endif +    #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)   {
pike.git/lib/modules/Process.pmod:166:       mapping(string:mixed) new_modifiers = (modifiers || ([])) + ([]);       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(). -  if (zero_type(new_modifiers->forkd)) new_modifiers->forkd = forkd_default; +  if (undefinedp(new_modifiers->forkd)) +  new_modifiers->forkd = forkd_default;    if (new_modifiers->forkd && assert_forkd()) {    process_fd = Stdio.File();    forkd_pipe->    send_fd(process_fd->pipe(Stdio.PROP_BIDIRECTIONAL|Stdio.PROP_SEND_FD));    while (forkd_pipe->write("\0") < 0)    ;       m_delete(new_modifiers, "forkd");    __callback = m_delete(new_modifiers, "callback");    -  if (zero_type(new_modifiers->uid)) { +  if (undefinedp(new_modifiers->uid)) {    new_modifiers->uid = geteuid(); -  if (zero_type(new_modifiers->gid)) { +  if (undefinedp(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();    }    }
pike.git/lib/modules/Process.pmod:217: Inside #if constant(Stdio.__HAVE_SEND_FD__)
   ForkdEncoder(process_fd));    data = sprintf("%4c%s", sizeof(data), data);    // 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");    }    int bytes = process_fd->write(data);    if (bytes != sizeof(data)) { +  int fd_errno = process_fd->errno();    process_fd->close();    process_fd = UNDEFINED; -  error("Failed to write spawn request (%d != %d).\n", -  bytes, sizeof(data)); +  error("Failed to write spawn request (%d != %d, errno: %d, errmsg: %s).\n", +  bytes, sizeof(data), fd_errno, strerror(fd_errno) || "?");    }    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);    }   
pike.git/lib/modules/Process.pmod:286: Inside #if constant(Stdio.__HAVE_SEND_FD__)
   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()); +  if (__callback) __callback(this);    break;    case "SIGNAL":    __last_signal = packet;    break;    case "START":    __status = 0; -  if (__callback) __callback(this_object()); +  if (__callback) __callback(this);    break;    case "STOP":    __status = 1; -  if (__callback) __callback(this_object()); +  if (__callback) __callback(this);    break;    case "EXIT":    __result = packet;    __status = 2;    do_close(); -  if (__callback) __callback(this_object()); +  if (__callback) __callback(this);    break;    default:    __result = 255;    __status = 2;    do_close();    error("Unsupported packet from forkd: %O\n", tag);    break;    }    }    }
pike.git/lib/modules/Process.pmod:400: Inside #if constant(kill)
  #if constant(kill)    ::kill(signum("SIGKILL"));   #endif    if(timeout_cb) timeout_cb(this);    }   }      // FIXME: Should probably be located elsewhere.   string locate_binary(array(string) path, string name)   { -  string dir; -  Stdio.Stat info; -  foreach(path, dir) + #ifdef __NT__ +  if( !has_suffix(lower_case(name), ".exe") ) +  name += ".exe"; + #endif +  +  foreach(path, string dir)    { -  string fname = dir + "/" + name; -  if ((info = file_stat(fname)) -  && (info[0] & 0111)) +  string fname = combine_path(dir, name); +  Stdio.Stat info = file_stat(fname); +  if (info && (info->mode & 0111))    return fname;    }    return 0;   }      protected array(string) runpike;      //! Spawn a new pike process similar to the current.   //!   //! @param argv   //! Arguments for the new process.   //!   //! @param options - //! Process creation options. See @[Process.Process] for details. + //! 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.)   //! -  + //! @param launcher + //! Optional launcher prefix command used to spawn the pike binary. + //! + //! When used this is typically something like + //! @expr{({ "/usr/bin/valgrind" })@}. + //! + //! Defaults to the empty array. + //!   //! @seealso   //! @[Process.Process] - Process spawn_pike(array(string) argv, void|mapping(string:mixed) options) + Process spawn_pike(array(string) argv, void|mapping(string:mixed) options, +  array(string)|void launcher)   {    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) -  res+=({"-M"+path}); +  res+=({"-M" + path}); +  if(options && options->add_predefines) +  { +  foreach (master()->predefines; string key; string value) +  if( stringp( value ) ) +  res+=({"-D" + key + "=" + value}); +  else if( intp( value ) ) +  res+=({"-D" + key }); +  } +  if(options && options->add_program_path) +  { +  foreach (master()->pike_program_path;; string value) +  res+=({"-P" + value}); +  } +  if(options && options->add_include_path) +  { +  foreach (master()->pike_include_path;; string value) +  res+=({"-I" + value}); +  }    -  // FIXME: Defines? pike_program_path? -  +     if (sizeof(res) && !has_value(res[0],"/")   #ifdef __NT__    && !has_value(res[0], "\\")   #endif /* __NT__ */    ) -  res[0] = locate_binary(getenv("PATH")/ - #if defined(__NT__) || defined(__amigaos__) -  ";" - #else -  ":" - #endif -  ,res[0]); +  res[0] = search_path(res[0]);    runpike = res;    } -  return Process(runpike + argv, options); +  if (!launcher) launcher = ({}); +  return Process(launcher + runpike + argv, options);   }      //! 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. + //! the exception of stdout and stderr. Each must be either absent, or + //! a function accepting a string; if present, the functions will be called + //! whenever output is made on the corresponding stream, otherwise the data + //! will be collected and returned in the result mapping.   //! - //! If @expr{modifiers->stdin@} is set to a string it will automaticly be + //! If @expr{modifiers->stdin@} is set to a string it will automatically be   //! converted to a pipe that is fed to stdin of the started process.   //!   //! @seealso   //! @[Process.Process] @[create_process]   //!   //! @returns   //! @mapping   //! @member string "stdout" - //! Everything the process wrote on stdout. + //! Everything the process wrote on stdout, unless a stdout function was + //! provided.   //! @member string "stderr" - //! Everything the process wrote on stderr. + //! Everything the process wrote on stderr, similarly.   //! @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 - //! potential to be very large. + //! It is therefore advisable to set up rlimits if the output has a + //! potential to be very large, or else provide functions to handle + //! partial data.   //!   //! @example   //! Process.run( ({ "ls", "-l" }) );   //! Process.run( ({ "ls", "-l" }), ([ "cwd":"/etc" ]) );   //! Process.run( "ls -l" );   //! Process.run( "awk -F: '{print $2}'", ([ "stdin":"foo:2\nbar:17\n" ]) );   mapping run(string|array(string) cmd, void|mapping modifiers)   {    string gotstdout="", gotstderr="", stdin_str;    int exitcode;       if(!modifiers)    modifiers = ([]);    -  if(modifiers->stdout || modifiers->stderr) +  if((modifiers->stdout && !callablep(modifiers->stdout)) +  || (modifiers->stderr && !callablep(modifiers->stderr)))    throw( ({ "Can not redirect stdout or stderr in Process.run, "    "please use Process.Process instead.", backtrace() }) );       Stdio.File mystdout = Stdio.File();    Stdio.File mystderr = Stdio.File();    Stdio.File mystdin;       object p;    if(stringp(modifiers->stdin))    {
pike.git/lib/modules/Process.pmod:553: Inside #if 0 //constant(Thread.Thread)
      exitcode = p->wait();    threads->wait();   #else    Pike.SmallBackend backend = Pike.SmallBackend();       mystdout->set_backend (backend);    mystderr->set_backend (backend);       mystdout->set_read_callback( lambda( mixed i, string data) { -  gotstdout += data; +  if (modifiers->stdout) modifiers->stdout(data); +  else gotstdout += data;    } );    mystderr->set_read_callback( lambda( mixed i, string data) { -  gotstderr += data; +  if (modifiers->stderr) modifiers->stderr(data); +  else gotstderr += data;    } );    mystdout->set_close_callback( lambda () {    mystdout->set_read_callback(0); -  +  catch { mystdout->close(); };    mystdout = 0;    });    mystderr->set_close_callback( lambda () {    mystderr->set_read_callback(0); -  +  catch { mystderr->close(); };    mystderr = 0;    });       if (mystdin) {    Shuffler.Shuffler sfr = Shuffler.Shuffler();    sfr->set_backend (backend);    Shuffler.Shuffle sf = sfr->shuffle( mystdin );    sf->add_source(stdin_str);    sf->set_done_callback (lambda () { -  +  catch { mystdin->close(); };    mystdin = 0;    });    sf->start();    }       while( mystdout || mystderr || mystdin )    backend( 1.0 );       exitcode = p->wait();   #endif
pike.git/lib/modules/Process.pmod:610: Inside #if constant(exece)
   path=[string]getenv("PATH");       foreach(path ? path/":" : ({}) , path)    if(file_stat(path=combine_path(path,file)))    return exece(path, foo, [mapping(string:string)]getenv());    }    return 69;   }   #endif    + protected string search_path_raw;   protected array(string) search_path_entries=0;    -  + //! 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@}. + //! + //! @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.   string search_path(string command)   {    if (command=="" || command[0]=='/') return command;    -  if (!search_path_entries) +  string path = getenv("PATH")||""; +  if ((path != search_path_raw) || !search_path_entries)    { -  +  string raw_path = path;   #ifdef __NT__ -  array(string) e=replace(getenv("PATH")||"", "\\", "/")/";"-({""}); - #elif defined(__amigaos__) -  array(string) e=(getenv("PATH")||"")/";"-({""}); - #else -  array(string) e=(getenv("PATH")||"")/":"-({""}); +  path = replace(path, "\\", "/");   #endif -  +  array(string) e = path/path_separator - ({""});       multiset(string) filter=(<>); -  search_path_entries=({}); +  array(string) entries=({});    foreach (e,string s)    {    string t;    if (s[0]=='~') // some shells allow ~-expansion in PATH    { -  if (s[0..1]=="~/" && (t=[string]getenv("HOME"))) +  if (s[0..1]=="~/" && (t=System.get_home()))    s=t+s[1..];    else    {    // expand user?    } -  + #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__ */    }    if (!filter[s] /* && directory exist */ )    { -  search_path_entries+=({s}); +  entries += ({s});    filter[s]=1;    }    } -  +  // FIXME: This is NOT thread safe! +  search_path_entries = entries; +  search_path_raw = raw_path;    } -  foreach (search_path_entries, string path) -  { -  string p=combine_path(path,command); -  Stdio.Stat s=file_stat(p); -  if (s && s->mode&0111) return p; +  return locate_binary(search_path_entries, command);   } -  return 0; - } +       //!   string sh_quote(string s)   {    return replace(s,    ({"\\", "'", "\"", " "}),    ({"\\\\", "\\'", "\\\"","\\ "}));   }      array(string) split_quoted_string(string s, int(0..1)|void nt_mode)
pike.git/lib/modules/Process.pmod:876:   //! Open for reading. Data written by the process to stdout   //! is available for read.   //! @value "w"   //! Open for writing. Data written to the file is available   //! to the process on stdin.   //! @endstring   //!   //! @seealso   //! @[system], @[spawn]    - Stdio.FILE|string popen(string s, string|void mode) { -  if(mode) -  return fpopen(s,mode); -  else + variant string popen(string s) {    return fpopen(s)->read();   }    -  + variant Stdio.FILE popen(string s, string mode) { +  return fpopen(s,mode); + } +    protected Stdio.FILE fpopen(string s, string|void mode)   {    Stdio.FILE f = Stdio.FILE();    if (!f) error("Popen failed. (couldn't create file)\n");       Stdio.File p = f->pipe();    if(!p) error("Popen failed. (couldn't create pipe)\n");       if (mode == "w")    spawn(s, p, 0, 0, /*destruct, f*/);
pike.git/lib/modules/Process.pmod:960: Inside #if undefined(__NT__) and #if constant(fork) && constant(exece)
      pie=Stdio.File();    pied=pie->pipe();       if(!(pid=fork()))    {    mixed err=catch    {    if(cwd && !cd(cwd))    { -  error( "pike: cannot change cwd to "+cwd+ -  ": "+strerror(errno())+"\n" ); +  error( "pike: cannot change cwd to %O: %m.\n", cwd );    }       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 */    foreach (fd_to_close, Stdio.File f)    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||({}));    -  error( "pike: failed to exece "+cmd+ -  ": "+strerror(errno())+"\n" ); +  error( "pike: failed to exece %O: %m.\n", cmd );    };       pied->write(encode_value(err));    exit(1);    }       foreach (fdp,object f) if (objectp(f)) { f->close(); destruct(f); }       pied->close();    destruct(pied);
pike.git/lib/modules/Process.pmod:1048: Inside #if undefined(__NT__) and #if constant(fork) && constant(exece)
   {    return pid->wait();    }       // void set_done_callback(function foo,mixed ... args);    // int result();    // array rusage();   }   #endif   #endif +  + #if constant(fork) || constant(System.daemon) + private int low_daemon(int nochdir, int noclose) + { + #if constant(System.daemon) +  return System.daemon(nochdir, noclose); + #else +  if (fork()) +  exit(0); +  + #if constant(System.setsid) +  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(); +  } +  return 0; + #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; +  } +  else if (objectp(f)) +  return f; +  else +  return 0; +  }; +  +  if (low_daemon(nochdir, noclose) == -1) +  error("Failed to daemonize: %m.\n"); +  if (!modifiers) +  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(); + } + #endif