1 /* Pipestream: A simple C++ interface to UNIX pipes
2 Copyright (C) 2005-2014 John C. Bowman,
3 with contributions from Mojca Miklavec
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
18
19 #include <iostream>
20 #include <cstring>
21 #include <cerrno>
22 #include <sstream>
23 #include <signal.h>
24
25 #include "pipestream.h"
26 #include "common.h"
27 #include "errormsg.h"
28 #include "settings.h"
29 #include "util.h"
30 #include "interact.h"
31 #include "lexical.h"
32 #include "camperror.h"
33 #include "pen.h"
34
open(const mem::vector<string> & command,const char * hint,const char * application,int out_fileno)35 void iopipestream::open(const mem::vector<string> &command, const char *hint,
36 const char *application, int out_fileno)
37 {
38 if(pipe(in) == -1) {
39 ostringstream buf;
40 buf << "in pipe failed: ";
41 for(size_t i=0; i < command.size(); ++i) buf << command[i];
42 camp::reportError(buf);
43 }
44
45 if(pipe(out) == -1) {
46 ostringstream buf;
47 buf << "out pipe failed: ";
48 for(size_t i=0; i < command.size(); ++i) buf << command[i];
49 camp::reportError(buf);
50 }
51 cout.flush(); // Flush stdout to avoid duplicate output.
52
53 if((pid=fork()) < 0) {
54 ostringstream buf;
55 buf << "fork failed: ";
56 for(size_t i=0; i < command.size(); ++i) buf << command[i];
57 camp::reportError(buf);
58 }
59
60 if(pid == 0) {
61 if(interact::interactive) signal(SIGINT,SIG_IGN);
62 close(in[1]);
63 close(out[0]);
64 close(STDIN_FILENO);
65 close(out_fileno);
66 dup2(in[0],STDIN_FILENO);
67 dup2(out[1],out_fileno);
68 close(in[0]);
69 close(out[1]);
70 char **argv=args(command);
71 if(argv) execvp(argv[0],argv);
72 execError(argv[0],hint,application);
73 kill(0,SIGTERM);
74 _exit(-1);
75 }
76 close(out[1]);
77 close(in[0]);
78 *buffer=0;
79 pipeopen=true;
80 pipein=true;
81 Running=true;
82 block(false,true);
83 }
84
eof()85 void iopipestream::eof()
86 {
87 if(pipeopen && pipein) {
88 close(in[1]);
89 pipein=false;
90 }
91 }
92
pipeclose()93 void iopipestream::pipeclose()
94 {
95 if(pipeopen) {
96 kill(pid,SIGTERM);
97 eof();
98 close(out[0]);
99 Running=false;
100 pipeopen=false;
101 waitpid(pid,NULL,0); // Avoid zombies.
102 }
103 }
104
block(bool write,bool read)105 void iopipestream::block(bool write, bool read)
106 {
107 if(pipeopen) {
108 int w=fcntl(in[1],F_GETFL);
109 int r=fcntl(out[0],F_GETFL);
110 fcntl(in[1],F_SETFL,write ? w & ~O_NONBLOCK : w | O_NONBLOCK);
111 fcntl(out[0],F_SETFL,read ? r & ~O_NONBLOCK : r | O_NONBLOCK);
112 }
113 }
114
readbuffer()115 ssize_t iopipestream::readbuffer()
116 {
117 ssize_t nc;
118 char *p=buffer;
119 ssize_t size=BUFSIZE-1;
120 errno=0;
121 for(;;) {
122 if((nc=read(out[0],p,size)) < 0) {
123 if(errno == EAGAIN) {p[0]=0; break;}
124 else camp::reportError("read from pipe failed");
125 nc=0;
126 }
127 p[nc]=0;
128 if(nc == 0) {
129 if(waitpid(pid,NULL,WNOHANG) == pid)
130 Running=false;
131 break;
132 }
133 if(nc > 0) {
134 if(settings::verbose > 2) cerr << p;
135 break;
136 }
137 }
138 return nc;
139 }
140
tailequals(const char * buf,size_t len,const char * prompt,size_t plen)141 bool iopipestream::tailequals(const char *buf, size_t len, const char *prompt,
142 size_t plen)
143 {
144 const char *a=buf+len;
145 const char *b=prompt+plen;
146 while(b >= prompt) {
147 if(a < buf) return false;
148 if(*a != *b) return false;
149 // Handle MSDOS incompatibility:
150 if(a > buf && *a == '\n' && *(a-1) == '\r') --a;
151 --a; --b;
152 }
153 return true;
154 }
155
readline()156 string iopipestream::readline()
157 {
158 sbuffer.clear();
159 int nc;
160 do {
161 nc=readbuffer();
162 sbuffer.append(buffer);
163 } while(buffer[nc-1] != '\n' && Running);
164 return sbuffer;
165 }
166
wait(const char * prompt)167 void iopipestream::wait(const char *prompt)
168 {
169 sbuffer.clear();
170 size_t plen=strlen(prompt);
171
172 do {
173 readbuffer();
174 sbuffer.append(buffer);
175 } while(!tailequals(sbuffer.c_str(),sbuffer.size(),prompt,plen));
176 }
177
wait()178 int iopipestream::wait()
179 {
180 for(;;) {
181 int status;
182 if (waitpid(pid,&status,0) == -1) {
183 if (errno == ECHILD) return 0;
184 if (errno != EINTR) {
185 ostringstream buf;
186 buf << "Process " << pid << " failed";
187 camp::reportError(buf);
188 }
189 } else {
190 if(WIFEXITED(status)) return WEXITSTATUS(status);
191 else {
192 ostringstream buf;
193 buf << "Process " << pid << " exited abnormally";
194 camp::reportError(buf);
195 }
196 }
197 }
198 }
199
Write(const string & s)200 void iopipestream::Write(const string &s)
201 {
202 ssize_t size=s.length();
203 if(settings::verbose > 2) cerr << s;
204 if(write(in[1],s.c_str(),size) != size) {
205 camp::reportFatal("write to pipe failed");
206 }
207 }
208