Imported Upstream version 17.25.4
[platform/upstream/libzypp.git] / zypp / base / IOTools.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/base/IOTools.cc
10  *
11 */
12
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <iostream>
16 #include <glib.h>
17
18 #include <zypp/AutoDispose.h>
19 #include <zypp/base/IOTools.h>
20 #include <zypp/base/LogTools.h>
21
22 namespace zypp::io {
23
24   BlockingMode setFILEBlocking (FILE * file, bool mode )
25   {
26     if ( !file ) return BlockingMode::FailedToSetMode;
27
28     int fd = ::fileno( file );
29
30     if ( fd == -1 )
31     { ERR << strerror( errno ) << std::endl; return BlockingMode::FailedToSetMode; }
32
33     int flags = ::fcntl( fd, F_GETFL );
34
35     if ( flags == -1 )
36     { ERR << strerror( errno ) << std::endl; return BlockingMode::FailedToSetMode; }
37
38     BlockingMode oldMode = ( flags & O_NONBLOCK ) == O_NONBLOCK ? BlockingMode::WasNonBlocking : BlockingMode::WasBlocking;
39     if ( !mode )
40       flags = flags | O_NONBLOCK;
41     else if ( flags & O_NONBLOCK )
42       flags = flags ^ O_NONBLOCK;
43
44     flags = ::fcntl( fd,F_SETFL,flags );
45
46     if ( flags == -1 )
47     { ERR << strerror(errno) << std::endl; return BlockingMode::FailedToSetMode; }
48
49     return oldMode;
50   }
51
52   std::pair<ReceiveUpToResult, std::string> receiveUpto(FILE *file, char c, timeout_type timeout, bool failOnUnblockError )
53   {
54     FILE * inputfile = file;
55     if ( !file )
56       return std::make_pair( ReceiveUpToResult::Error, std::string() );
57
58     int    inputfileFd = ::fileno( inputfile );
59
60     size_t linebuffer_size = 0;
61     zypp::AutoFREE<char> linebuf;
62
63     const auto prevMode = setFILEBlocking( file, false );
64     if ( prevMode == BlockingMode::FailedToSetMode && failOnUnblockError )
65       return std::make_pair( ReceiveUpToResult::Error, std::string() );
66
67     // reset the blocking mode when we are done
68     zypp::OnScopeExit resetMode([ prevMode, fd = file ]( ){
69       if ( prevMode == BlockingMode::WasBlocking )
70         setFILEBlocking( fd, true );
71     });
72
73     bool haveTimeout = (timeout != no_timeout);
74     int remainingTimeout = static_cast<int>( timeout );
75     zypp::AutoDispose<GTimer *> timer( nullptr );
76     if ( haveTimeout )
77       timer = zypp::AutoDispose<GTimer *>( g_timer_new(), &g_free );
78
79     std::string line;
80     do
81     {
82       /* Watch inputFile to see when it has input. */
83
84       GPollFD fd;
85       fd.fd = inputfileFd;
86       fd.events =  G_IO_IN | G_IO_HUP | G_IO_ERR;
87       fd.revents = 0;
88
89       if ( timer )
90         g_timer_start( timer );
91
92       clearerr( inputfile );
93
94       int retval = g_poll( &fd, 1, timeout );
95       if ( retval == -1 )
96       {
97         if ( errno != EINTR ) {
98           ERR << "select error: " << strerror(errno) << std::endl;
99           return std::make_pair( ReceiveUpToResult::Error, std::string() );
100         }
101       }
102       else if ( retval )
103       {
104         // Data is available now.
105         ssize_t nread = getdelim( &linebuf.value(), &linebuffer_size, c, inputfile );
106         if ( nread == -1 ) {
107           if ( ::feof( inputfile ) )
108             return std::make_pair( ReceiveUpToResult::EndOfFile, line );
109         }
110         else
111         {
112           if ( nread > 0 )
113             line += std::string( linebuf, nread );
114
115           if ( ! ::ferror( inputfile ) || ::feof( inputfile ) ) {
116             return std::make_pair( ReceiveUpToResult::Success, line ); // complete line
117           }
118         }
119       }
120
121       // we timed out, or were interrupted for some reason
122       // check if we can wait more
123       if ( timer ) {
124         remainingTimeout -= g_timer_elapsed( timer, nullptr );
125         if ( remainingTimeout <= 0 )
126           return std::make_pair( ReceiveUpToResult::Timeout, line );
127       }
128     } while ( true );
129   }
130
131   TimeoutException::~TimeoutException() noexcept
132   { }
133 }