void make_pipe(const wchar_t *test_path)
 {
     wcstring vars_path = test_path ? wcstring(test_path) : default_vars_path();
     vars_path.append(L".notifier");
     const std::string narrow_path = wcs2string(vars_path);
     
     int fd = wopen_cloexec(vars_path, O_RDWR | O_NONBLOCK, 0600);
     if (fd < 0 && errno == ENOENT)
     {
         /* File doesn't exist, try creating it */
         if (mkfifo(narrow_path.c_str(), 0600) >= 0)
         {
             fd = wopen_cloexec(vars_path, O_RDWR | O_NONBLOCK, 0600);
         }
     }
     if (fd < 0)
     {
         // Maybe open failed, maybe mkfifo failed
         int err = errno;
         report_error(err, L"Unable to make or open a FIFO for universal variables with path '%ls'", vars_path.c_str());
     }
     else
     {
         pipe_fd = fd;
     }
 }
Esempio n. 2
0
// Do a private, read-only map of the entirety of a history file with the given name. Returns true if successful. Returns the mapped memory region by reference.
static bool map_file(const wcstring &name, const char **out_map_start, size_t *out_map_len)
{
    bool result = false;
    wcstring filename = history_filename(name, L"");
    if (! filename.empty())
    {
        int fd;
		if((fd = wopen_cloexec(filename, O_RDONLY)) > 0)
		{
			off_t len = lseek( fd, 0, SEEK_END );
			if(len != (off_t)-1)
			{
				size_t mmap_length = (size_t)len;
				if(lseek(fd, 0, SEEK_SET) == 0)
				{
                    char *mmap_start;
					if ((mmap_start = (char *)mmap(0, mmap_length, PROT_READ, MAP_PRIVATE, fd, 0)) != MAP_FAILED)
					{
						result = true;
                        *out_map_start = mmap_start;
                        *out_map_len = mmap_length;
					}
				}
			}
			close( fd );
		}
    }
    return result;
}
Esempio n. 3
0
FILE *wfopen(const wcstring &path, const char *mode) {
    int permissions = 0, options = 0;
    size_t idx = 0;
    switch (mode[idx++]) {
        case 'r': {
            permissions = O_RDONLY;
            break;
        }
        case 'w': {
            permissions = O_WRONLY;
            options = O_CREAT | O_TRUNC;
            break;
        }
        case 'a': {
            permissions = O_WRONLY;
            options = O_CREAT | O_APPEND;
            break;
        }
        default: {
            errno = EINVAL;
            return NULL;
        }
    }
    // Skip binary.
    if (mode[idx] == 'b') idx++;

    // Consider append option.
    if (mode[idx] == '+') permissions = O_RDWR;

    int fd = wopen_cloexec(path, permissions | options, 0666);
    if (fd < 0) return NULL;
    FILE *result = fdopen(fd, mode);
    if (result == NULL) close(fd);
    return result;
}
bool env_universal_t::open_temporary_file(const wcstring &directory, wcstring *out_path, int *out_fd)
{
    /* Create and open a temporary file for writing within the given directory */
    /* Try to create a temporary file, up to 10 times. We don't use mkstemps because we want to open it CLO_EXEC. This should almost always succeed on the first try. */
    assert(! string_suffixes_string(L"/", directory));
    
    bool success = false;
    const wcstring tmp_name_template = directory + L"/fishd.tmp.XXXXXX";
    wcstring tmp_name;
    for (size_t attempt = 0; attempt < 10 && ! success; attempt++)
    {
        int result_fd = -1;
        char *narrow_str = wcs2str(tmp_name_template.c_str());
#if HAVE_MKOSTEMP
        result_fd = mkostemp(narrow_str, O_CLOEXEC);
        if (result_fd >= 0)
        {
            tmp_name = str2wcstring(narrow_str);
        }
#else
        if (mktemp(narrow_str))
        {
            /* It was successfully templated; try opening it atomically */
            tmp_name = str2wcstring(narrow_str);
            result_fd = wopen_cloexec(tmp_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0644);
        }
#endif
        
        if (result_fd >= 0)
        {
            /* Success */
            *out_fd = result_fd;
            *out_path = str2wcstring(narrow_str);
            success = true;
        }
        free(narrow_str);
    }
    if (! success)
    {
        int err = errno;
        report_error(err, L"Unable to open file '%ls'", tmp_name.c_str());
    }
    return success;
}
bool env_universal_t::load_from_path(const wcstring &path, callback_data_list_t *callbacks)
{
    ASSERT_IS_LOCKED(lock);
    
    /* Check to see if the file is unchanged. We do this again in load_from_fd, but this avoids opening the file unnecessarily. */
    if (last_read_file != kInvalidFileID && file_id_for_path(path) == last_read_file)
    {
        UNIVERSAL_LOG("Sync elided based on fast stat()");
        return true;
    }
    
    bool result = false;
    int fd = wopen_cloexec(path, O_RDONLY);
    if (fd >= 0)
    {
        UNIVERSAL_LOG("Reading from file");
        this->load_from_fd(fd, callbacks);
        close(fd);
        result = true;
    }
    return result;
}
Esempio n. 6
0
/// The  source builtin, sometimes called `.`. Evaluates the contents of a file in the current
/// context.
int builtin_source(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
    ASSERT_IS_MAIN_THREAD();
    const wchar_t *cmd = argv[0];
    int argc = builtin_count_args(argv);
    help_only_cmd_opts_t opts;

    int optind;
    int retval = parse_help_only_cmd_opts(opts, &optind, argc, argv, parser, streams);
    if (retval != STATUS_CMD_OK) return retval;

    if (opts.print_help) {
        builtin_print_help(parser, streams, cmd, streams.out);
        return STATUS_CMD_OK;
    }

    int fd;
    struct stat buf;
    const wchar_t *fn, *fn_intern;

    if (argc == optind || wcscmp(argv[optind], L"-") == 0) {
        // Either a bare `source` which means to implicitly read from stdin or an explicit `-`.
        if (argc == optind && !streams.stdin_is_directly_redirected) {
            // Don't implicitly read from the terminal.
            return STATUS_CMD_ERROR;
        }
        fn = L"-";
        fn_intern = fn;
        fd = dup(streams.stdin_fd);
    } else {
        if ((fd = wopen_cloexec(argv[optind], O_RDONLY)) == -1) {
            streams.err.append_format(_(L"%ls: Error encountered while sourcing file '%ls':\n"),
                                      cmd, argv[optind]);
            builtin_wperror(cmd, streams);
            return STATUS_CMD_ERROR;
        }

        if (fstat(fd, &buf) == -1) {
            close(fd);
            streams.err.append_format(_(L"%ls: Error encountered while sourcing file '%ls':\n"),
                                      cmd, argv[optind]);
            builtin_wperror(L"source", streams);
            return STATUS_CMD_ERROR;
        }

        if (!S_ISREG(buf.st_mode)) {
            close(fd);
            streams.err.append_format(_(L"%ls: '%ls' is not a file\n"), cmd, argv[optind]);
            return STATUS_CMD_ERROR;
        }

        fn_intern = intern(argv[optind]);
    }

    const source_block_t *sb = parser.push_block<source_block_t>(fn_intern);
    reader_push_current_filename(fn_intern);

    // This is slightly subtle. If this is a bare `source` with no args then `argv + optind` already
    // points to the end of argv. Otherwise we want to skip the file name to get to the args if any.
    env_set_argv(argv + optind + (argc == optind ? 0 : 1));

    retval = reader_read(fd, streams.io_chain ? *streams.io_chain : io_chain_t());

    parser.pop_block(sb);

    if (retval != STATUS_CMD_OK) {
        streams.err.append_format(_(L"%ls: Error while reading file '%ls'\n"), cmd,
                                  fn_intern == intern_static(L"-") ? L"<stdin>" : fn_intern);
    } else {
        retval = proc_get_last_status();
    }

    // Do not close fd after calling reader_read. reader_read automatically closes it before calling
    // eval.
    reader_pop_current_filename();
    return retval;
}
bool env_universal_t::open_and_acquire_lock(const wcstring &path, int *out_fd)
{
    /* Attempt to open the file for reading at the given path, atomically acquiring a lock. On BSD, we can use O_EXLOCK. On Linux, we open the file, take a lock, and then compare fstat() to stat(); if they match, it means that the file was not replaced before we acquired the lock.
    
    We pass O_RDONLY with O_CREAT; this creates a potentially empty file. We do this so that we have something to lock on.
    */
    int result_fd = -1;
    bool needs_lock = true;
    int flags = O_RDONLY | O_CREAT;
#ifdef O_EXLOCK
    flags |= O_EXLOCK;
    needs_lock = false;
#endif
    for (;;)
    {
        int fd = wopen_cloexec(path, flags, 0644);
        if (fd < 0)
        {
            int err = errno;
            if (err == EINTR)
            {
                /* Signal; try again */
                continue;
            }
#ifdef O_EXLOCK
            else if (err == EOPNOTSUPP)
            {
                /* Filesystem probably does not support locking. Clear the flag and try again. Note that we try taking the lock via flock anyways. */
                flags &= ~O_EXLOCK;
                needs_lock = true;
                continue;
            }
#endif
            else
            {
                report_error(err, L"Unable to open universal variable file '%ls'", path.c_str());
                break;
            }
        }
        
        /* If we get here, we must have a valid fd */
        assert(fd >= 0);
        
        /* Try taking the lock, if necessary. If we failed, we may be on lockless NFS, etc.; in that case we pretend we succeeded. See the comment in save_to_path for the rationale. */
        if (needs_lock)
        {
            while (flock(fd, LOCK_EX) < 0)
            {
                /* error */
                if (errno != EINTR)
                {
                    int err = errno;
                    report_error(err, L"Unable to lock universal variable file '%ls'", path.c_str());
                    break;
                }
            }
        }
        
        /* Hopefully we got the lock. However, it's possible the file changed out from under us while we were waiting for the lock. Make sure that didn't happen. */
        if (file_id_for_fd(fd) != file_id_for_path(path))
        {
            /* Oops, it changed! Try again */
            close(fd);
            continue;
        }

        
        /* Finally, we have an fd that's valid and hopefully locked. We're done */
        assert(fd >= 0);
        result_fd = fd;
        break;
    }
    
    *out_fd = result_fd;
    
    return result_fd >= 0;
}