Skip to content
Snippets Groups Projects
Select Git revision
  • cf7b560216f6dab91cd937db8da8d4ee721e6a1d
  • master default
  • wip-slh-dsa-sha2-128s
  • master-updates
  • release-3.10-fixes
  • getopt-prototype
  • fix-bcrypt-warning
  • refactor-hmac
  • wip-use-alignas
  • trim-sha3-context
  • fix-gitlab-ci
  • check-fat-emulate
  • delete-digest_func-size
  • slh-dsa-shake-128f-nettle
  • slh-dsa-shake-128s-nettle
  • slh-dsa-shake-128s
  • delete-openpgp
  • ppc64-sha512
  • delete-md5-compat
  • cleanup-hmac-tests
  • ppc64-sha256
  • nettle_3.10.2_release_20250626
  • nettle_3.10.1_release_20241230
  • nettle_3.10_release_20240616
  • nettle_3.10rc2
  • nettle_3.10rc1
  • nettle_3.9.1_release_20230601
  • nettle_3.9_release_20230514
  • nettle_3.8.1_release_20220727
  • nettle_3.8_release_20220602
  • nettle_3.7.3_release_20210606
  • nettle_3.7.2_release_20210321
  • nettle_3.7.1_release_20210217
  • nettle_3.7_release_20210104
  • nettle_3.7rc1
  • nettle_3.6_release_20200429
  • nettle_3.6rc3
  • nettle_3.6rc2
  • nettle_3.6rc1
  • nettle_3.5.1_release_20190627
  • nettle_3.5_release_20190626
41 results

testutils.c

