tools: add playbook driver switch
[barry/progweb.git] / src / fifoargs.cc
blob5b0775b4338af7bc8c6ac417858a121a03f988fd
1 ///
2 /// \file fifoargs.cc
3 /// Class for passing command line arguments via fifo instead
4 /// of command line.
5 ///
7 /*
8 Copyright (C) 2012, Net Direct Inc. (http://www.netdirect.ca/)
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 See the GNU General Public License in the COPYING file at the
20 root directory of this project for more details.
23 #include "fifoargs.h"
24 #include "error.h"
25 #include "common.h"
26 #include <iostream>
27 #include <sstream>
28 #include <iomanip>
29 #include <string>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <errno.h>
36 using namespace std;
38 namespace Barry {
40 //////////////////////////////////////////////////////////////////////////////
41 // FifoArgs class
43 std::ostream& FifoArgs::Write(std::ostream &os) const
45 if( m_pin.Valid() )
46 os << "Pin " << m_pin.Str() << endl;
47 if( m_password.size() )
48 os << "Password " << m_password << endl;
49 if( m_log_filename.size() )
50 os << "LogFilename " << m_log_filename << endl;
51 if( m_use_serial_mode )
52 os << "UseSerialMode" << endl;
53 if( m_verbose )
54 os << "Verbose" << endl;
56 return os;
59 std::istream& FifoArgs::Read(std::istream &is)
61 string line, token, arg;
63 // start fresh
64 Clear();
66 while( getline(is, line) ) {
67 istringstream iss(line);
68 iss >> token >> ws;
70 if( token == "Pin" )
71 iss >> m_pin;
72 else if( token == "Password" )
73 getline(iss, m_password);
74 else if( token == "LogFilename" )
75 getline(iss, m_log_filename);
76 else if( token == "UseSerialMode" )
77 m_use_serial_mode = true;
78 else if( token == "Verbose" )
79 m_verbose = true;
82 return is;
85 void FifoArgs::Clear()
87 m_pin.Clear();
88 m_password.clear();
89 m_log_filename.clear();
90 m_use_serial_mode = false;
91 m_verbose = false;
95 //////////////////////////////////////////////////////////////////////////////
96 // FifoServer class
98 FifoServer::FifoServer(const FifoArgs &args)
99 : m_args(args)
100 , m_created(false)
102 int m_fifo = mkfifo(BARRY_FIFO_NAME, 0660);
103 if( m_fifo != 0 )
104 throw ErrnoError("Cannot open Barry argument fifo", errno);
105 m_created = true;
108 FifoServer::~FifoServer()
110 Cleanup();
113 bool FifoServer::Serve(int timeout_sec)
115 if( !m_created )
116 return false;
118 // man fifo(7) says that opening write-only in non-blocking mode
119 // will fail until the other side opens for read. So continue
120 // to attempt opens until out of time.
121 timeout_sec *= 4;
122 while( timeout_sec-- ) {
123 // attempt to open in non-blocking mode
125 // Security Note:
126 // --------------
127 // This should be safe from symlink attacks, since
128 // mkfifo(), in the constructor, will fail if any other
129 // file or symlink already exists, and therefore we will
130 // never get to this open() call if that fails. And if
131 // mkfifo() succeeds, then we are guaranteed (assuming /tmp
132 // permissions are correct) that only root or our own
133 // user can replace the fifo with something else, such
134 // as a symlink.
136 // The server side is not intended to run as root, yet
137 // if it is, then we are still safe, due to the above logic.
138 // The client side can run as root (depending on what pppd
139 // does with pppob), and has no control over creation of
140 // the fifo, but only opens it for reading, never for
141 // creation or writing. (See FifoClient() below.)
143 // Therefore, we can only be attacked, via symlink,
144 // by root or ourselves.
146 int fd = open(BARRY_FIFO_NAME, O_WRONLY | O_NONBLOCK);
147 if( fd == -1 ) {
148 usleep(250000);
149 continue;
152 ostringstream oss;
153 m_args.Write(oss);
154 int written = write(fd, oss.str().data(), oss.str().size());
155 close(fd);
157 // only success if we wrote all the data
158 return written == (int)oss.str().size();
161 // timeout
162 return false;
165 void FifoServer::Cleanup()
167 if( m_created ) {
168 unlink(BARRY_FIFO_NAME);
169 m_created = false;
174 //////////////////////////////////////////////////////////////////////////////
175 // FifoClient class
177 FifoClient::FifoClient()
181 /// Tries to open the fifo and read the arguments from it.
182 /// If it fails in any way, or timeout, returns false.
183 bool FifoClient::Fetch(int timeout_sec)
185 // See man fifo(7). Should always succeed, as long as
186 // the file exists and permissions allow.
187 int fd = open(BARRY_FIFO_NAME, O_RDONLY | O_NONBLOCK);
188 if( fd == -1 )
189 return false;
191 string sbuf;
192 timeout_sec *= 4;
193 while( timeout_sec-- ) {
194 char buf[4096];
195 int r = read(fd, buf, sizeof(buf));
197 if( r == 0 ) {
198 // only consider this the end of file if
199 // we've already read something, otherwise we close
200 // before the server has a chance to speak up
201 if( sbuf.size() )
202 break;
203 else
204 usleep(250000);
206 else if( r < 0 ) {
207 usleep(250000);
208 continue;
210 else {
211 timeout_sec++;
212 sbuf.append(buf, r);
215 close(fd);
217 // parse
218 istringstream iss(sbuf);
219 m_args.Read(iss);
220 return true;
223 } // Barry namespace