3 #include "ExecCLITool.h"
4 #include <Security/utilities.h>
5 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
7 #pragma mark -------------------- ExecCLITool implementation --------------------
9 ExecCLITool::ExecCLITool() : dataRead(NULL
),dataLength(0),dataToWrite(NULL
),dataToWriteLength(0)
11 stdinpipe
[0]=0, stdinpipe
[1]=0;
12 stdoutpipe
[0]=0, stdoutpipe
[1]=0;
15 ExecCLITool::~ExecCLITool()
22 int ExecCLITool::run(const char *toolPath
, const char *toolEnvVar
, ...)
29 // try to run the tool
30 switch (pid_t pid
= fork())
36 va_start(params
, toolEnvVar
);
37 arglist
.set(toolPath
,params
);
39 child(toolPath
,toolEnvVar
,arglist
);
42 case -1: // error (in parent)
60 void ExecCLITool::reset()
69 dataToWriteLength
= 0;
73 void ExecCLITool::input(const char *data
,unsigned int length
)
80 dataToWriteLength
=length
;
84 dataToWrite
=reinterpret_cast<char *>(malloc(length
));
85 ::memmove(dataToWrite
, data
, dataToWriteLength
);
88 void ExecCLITool::input(CFStringRef theString
, bool appendNULL
)
90 // Used mainly for preserving UTF-8 passwords
91 // hdiutil et al require the NULL to be sent as part of the password string from STDIN
92 Boolean isExternalRepresentation
= false;
93 CFStringEncoding encoding
= kCFStringEncodingUTF8
;
94 CFIndex usedBufLen
= 0;
98 MacOSError::throwMe(paramErr
);
100 CFRange stringRange
= CFRangeMake(0,CFStringGetLength(theString
));
101 // Call once first just to get length
102 CFIndex length
= CFStringGetBytes(theString
, stringRange
, encoding
, lossByte
,
103 isExternalRepresentation
, NULL
, 0, &usedBufLen
);
107 dataToWriteLength
=usedBufLen
;
114 dataToWrite
=reinterpret_cast<char *>(malloc(dataToWriteLength
));
115 length
= CFStringGetBytes(theString
, stringRange
, encoding
, lossByte
, isExternalRepresentation
,
116 reinterpret_cast<UInt8
*>(dataToWrite
), dataToWriteLength
, &usedBufLen
);
120 dataToWrite
[dataToWriteLength
-1]=0;
121 dataToWrite
[dataToWriteLength
]='\n';
125 void ExecCLITool::initialize()
127 dataLength
= 0; // ignore any previous output on new run
129 if (!dataRead
) // Allocate buffer for child's STDOUT return
131 dataRead
= (char *)malloc(256);
133 UnixError::throwMe();
136 // Create pipe to catch tool output
137 if (pipe(stdoutpipe
)) // for reading data from child into parent
138 UnixError::throwMe();
140 if (pipe(stdinpipe
)) // for writing data from parent to child
141 UnixError::throwMe();
144 void ExecCLITool::child(const char *toolPath
, const char *toolEnvVar
, VAArgList
& arglist
)
146 // construct path to tool
149 char toolExecutable
[PATH_MAX
+ 1];
150 const char *path
= toolEnvVar
? getenv(toolEnvVar
) : NULL
;
153 snprintf(toolExecutable
, sizeof(toolExecutable
), "%s", toolPath
);
155 close(stdoutpipe
[0]); // parent read
156 close(STDOUT_FILENO
);
157 if (dup2(stdoutpipe
[1], STDOUT_FILENO
) < 0)
158 UnixError::throwMe();
159 close(stdoutpipe
[1]);
161 close(stdinpipe
[1]); // parent write
163 if (dup2(stdinpipe
[0], STDIN_FILENO
) < 0)
164 UnixError::throwMe();
167 // std::cerr << "execl(\"" << toolExecutable << "\")" << std::endl;
168 execv(toolPath
, const_cast<char * const *>(arglist
.get()));
169 // std::cerr << "execl of " << toolExecutable << " failed, errno=" << errno << std::endl;
178 // Unconditional suicide follows.
182 void ExecCLITool::parent(pid_t pid
)
184 static const int timeout
= 300;
185 static const bool dontNeedToWait
= false;
187 close(stdinpipe
[0]); // child read
188 close(stdoutpipe
[1]); // child write
194 struct timespec rqtp
= {0,};
195 rqtp
.tv_nsec
= 100000000; // 10^8 nanoseconds = 1/10th of a second
196 for (int nn
= timeout
; nn
> 0; nanosleep(&rqtp
, NULL
), nn
--)
201 switch (waitpid(pid
, &status
, WNOHANG
))
203 case 0: // child still running
209 case EAGAIN
: // transient
211 case ECHILD
: // no such child (dead; already reaped elsewhere)
212 CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION
);
214 UnixError::throwMe();
217 // std::cerr << "waitpid succeeded, pid=" << rc << std::endl;
223 void ExecCLITool::parentReadOutput()
225 // parent - resulting blob comes in on stdoutpipe[0]
226 unsigned int totalRead
= 0;
227 char buffer
[kReadBufSize
];
231 int thisRead
= read(stdoutpipe
[0], buffer
, kReadBufSize
);
234 if (errno
==EINTR
) // try some more
236 // std::cerr << "abnormal read end:" << errno << std::endl;
239 if (thisRead
== 0) // normal termination
241 dataLength
= totalRead
;
242 // std::cerr << "Normal read end" << std::endl;
246 // Resize dataRead if necessary
247 if (kReadBufSize
< (totalRead
+ (unsigned int)thisRead
))
249 uint32 newLen
= dataLength
+ kReadBufSize
;
250 dataRead
= (char *)realloc(dataRead
, newLen
);
254 // Append the data to dataRead
255 memmove(dataRead
+ totalRead
, buffer
, thisRead
);
256 totalRead
+= thisRead
;
258 close(stdoutpipe
[0]);
262 void ExecCLITool::parentWriteInput()
264 if (dataToWriteLength
>0)
266 int bytesWritten
= write(stdinpipe
[1],dataToWrite
,dataToWriteLength
);
267 if (bytesWritten
< 0)
268 UnixError::throwMe();
273 void ExecCLITool::closeAllPipes()
275 for (int ix
=0;ix
<2;ix
++)
278 close(stdoutpipe
[ix
]);
282 for (int ix
=0;ix
<2;ix
++)
285 close(stdinpipe
[ix
]);
290 #pragma mark -------------------- VAArgList implementation --------------------
292 int VAArgList::set(const char *path
,va_list params
)
295 va_copy(params2
, params
);
297 // Count up the number of arguments
299 while (va_arg(params
,const char *) != NULL
)
302 argv
= (ArgvArgPtr
*)malloc((nn
+ 1) * sizeof(*argv
));
308 while ((argv
[nn
]=va_arg(params2
,const char *)) != NULL
)