menu: added new Keywords tag to .desktop files
[barry.git] / src / fifoargs.cc
blob293a32b9d965c26236efb02d130120737f00c72e
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-2013, 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 "i18n.h"
24 #include "fifoargs.h"
25 #include "error.h"
26 #include "common.h"
27 #include <iostream>
28 #include <sstream>
29 #include <iomanip>
30 #include <string>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <errno.h>
37 using namespace std;
39 namespace Barry {
41 //////////////////////////////////////////////////////////////////////////////
42 // FifoArgs class
44 std::ostream& FifoArgs::Write(std::ostream &os) const
46 if( m_pin.Valid() )
47 os << "Pin " << m_pin.Str() << endl;
48 if( m_password.size() )
49 os << "Password " << m_password << endl;
50 if( m_log_filename.size() )
51 os << "LogFilename " << m_log_filename << endl;
52 if( m_use_serial_mode )
53 os << "UseSerialMode" << endl;
54 if( m_verbose )
55 os << "Verbose" << endl;
57 return os;
60 std::istream& FifoArgs::Read(std::istream &is)
62 string line, token, arg;
64 // start fresh
65 Clear();
67 while( getline(is, line) ) {
68 istringstream iss(line);
69 iss >> token >> ws;
71 if( token == "Pin" )
72 iss >> m_pin;
73 else if( token == "Password" )
74 getline(iss, m_password);
75 else if( token == "LogFilename" )
76 getline(iss, m_log_filename);
77 else if( token == "UseSerialMode" )
78 m_use_serial_mode = true;
79 else if( token == "Verbose" )
80 m_verbose = true;
83 return is;
86 void FifoArgs::Clear()
88 m_pin.Clear();
89 m_password.clear();
90 m_log_filename.clear();
91 m_use_serial_mode = false;
92 m_verbose = false;
96 //////////////////////////////////////////////////////////////////////////////
97 // FifoServer class
99 FifoServer::FifoServer(const FifoArgs &args)
100 : m_args(args)
101 , m_created(false)
103 int m_fifo = mkfifo(BARRY_FIFO_NAME, 0660);
104 if( m_fifo != 0 )
105 throw ErrnoError(_("Cannot open Barry argument fifo"), errno);
106 m_created = true;
109 FifoServer::~FifoServer()
111 Cleanup();
114 bool FifoServer::Serve(int timeout_sec)
116 if( !m_created )
117 return false;
119 // man fifo(7) says that opening write-only in non-blocking mode
120 // will fail until the other side opens for read. So continue
121 // to attempt opens until out of time.
122 timeout_sec *= 4;
123 while( timeout_sec-- ) {
124 // attempt to open in non-blocking mode
126 // Security Note:
127 // --------------
128 // This should be safe from symlink attacks, since
129 // mkfifo(), in the constructor, will fail if any other
130 // file or symlink already exists, and therefore we will
131 // never get to this open() call if that fails. And if
132 // mkfifo() succeeds, then we are guaranteed (assuming /tmp
133 // permissions are correct) that only root or our own
134 // user can replace the fifo with something else, such
135 // as a symlink.
137 // The server side is not intended to run as root, yet
138 // if it is, then we are still safe, due to the above logic.
139 // The client side can run as root (depending on what pppd
140 // does with pppob), and has no control over creation of
141 // the fifo, but only opens it for reading, never for
142 // creation or writing. (See FifoClient() below.)
144 // Therefore, we can only be attacked, via symlink,
145 // by root or ourselves.
147 int fd = open(BARRY_FIFO_NAME, O_WRONLY | O_NONBLOCK);
148 if( fd == -1 ) {
149 usleep(250000);
150 continue;
153 ostringstream oss;
154 m_args.Write(oss);
155 int written = write(fd, oss.str().data(), oss.str().size());
156 close(fd);
158 // only success if we wrote all the data
159 return written == (int)oss.str().size();
162 // timeout
163 return false;
166 void FifoServer::Cleanup()
168 if( m_created ) {
169 unlink(BARRY_FIFO_NAME);
170 m_created = false;
175 //////////////////////////////////////////////////////////////////////////////
176 // FifoClient class
178 FifoClient::FifoClient()
182 /// Tries to open the fifo and read the arguments from it.
183 /// If it fails in any way, or timeout, returns false.
184 bool FifoClient::Fetch(int timeout_sec)
186 // See man fifo(7). Should always succeed, as long as
187 // the file exists and permissions allow.
188 int fd = open(BARRY_FIFO_NAME, O_RDONLY | O_NONBLOCK);
189 if( fd == -1 )
190 return false;
192 string sbuf;
193 timeout_sec *= 4;
194 while( timeout_sec-- ) {
195 char buf[4096];
196 int r = read(fd, buf, sizeof(buf));
198 if( r == 0 ) {
199 // only consider this the end of file if
200 // we've already read something, otherwise we close
201 // before the server has a chance to speak up
202 if( sbuf.size() )
203 break;
204 else
205 usleep(250000);
207 else if( r < 0 ) {
208 usleep(250000);
209 continue;
211 else {
212 timeout_sec++;
213 sbuf.append(buf, r);
216 close(fd);
218 // parse
219 istringstream iss(sbuf);
220 m_args.Read(iss);
221 return true;
224 } // Barry namespace