Blame
  • Process.pmod 15.35 KiB
    #pike __REAL_VERSION__
    
    constant create_process = __builtin.create_process;
    
    #if constant(__builtin.TraceProcess)
    constant TraceProcess = __builtin.TraceProcess;
    #endif
    
    //! Slightly polished version of @[create_process].
    //! @seealso
    //!   @[create_process]
    class Process
    {
      inherit create_process;
      static function(Process:void) read_cb;
      static function(Process:void) timeout_cb;
    
      //! @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 m
      //!   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.
      //!   @endmapping
      //! @seealso
      //!   @[create_process], @[split_quoted_string]
      static void create( string|array(string) args, void|mapping(string:mixed) m )
      {
        if( stringp( args ) ) {
          args = split_quoted_string( [string]args
    #ifdef __NT__
    				  ,1
    #endif /* __NT__ */
    				  );
        }
        if( !m )
          ::create( [array(string)]args );
        else {
          ::create( [array(string)]args, [mapping(string:mixed)]m );
    
          if(read_cb=m->read_callback)
    	call_out(watcher, 0.1);
    
          if( (timeout_cb=m->timeout_callback) || m->timeout )
    	call_out(killer, m->timeout||15);
        }
      }
    
      static void destroy() {
        remove_call_out(watcher);
        remove_call_out(killer);
      }
    
      static void watcher() {
        // It was another sigchld, but not one from our process.
        if(::status()==0)
          call_out(watcher, 0.1);
        else {
          remove_call_out(killer);
          if(read_cb) read_cb(this);
        }
      }
    
      static void killer() {
        remove_call_out(watcher);
    #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)
      {
        string fname = dir + "/" + name;
        if ((info = file_stat(fname))
    	&& (info[0] & 0111))
          return fname;
      }
      return 0;
    }
    
    static 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.
    //!
    //! @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)
          res+=({"-M"+path});
    
        // 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]);
        runpike = res;
      }
      return Process(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.
    //!
    //! @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
    //!   potientially be very large.
    //!
    //! @example
    //!   Process.run( ({ "ls", "-l" }) );
    //!   Process.run( ({ "ls -l" }) );
    //!   Process.run( ({ "ls", "-l" }), ([ "cwd":"/etc" ]) );
    mapping run(string|array(string) cmd, void|mapping modifiers)
    {
      string gotstdout="", gotstderr="";
      int exitcode;
    
      if(!modifiers)
        modifiers = ([]);
    
      if(modifiers->stdout || modifiers->stderr)
        throw( ({ "Can not redirect stdout or stderr in run_process, "
                  "please use Process.Process instead.", backtrace() }) );
    
      Stdio.File mystdout = Stdio.File(); 
      Stdio.File mystderr = Stdio.File();
    
      object p = Process(cmd, modifiers + ([ 
                           "stdout":mystdout->pipe(),
                           "stderr":mystderr->pipe(),
                         ]));
    
    #if constant(Thread.Thread)
      array readthreads = ({
          thread_create( lambda() { gotstdout = mystdout->read(); } ),
          thread_create( lambda() { gotstderr = mystderr->read(); } )
        });
      
      exitcode = p->wait();
      readthreads->wait();
    #else //No threads, use callbacks
      mystdout->set_read_callback( lambda( mixed i, string data) { 
                                     gotstdout += data; 
                                   } );
      mystderr->set_read_callback( lambda( mixed i, string data) {
                                     gotstderr += data;
                                   } );
    
      while( !p->status() ) // FIXME: What about "1" as in stopped?
        Pike.DefaultBackend( 1.0 );
    
      mystdout->set_read_callback(0);
      mystderr->set_read_callback(0);
      
      gotstdout += mystdout->read();
      gotstderr += mystderr->read();
    
      exitcode = p->wait();
    #endif
    
      return ([ "stdout"  : gotstdout,
                "stderr"  : gotstderr,
                "exitcode": exitcode   ]);
    }
    
    #if constant(exece)
    //!
    int exec(string file,string ... foo)
    {
      if (sizeof(file)) {
        string path;
    
        if(has_value(file,"/"))
          return exece(combine_path(getcwd(),file),foo,
    		   [mapping(string:string)]getenv());
    
        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
    
    static array(string) search_path_entries=0;
    
    //!
    string search_path(string command)
    {
       if (command=="" || command[0]=='/') return command;
    
       if (!search_path_entries) 
       {
          array(string) e=(getenv("PATH")||"")/":"-({""});
    
          multiset(string) filter=(<>);
          search_path_entries=({});
          foreach (e,string s)
          {
    	 string t;
    	 if (s[0]=='~')  // some shells allow ~-expansion in PATH
    	 {
    	    if (s[0..1]=="~/" && (t=[string]getenv("HOME")))
    	       s=t+s[1..];
    	    else
    	    {
    	       // expand user?
    	    }
    	 }
    	 if (!filter[s] /* && directory exist */ ) 
    	 {
    	    search_path_entries+=({s}); 
    	    filter[s]=1;
    	 }
          }
       }
       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 0;
    }
    
    //!
    string sh_quote(string s)
    {
      return replace(s,
    	({"\\", "'", "\"", " "}),
    	({"\\\\", "\\'", "\\\"","\\ "}));
    }
    
    //!
    array(string) split_quoted_string(string s, int(0..1)|void nt_mode)
    {
      // Remove initial white-space.
      sscanf(s,"%*[ \n\t]%s",s);
    
      // Prefix interresting characters with NUL,
      // and split on NUL.
      s=replace(s,
    	    ({"\"",  "'",  "\\",  " ",  "\t",  "\n", "\0"}),
    	    ({"\0\"","\0'","\0\\","\0 ","\0\t","\0\n", "\0\0"}));
      array(string) x=s/"\0";
      array(string) ret=({x[0]});
    
      for(int e=1;e<sizeof(x);e++)
      {
        if (!sizeof(x[e])) {
          // Escaped NUL.
          ret[-1] += "\0";
          e++;
          continue;
        }
        switch(x[e][0])
        {
          case '"':
          ret[-1]+=x[e][1..];
          while(x[++e][0]!='"')
          {
    	if(sizeof(x[e])==1 && x[e][0]=='\\' && x[e+1][0]=='"') e++;
    	ret[-1]+=x[e];
          }
          ret[-1]+=x[e][1..];
          break;
    
          case '\'':
          ret[-1]+=x[e][1..];
          while(x[++e][0]!='\'') ret[-1]+=x[e];
          ret[-1]+=x[e][1..];
          break;
          
          case '\\':
          if(sizeof(x[e])>1)
          {
    	if (nt_mode) {
    	  // On NT we only escape special characters with \;
    	  // other \'s we keep verbatim.
    	  ret[-1]+=x[e];
    	} else {
    	  ret[-1]+=x[e][1..];
    	}
          }else{
    	// Escaped special character.
    	ret[-1]+=x[++e];
          }
          break;
          
          case ' ':
          case '\t':
          case '\n':
    	while(sizeof(x[e])==1)
    	{
    	  if(e+1 < sizeof(x))
    	  {
    	    if((<' ','\t','\n'>) [x[e+1][0]])
    	      e++;
    	    else
    	      break;
    	  }else{
    	    return ret;
    	  }
    	}
    	ret+=({x[e][1..]});
          break;
    
          default:
    	ret[-1]+="\0"+x[e];
    	break;
        }
      }
      return ret;
    }
    
    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).
    //!
    //! @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]
    {
      mapping(string:mixed) data=(["env":getenv()]);
      if(stdin) data->stdin=stdin;
      if(stdout) data->stdout=stdout;
      if(stderr) data->stderr=stderr;
    #if defined(__NT__)
      // if the command string command is not quoted, add double quotes to
      // make sure it is not modified by create_process
      if (sizeof(command) <= 1 ||
          command[0] != '\"' || command[sizeof(command)-1] != '\"')
        command = "\"" + command + "\"";
      return create_process(({ "cmd", "/c", command }),data);
    #elif defined(__amigaos__)
      return create_process(split_quoted_string(command),data);
    #else /* !__NT__||__amigaos__ */
      return create_process(({ "/bin/sh", "-c", command }),data);
    #endif /* __NT__||__amigaos__ */
    
    }
    
    //! @decl string popen(string command)
    //! 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]
    
    //! @decl Stdio.FILE popen(string command, string mode)
    //! Open a "process" for reading or writing.  The @[command] is executed
    //! 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:
    //! @string
    //!   @value "r"
    //!   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
        return fpopen(s)->read();
    }
    
    static 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*/);
      else
        spawn(s, 0, p, 0, /*destruct, f*/);
      p->close();
      destruct(p);
    
      return f;
    }
    
    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.
    //!
    //! @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]
    {
      return spawn(command, stdin, stdout, stderr)->wait();
    }
    
    #ifndef __NT__
    #if constant(fork)
    constant fork = predef::fork;
    #endif
    
    #if constant(exece)
    constant exece = predef::exece;
    #endif
    
    #if constant(fork) && constant(exece)
    
    //!
    class Spawn
    {
       object stdin;
       object stdout;
       object stderr;
       array(object) fd;
    
       object pid;
    
       private object low_spawn(array(Stdio.File) fdp,
    			    array(Stdio.File) fd_to_close,
    			    string cmd, void|array(string) args, 
    			    void|mapping(string:string) env, 
    			    string|void cwd)
       {
          Stdio.File pie, pied; // interprocess communication
          object pid;
    
          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" );
    	    }
    
    	    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" );
    	 };
    
    	 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;
       }
    
      //!
       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);
       }
    
    #if constant(kill)
       //!
       int kill(int signal)
       { 
          return predef::kill(pid,signal); 
       }
    #endif
    
       //!
       int wait()
       {
         return pid->wait();
       }
    
       // void set_done_callback(function foo,mixed ... args);
       // int result();
       // array rusage();
    }
    #endif
    #endif