aboutsummaryrefslogblamecommitdiffstats
path: root/util/stream/pipe.cpp
blob: 51be1934a7588d814bbcfe28007fd376c72dea03 (plain) (tree)
1
2
3
4
5
6
7
8
9







                                    
                                                          
                        
     
                 

                                                                                             
      
                                              
                               
                                                                                

         
                     
                               






                            
                                                              
                                     

 
                                  
 
                                              
                             

 
                                                  
                                  




                                                          
                               





                                                                                                  
 
                                                
                             


                                                        
                                                                                
                                                 
 
                                            
                           










                                                                                              
                           
                           
                        
     





                                       
                                      
                                                   
                            
                 
     






                                         
                                        




                                                                                    
#include "pipe.h"

#include <util/generic/yexception.h>

#include <cstdio>
#include <cerrno>

class TPipeBase::TImpl {
public:
    inline TImpl(const TString& command, const char* mode)
        : Pipe_(nullptr)
    {
#ifndef _freebsd_
        if (strcmp(mode, "r+") == 0) {
            ythrow TSystemError(EINVAL) << "pipe \"r+\" mode is implemented only on FreeBSD";
        }
#endif
        Pipe_ = ::popen(command.data(), mode);
        if (Pipe_ == nullptr) {
            ythrow TSystemError() << "failed to open pipe: " << command.Quote();
        }
    }

    inline ~TImpl() {
        if (Pipe_ != nullptr) {
            ::pclose(Pipe_);
        }
    }

public:
    FILE* Pipe_;
};

TPipeBase::TPipeBase(const TString& command, const char* mode)
    : Impl_(new TImpl(command, mode))
{
}

TPipeBase::~TPipeBase() = default;

TPipeInput::TPipeInput(const TString& command)
    : TPipeBase(command, "r")
{
}

size_t TPipeInput::DoRead(void* buf, size_t len) {
    if (Impl_->Pipe_ == nullptr) {
        return 0;
    }

    size_t bytesRead = ::fread(buf, 1, len, Impl_->Pipe_);
    if (bytesRead == 0) {
        int exitStatus = ::pclose(Impl_->Pipe_);
        Impl_->Pipe_ = nullptr;
        if (exitStatus == -1) {
            ythrow TSystemError() << "pclose() failed";
        } else if (exitStatus != 0) {
            ythrow yexception() << "subprocess exited with non-zero status(" << exitStatus << ")";
        }
    }
    return bytesRead;
}

TPipeOutput::TPipeOutput(const TString& command)
    : TPipeBase(command, "w")
{
}

void TPipeOutput::DoWrite(const void* buf, size_t len) {
    if (Impl_->Pipe_ == nullptr || len != ::fwrite(buf, 1, len, Impl_->Pipe_)) {
        ythrow TSystemError() << "fwrite failed";
    }
}

void TPipeOutput::Close() {
    int exitStatus = ::pclose(Impl_->Pipe_);
    Impl_->Pipe_ = nullptr;
    if (exitStatus == -1) {
        ythrow TSystemError() << "pclose() failed";
    } else if (exitStatus != 0) {
        ythrow yexception() << "subprocess exited with non-zero status(" << exitStatus << ")";
    }
}

TPipedBase::TPipedBase(PIPEHANDLE fd)
    : Handle_(fd)
{
}

TPipedBase::~TPipedBase() {
    if (Handle_.IsOpen()) {
        Handle_.Close();
    }
}

TPipedInput::TPipedInput(PIPEHANDLE fd)
    : TPipedBase(fd)
{
}

TPipedInput::~TPipedInput() = default;

size_t TPipedInput::DoRead(void* buf, size_t len) {
    if (!Handle_.IsOpen()) {
        return 0;
    }
    return Handle_.Read(buf, len);
}

TPipedOutput::TPipedOutput(PIPEHANDLE fd)
    : TPipedBase(fd)
{
}

TPipedOutput::~TPipedOutput() = default;

void TPipedOutput::DoWrite(const void* buf, size_t len) {
    if (!Handle_.IsOpen() || static_cast<ssize_t>(len) != Handle_.Write(buf, len)) {
        ythrow TSystemError() << "pipe writing failed";
    }
}