Exemple #1
0
// Called in a forked child
static void exec_write_and_exit(int fd, const char *buff, size_t count, int status)
{
    if (write_loop(fd, buff, count) == -1)
    {
        debug(0, WRITE_ERROR);
        wperror(L"write");
        exit_without_destructors(status);
    }
    exit_without_destructors(status);
}
Exemple #2
0
/// Returns control of the terminal to the shell, and saves the terminal attribute state to the job,
/// so that we can restore the terminal ownership to the job at a later time.
static bool terminal_return_from_job(job_t *j) {
    errno = 0;
    if (j->pgid == 0) {
        debug(2, "terminal_return_from_job() returning early due to no process group");
        return true;
    }

    signal_block();
    if (tcsetpgrp(STDIN_FILENO, getpgrp()) == -1) {
        if (errno == ENOTTY) redirect_tty_output();
        debug(1, _(L"Could not return shell to foreground"));
        wperror(L"tcsetpgrp");
        signal_unblock();
        return false;
    }

    // Save jobs terminal modes.
    if (tcgetattr(STDIN_FILENO, &j->tmodes)) {
        if (errno == EIO) redirect_tty_output();
        debug(1, _(L"Could not return shell to foreground"));
        wperror(L"tcgetattr");
        signal_unblock();
        return false;
    }

// Disabling this per
// https://github.com/adityagodbole/fish-shell/commit/9d229cd18c3e5c25a8bd37e9ddd3b67ddc2d1b72 On
// Linux, 'cd . ; ftp' prevents you from typing into the ftp prompt. See
// https://github.com/fish-shell/fish-shell/issues/121
#if 0
    // Restore the shell's terminal modes.
    if (tcsetattr(STDIN_FILENO, TCSADRAIN, &shell_modes) == -1) {
        if (errno == EIO) redirect_tty_output();
        debug(1, _(L"Could not return shell to foreground"));
        wperror(L"tcsetattr");
        return false;
    }
#endif

    signal_unblock();
    return true;
}
 ~universal_notifier_shmem_poller_t()
 {
     if (region != NULL)
     {
         // Behold: C++ in all its glory!
         void *address = const_cast<void *>(static_cast<volatile void *>(region));
         if (munmap(address, sizeof(universal_notifier_shmem_t)) < 0)
         {
             wperror(L"munmap");
         }
     }
 }
int dump_memory(DWORD PID,PBYTE pbStartingAddress,DWORD iLength,char * szFile)
{
	FILE * fp=fopen(szFile,"wb+");
	void * pbMemory;
	DWORD iRead;
	HANDLE hProcess;
	
	utils_debug("Dumping Process : %08x, from %08x, length %08x, to file %s.\n",PID,pbStartingAddress,iLength,szFile);
	if(!fp){
		utils_error("Open %s(read/write) failed.\n",fp);
		return 0;
	};
	pbMemory=malloc(iLength);
	if(!pbMemory){
		utils_error("Memory allocation failed (%d bytes).\n",iLength);
		fclose(fp);
		return 0;
	};

	if( !(hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,PID)) ){
		wperror("OpenProcess failed:");
		free(pbMemory);
		fclose(fp);
		return 0;
	}
	if( !ReadProcessMemory(hProcess,pbStartingAddress,pbMemory,iLength,&iRead)){
		wperror("ReadProcessMemory failed:");
		free(pbMemory);
		fclose(fp);
		return 0;
	};

	fwrite(pbMemory,1,iLength,fp);

	utils_trace("Ok, %d bytes written.\n",iLength);

	free(pbMemory);
	fclose(fp);
	return 1;
};
Exemple #5
0
/**
   Returns control of the terminal to the shell, and saves the terminal
   attribute state to the job, so that we can restore the terminal
   ownership to the job at a later time .
*/
static int terminal_return_from_job(job_t *j)
{

    if (tcsetpgrp(0, getpgrp()))
    {
        debug(1, _(L"Could not return shell to foreground"));
        wperror(L"tcsetpgrp");
        return 0;
    }

    /*
       Save jobs terminal modes.
    */
    if (tcgetattr(0, &j->tmodes))
    {
        debug(1, _(L"Could not return shell to foreground"));
        wperror(L"tcgetattr");
        return 0;
    }

    /* Disabling this per https://github.com/adityagodbole/fish-shell/commit/9d229cd18c3e5c25a8bd37e9ddd3b67ddc2d1b72
       On Linux, 'cd . ; ftp' prevents you from typing into the ftp prompt
       See https://github.com/fish-shell/fish-shell/issues/121
    */
#if 0
    /*
       Restore the shell's terminal modes.
    */
    if (tcsetattr(0, TCSADRAIN, &shell_modes))
    {
        debug(1, _(L"Could not return shell to foreground"));
        wperror(L"tcsetattr");
        return 0;
    }
#endif

    return 1;
}
Exemple #6
0
io_buffer_t *io_buffer_t::create(bool is_input, int fd)
{
    bool success = true;
    if (fd == -1)
    {
        fd = is_input ? 0 : 1;
    }
    io_buffer_t *buffer_redirect = new io_buffer_t(fd, is_input);

    if (exec_pipe(buffer_redirect->pipe_fd) == -1)
    {
        debug(1, PIPE_ERROR);
        wperror(L"pipe");
        success = false;
    }
    else if (fcntl(buffer_redirect->pipe_fd[0],
                   F_SETFL,
                   O_NONBLOCK))
    {
        debug(1, PIPE_ERROR);
        wperror(L"fcntl");
        success = false;
    }

    if (! success)
    {
        delete buffer_redirect;
        buffer_redirect = NULL;
    }
    else
    {
        //fprintf(stderr, "Created pipes {%d, %d} for %p\n", buffer_redirect->pipe_fd[0], buffer_redirect->pipe_fd[1], buffer_redirect);
    }

    return buffer_redirect;
}
Exemple #7
0
void exec_close(int fd) {
    ASSERT_IS_MAIN_THREAD();

    // This may be called in a child of fork(), so don't allocate memory.
    if (fd < 0) {
        debug(0, L"Called close on invalid file descriptor ");
        return;
    }

    while (close(fd) == -1) {
        debug(1, FD_ERROR, fd);
        wperror(L"close");
        break;
    }
}
Exemple #8
0
// Read the entire contents of a file into the specified string.
static wcstring read_file(FILE *f) {
    wcstring result;
    while (1) {
        wint_t c = fgetwc(f);
        if (c == WEOF) {
            if (ferror(f)) {
                wperror(L"fgetwc");
                exit(1);
            }
            break;
        }
        result.push_back((wchar_t)c);
    }
    return result;
}
Exemple #9
0
shared_ptr<io_buffer_t> io_buffer_t::create(int fd, const io_chain_t &conflicts,
                                            size_t buffer_limit) {
    bool success = true;
    assert(fd >= 0);
    shared_ptr<io_buffer_t> buffer_redirect(new io_buffer_t(fd, buffer_limit));

    if (exec_pipe(buffer_redirect->pipe_fd) == -1) {
        debug(1, PIPE_ERROR);
        wperror(L"pipe");
        success = false;
    } else if (!buffer_redirect->avoid_conflicts_with_io_chain(conflicts)) {
        // The above call closes the fds on error.
        success = false;
    } else if (make_fd_nonblocking(buffer_redirect->pipe_fd[0]) != 0) {
        debug(1, PIPE_ERROR);
        wperror(L"fcntl");
        success = false;
    }

    if (!success) {
        buffer_redirect.reset();
    }
    return buffer_redirect;
}
Exemple #10
0
io_buffer_t *io_buffer_t::create(int fd, const io_chain_t &conflicts) {
    bool success = true;
    assert(fd >= 0);
    io_buffer_t *buffer_redirect = new io_buffer_t(fd);

    if (exec_pipe(buffer_redirect->pipe_fd) == -1) {
        debug(1, PIPE_ERROR);
        wperror(L"pipe");
        success = false;
    } else if (!buffer_redirect->avoid_conflicts_with_io_chain(conflicts)) {
        // The above call closes the fds on error.
        success = false;
    } else if (make_fd_nonblocking(buffer_redirect->pipe_fd[0]) != 0) {
        debug(1, PIPE_ERROR);
        wperror(L"fcntl");
        success = false;
    }

    if (!success) {
        delete buffer_redirect;
        buffer_redirect = NULL;
    }
    return buffer_redirect;
}
Exemple #11
0
/**
   Read from descriptors until they are empty.

   \param j the job to test
*/
static void read_try(job_t *j)
{
    io_buffer_t *buff = NULL;

    /*
      Find the last buffer, which is the one we want to read from
    */
    const io_chain_t chain = j->all_io_redirections();
    for (size_t idx = 0; idx < chain.size(); idx++)
    {
        io_data_t *d = chain.at(idx).get();
        if (d->io_mode == IO_BUFFER)
        {
            buff = static_cast<io_buffer_t *>(d);
        }
    }

    if (buff)
    {
        debug(3, L"proc::read_try('%ls')\n", j->command_wcstr());
        while (1)
        {
            char b[BUFFER_SIZE];
            long l;

            l=read_blocked(buff->pipe_fd[0],
                           b, BUFFER_SIZE);
            if (l==0)
            {
                break;
            }
            else if (l<0)
            {
                if (errno != EAGAIN)
                {
                    debug(1,
                          _(L"An error occured while reading output from code block"));
                    wperror(L"read_try");
                }
                break;
            }
            else
            {
                buff->out_buffer_append(b, l);
            }
        }
    }
}
Exemple #12
0
/**
   Load or save all variables
*/
static void load_or_save( int save)
{
	const wcstring wdir = fishd_get_config();
	char hostname[HOSTNAME_LEN];
	connection_t c;
	int fd;
	
	if (wdir.empty())
		return;
	
	std::string dir = wcs2string( wdir );
	
	gethostname( hostname, HOSTNAME_LEN );
	
    std::string name;
    name.append(dir);
    name.append("/");
    name.append(FILE);
    name.append(hostname);
	
	debug( 4, L"Open file for %s: '%s'", 
		   save?"saving":"loading", 
		   name.c_str() );
	
    /* OK to not use CLO_EXEC here because fishd is single threaded */
	fd = open(name.c_str(), save?(O_CREAT | O_TRUNC | O_WRONLY):O_RDONLY, 0600);
	
	if( fd == -1 )
	{
		debug( 1, L"Could not open load/save file. No previous saves?" );
		wperror( L"open" );
		return;		
	}
	debug( 4, L"File open on fd %d", c.fd );

	connection_init( &c, fd );

	if( save )
	{
		
		write_loop( c.fd, SAVE_MSG, strlen(SAVE_MSG) );
		enqueue_all( &c );
	}
	else
		read_message( &c );

	connection_destroy( &c );	
}
void connection_destroy(connection_t *c)
{
    if (c->unsent) delete c->unsent;

    /*
      A connection need not always be open - we only try to close it
      if it is open.
    */
    if (c->fd >= 0)
    {
        if (close(c->fd))
        {
            wperror(L"close");
        }
    }
}
Exemple #14
0
/**
   Tests if the tokenizer buffer is large enough to hold contents of
   the specified length, and if not, reallocates the tokenizer buffer.

   \return 0 if the system could not provide the memory needed, and 1 otherwise.
*/
static int check_size( tokenizer *tok, size_t len )
{
	if( tok->last_len <= len )
	{
		wchar_t *tmp;
		tok->last_len = len +1;
		tmp = realloc( tok->last, sizeof(wchar_t)*tok->last_len );
		if( tmp == 0 )
		{
			wperror( L"realloc" );
			return 0;
		}
		tok->last = tmp;
	}
	return 1;
}
Exemple #15
0
void io_buffer_t::read()
{
    exec_close(pipe_fd[1]);

    if (io_mode == IO_BUFFER)
    {
        /*    if( fcntl( pipe_fd[0], F_SETFL, 0 ) )
            {
              wperror( L"fcntl" );
              return;
              }  */
        debug(4, L"io_buffer_t::read: blocking read on fd %d", pipe_fd[0]);
        while (1)
        {
            char b[4096];
            long l;
            l=read_blocked(pipe_fd[0], b, 4096);
            if (l==0)
            {
                break;
            }
            else if (l<0)
            {
                /*
                  exec_read_io_buffer is only called on jobs that have
                  exited, and will therefore never block. But a broken
                  pipe seems to cause some flags to reset, causing the
                  EOF flag to not be set. Therefore, EAGAIN is ignored
                  and we exit anyway.
                */
                if (errno != EAGAIN)
                {
                    debug(1,
                          _(L"An error occured while reading output from code block on file descriptor %d"),
                          pipe_fd[0]);
                    wperror(L"io_buffer_t::read");
                }

                break;
            }
            else
            {
                out_buffer_append(b, l);
            }
        }
    }
}
Exemple #16
0
/**
   Read from descriptors until they are empty. 

   \param j the job to test
*/
static void read_try( job_t *j )
{
	io_data_t *d, *buff=0;

	/*
	  Find the last buffer, which is the one we want to read from
	*/
	for( d = j->io; d; d=d->next )
	{
		
		if( d->io_mode == IO_BUFFER )
		{
			buff=d;
		}
	}
	
	if( buff )
	{
		debug( 3, L"proc::read_try('%ls')\n", j->command_wcstr() );
		while(1)
		{
			char b[BUFFER_SIZE];
			int l;
			
			l=read_blocked( buff->param1.pipe_fd[0],
					b, BUFFER_SIZE );
			if( l==0 )
			{
				break;
			}
			else if( l<0 )
			{
				if( errno != EAGAIN )
				{
					debug( 1, 
						   _( L"An error occured while reading output from code block" ) );
					wperror( L"read_try" );			
				}				
				break;
			}
			else
			{
                buff->out_buffer_append(b, l);
			}			
		}
	}
}
Exemple #17
0
/**
   This function is a wrapper around fork. If the fork calls fails
   with EAGAIN, it is retried FORK_LAPS times, with a very slight
   delay between each lap. If fork fails even then, the process will
   exit with an error message.
*/
pid_t execute_fork(bool wait_for_threads_to_die)
{
    ASSERT_IS_MAIN_THREAD();
    
    if (wait_for_threads_to_die) {
        /* Make sure we have no outstanding threads before we fork. This is a pretty sketchy thing to do here, both because exec.cpp shouldn't have to know about iothreads, and because the completion handlers may do unexpected things. */
        iothread_drain_all();
    }
    
	pid_t pid;
	struct timespec pollint;
	int i;
	
    g_fork_count++;
    
	for( i=0; i<FORK_LAPS; i++ )
	{
		pid = fork();
		if( pid >= 0)
		{
			return pid;
		}
		
		if( errno != EAGAIN )
		{
			break;
		}

		pollint.tv_sec = 0;
		pollint.tv_nsec = FORK_SLEEP_TIME;

		/*
		  Don't sleep on the final lap - sleeping might change the
		  value of errno, which will break the error reporting below.
		*/
		if( i != FORK_LAPS-1 )
		{
			nanosleep( &pollint, NULL );
		}
	}
	
	debug_safe( 0, FORK_ERROR );
	wperror (L"fork");
	FATAL_EXIT();
    return 0;
}
void env_universal_destroy()
{
    /*
      Go into blocking mode and send all data before exiting
    */
    if (env_universal_server.fd >= 0)
    {
        if (fcntl(env_universal_server.fd, F_SETFL, 0) != 0)
        {
            wperror(L"fcntl");
        }
        try_send_all(&env_universal_server);
    }

    connection_destroy(&env_universal_server);
    env_universal_server.fd =-1;
    s_env_univeral_inited = false;
}
Exemple #19
0
/**
   Read the entire contents of a file into the specified string
 */
static void read_file(FILE *f, wcstring &b)
{
    while (1)
    {
        errno=0;
        wint_t c = fgetwc(f);
        if (c == WEOF)
        {
            if (errno)
            {
                wperror(L"fgetwc");
                exit(1);
            }

            break;
        }
        b.push_back((wchar_t)c);
    }
}
Exemple #20
0
/**
   Acquire the lock for the socket
   Returns the name of the lock file if successful or 
   NULL if unable to obtain lock.
   The returned string must be free()d after unlink()ing the file to release 
   the lock
*/
static char *acquire_socket_lock( const char *sock_name )
{
	size_t len = strlen( sock_name );
	char *lockfile = (char *)malloc( len + strlen( LOCKPOSTFIX ) + 1 );
	
	if( lockfile == NULL ) 
	{
		wperror( L"acquire_socket_lock" );
		exit( EXIT_FAILURE );
	}
	strcpy( lockfile, sock_name );
	strcpy( lockfile + len, LOCKPOSTFIX );
	if ( !acquire_lock_file( lockfile, LOCKTIMEOUT, 1 ) )
	{
		free( lockfile );
		lockfile = NULL;
	}
	return lockfile;
}
/**
   Attempt to send the specified message to the specified file descriptor

   \return 1 on sucess, 0 if the message could not be sent without blocking and -1 on error
*/
static int try_send(message_t *msg,
                    int fd)
{

    debug(3,
          L"before write of %d chars to fd %d", msg->body.size(), fd);

    ssize_t res = write(fd, msg->body.c_str(), msg->body.size());

    if (res != -1)
    {
        debug(4, L"Wrote message '%s'", msg->body.c_str());
    }
    else
    {
        debug(4, L"Failed to write message '%s'", msg->body.c_str());
    }

    if (res == -1)
    {
        switch (errno)
        {
            case EAGAIN:
                return 0;

            default:
                debug(2,
                      L"Error while sending universal variable message to fd %d. Closing connection",
                      fd);
                if (debug_level > 2)
                    wperror(L"write");

                return -1;
        }
    }
    msg->count--;

    if (!msg->count)
    {
        delete msg;
    }
    return 1;
}
Exemple #22
0
int
lisp_pipe(int fd[2])
{
  HANDLE input, output;
  SECURITY_ATTRIBUTES sa;

  sa.nLength= sizeof(SECURITY_ATTRIBUTES);
  sa.lpSecurityDescriptor = NULL;
  sa.bInheritHandle = TRUE;

  if (!CreatePipe(&input, &output, &sa, 0))
    {
      wperror("CreatePipe");
      return -1;
    }
  fd[0] = (int) ((intptr_t)input);
  fd[1] = (int) ((intptr_t)output);
  return 0;
}
/**
   Make sure the connection is healthy. If not, close it, and try to
   establish a new connection.
*/
static void check_connection()
{
	if( !init )
		return;
	
	if( env_universal_server.killme )
	{
		debug( 3, L"Lost connection to universal variable server." );
		
		if( close( env_universal_server.fd ) )
		{
			wperror( L"close" );
		}
		
		env_universal_server.fd = -1;
		env_universal_server.killme=0;
		env_universal_server.input.clear();	
		env_universal_read_all();
	}	
}
Exemple #24
0
shared_ptr<io_bufferfill_t> io_bufferfill_t::create(const io_chain_t &conflicts,
                                                    size_t buffer_limit) {
    // Construct our pipes.
    auto pipes = make_autoclose_pipes(conflicts);
    if (!pipes) {
        return nullptr;
    }
    // Our buffer will read from the read end of the pipe. This end must be non-blocking. This is
    // because our fillthread needs to poll to decide if it should shut down, and also accept input
    // from direct buffer transfers.
    if (make_fd_nonblocking(pipes->read.fd())) {
        debug(1, PIPE_ERROR);
        wperror(L"fcntl");
        return nullptr;
    }
    // Our fillthread gets the read end of the pipe; out_pipe gets the write end.
    auto buffer = std::make_shared<io_buffer_t>(buffer_limit);
    buffer->begin_background_fillthread(std::move(pipes->read));
    return std::make_shared<io_bufferfill_t>(std::move(pipes->write), buffer);
}
Exemple #25
0
maybe_t<autoclose_pipes_t> make_autoclose_pipes(const io_chain_t &ios) {
    int pipes[2] = {-1, -1};

    if (pipe(pipes) < 0) {
        debug(1, PIPE_ERROR);
        wperror(L"pipe");
        return none();
    }
    set_cloexec(pipes[0]);
    set_cloexec(pipes[1]);

    if (!pipe_avoid_conflicts_with_io_chain(pipes, ios)) {
        // The pipes are closed on failure here.
        return none();
    }
    autoclose_pipes_t result;
    result.read = autoclose_fd_t(pipes[0]);
    result.write = autoclose_fd_t(pipes[1]);
    return {std::move(result)};
}
Exemple #26
0
main(int argc, char *argv[])
{
	int c;

	while ((c = getopt(argc, argv, "F:ntvcdr:i")) != EOF)
		switch (c) {
		case 'F':
			if (!optarg || optarg[1])
				usage(argv[0]);
			field_sep = *optarg;
			break;
		case 'r':
			if (!optarg)
				usage(argv[0]);
			remote = strdup(optarg);
			break;
		case 'n': o_print_name = 0; break;
		case 't': o_print_type = 0; break;
		case 'v': o_print_value = 0; break;
		case 'c': o_verify = 1; break;
		case 'd': o_print_decimal = 1; break;
		case 'i': o_ignore_error = 1; break;
		case '?':
			usage(argv[0]);
		}


	if (argv[optind] != NULL) {
		LONG ret;

		if (argv[optind + 1] != NULL)
			usage(argv[0]);
		/* Dump a key */
		print_registry(split_name(argv[optind], &rootkey, rootname));
		if (remote && (ret = RegCloseKey(rootkey)) != ERROR_SUCCESS)
			wperror("RegCloseKey", ret);
	} else
		/* Read input and add keys */
		input_process();
	return (0);
}
Exemple #27
0
void io_buffer_t::begin_background_fillthread(autoclose_fd_t fd) {
    ASSERT_IS_MAIN_THREAD();
    assert(!fillthread_ && "Already have a fillthread");

    // We want our background thread to own the fd but it's not easy to move into a std::function.
    // Use a shared_ptr.
    auto fdref = move_to_sharedptr(std::move(fd));

    // Our function to read until the receiver is closed.
    // It's OK to capture 'this' by value because 'this' owns the background thread and joins it
    // before dtor.
    std::function<void(void)> func = [this, fdref]() {
        this->run_background_fillthread(std::move(*fdref));
    };

    pthread_t fillthread{};
    if (!make_pthread(&fillthread, std::move(func))) {
        wperror(L"make_pthread");
    }
    fillthread_ = fillthread;
}
int
CommitMemory (LogicalAddress start, natural len) 
{
#if DEBUG_MEMORY
  fprintf(dbgout, "Committing memory at 0x" LISP ", size 0x" LISP "\n", start, len);
#endif
#ifdef WINDOWS
  LogicalAddress rc;

  if ((start < ((LogicalAddress)nil_value)) &&
      (((LogicalAddress)nil_value) < (start+len))) {
    /* nil area is in the executable on Windows; ensure range is
       read-write */
    DWORD as_if_i_care;
    if (!VirtualProtect(start,len,PAGE_EXECUTE_READWRITE,&as_if_i_care)) {
      return false;
    }
    return true;
  }
  rc = VirtualAlloc(start, len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  if (!rc) {
    wperror("CommitMemory VirtualAlloc");
    return false;
  }
  return true;
#else
  int i;
  void *addr;

  for (i = 0; i < 3; i++) {
    addr = mmap(start, len, MEMPROTECT_RWX, MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0);
    if (addr == start) {
      return true;
    } else {
      mmap(addr, len, MEMPROTECT_NONE, MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0);
    }
  }
  return false;
#endif
}
LogicalAddress
MapMemory(LogicalAddress addr, natural nbytes, int protection)
{
  LogicalAddress p;
#if DEBUG_MEMORY
  fprintf(dbgout, "Mapping memory at 0x" LISP ", size 0x" LISP "\n", addr, nbytes);
#endif
#ifdef WINDOWS
  p = VirtualAlloc(addr, nbytes, MEM_RESERVE|MEM_COMMIT, MEMPROTECT_RWX);
  if (p == NULL) {
    wperror("MapMemory");
  }
  return p;
#else
  {
    int flags = MAP_PRIVATE|MAP_ANON;

    if (addr > 0) flags |= MAP_FIXED;
    return mmap(addr, nbytes, protection, flags, -1, 0);
  }
#endif
}
Exemple #30
0
/*
 * Split a registry key name into its root name (setting rkey)
 * returning a pointer to the rest.
 * If connection to a remote computer has been specified rkey is
 * set to that machine's handle.
 * In that case the returned rkey must be passed to a call to RegCloseKey
 */
static char *
split_name(char *name, HKEY *rkey, char *rname)
{
	char *s, *p;
	LONG ret;

	for (s = name, p = rname; *s && *s != '\\'; s++, p++)
		*p = *s;
	*p = '\0';
	if (*s == '\\')
		s++;
	if (nameval(handles, sizeof(handles) / sizeof(struct s_nameval), rname, (DWORD *)rkey)) {
		if (remote) {
			/* Redirect to the remote machine */
			if ((ret = RegConnectRegistry(remote, *rkey, rkey)) != ERROR_SUCCESS)
				wperror(remote, ret);
		}
		return (*s ? s : NULL);
	} else {
		fprintf(stderr, "Unknown root handle name [%s]\n", rname);
		exit(1);
	}
}