1 /**************************************************************************/
2 /* */
3 /* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */
4 /* */
5 /* NXCOMP, NX protocol compression and NX extensions to this software */
6 /* are copyright of NoMachine. Redistribution and use of the present */
7 /* software is allowed according to terms specified in the file LICENSE */
8 /* which comes in the source distribution. */
9 /* */
10 /* Check http://www.nomachine.com/licensing.html for applicability. */
11 /* */
12 /* NX and NoMachine are trademarks of Medialogic S.p.A. */
13 /* */
14 /* All rights reserved. */
15 /* */
16 /**************************************************************************/
17
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <pwd.h>
22 #include <sys/types.h>
23 #include <sys/wait.h>
24
25 #include "Pipe.h"
26 #include "Misc.h"
27 #include "Fork.h"
28
29 //
30 // Set the verbosity level.
31 //
32
33 #define PANIC
34 #define WARNING
35 #undef TEST
36 #undef DEBUG
37
38 extern void RegisterChild(int child);
39
40 static int Psplit(const char *command, char *parameters[], int limit);
41
42 //
43 // These are slightly modified versions of popen(3) and pclose(3)
44 // that don't rely on a shell to be available on the system, so
45 // that they can also work on Windows. As an additional benefit,
46 // these functions give up all privileges before running the com-
47 // mand. Code is taken from the X distribution and, in turn, is
48 // based on libc from FreeBSD 2.2.
49 //
50
51 static struct pid
52 {
53 struct pid *next;
54 FILE *fp;
55 int self;
56
57 } *pidlist;
58
59 //
60 // A very unsofisticated attempt to parse the command line and
61 // split each parameter in distinct strings. This is not going
62 // to work when dealing with parameters containing spaces, even
63 // if they are enclosed in quotes.
64 //
65
Psplit(const char * command,char * parameters[],int limit)66 int Psplit(const char *command, char *parameters[], int limit)
67 {
68 char *line;
69 char *value;
70
71 int number;
72
73 //
74 // Preapare the list of parameters.
75 //
76
77 for (number = 0; number < limit; number++)
78 {
79 parameters[number] = NULL;
80 }
81
82 //
83 // Copy the command to get rid of the
84 // const qualifier.
85 //
86
87 line = new char[strlen(command) + 1];
88
89 if (line == NULL)
90 {
91 goto PsplitError;
92 }
93
94 strcpy(line, command);
95
96 number = 0;
97
98 value = strtok(line, " ");
99
100 while (value != NULL && number < limit)
101 {
102 #ifdef DEBUG
103 *logofs << "Psplit: Got parameter '" << value
104 << "'.\n" << logofs_flush;
105 #endif
106
107 parameters[number] = new char[strlen(value) + 1];
108
109 if (parameters[number] == NULL)
110 {
111 goto PsplitError;
112 }
113
114 strcpy(parameters[number], value);
115
116 number++;
117
118 //
119 // If this is the first parameter, then
120 // copy it in the second position and
121 // use it as the name of the command.
122 //
123
124 if (number == 1)
125 {
126 parameters[number] = new char[strlen(value) + 1];
127
128 if (parameters[number] == NULL)
129 {
130 goto PsplitError;
131 }
132
133 strcpy(parameters[number], value);
134
135 number++;
136 }
137
138 value = strtok(NULL, " ");
139 }
140
141 //
142 // Needs at least to have the command itself and
143 // the first argument, being again the name of
144 // the command.
145 //
146
147 if (number < 2)
148 {
149 goto PsplitError;
150 }
151
152 return number;
153
154 PsplitError:
155
156 #ifdef PANIC
157 *logofs << "Psplit: PANIC! Can't split command line '"
158 << command << "'.\n" << logofs_flush;
159 #endif
160
161 cerr << "Error" << ": Can't split command line '"
162 << command << "'.\n";
163
164 delete [] line;
165
166 return -1;
167 }
168
Popen(char * const parameters[],const char * type)169 FILE *Popen(char * const parameters[], const char *type)
170 {
171 FILE *iop;
172
173 struct pid *cur;
174 int pdes[2], pid;
175
176 if (parameters == NULL || type == NULL)
177 {
178 return NULL;
179 }
180
181 if ((*type != 'r' && *type != 'w') || type[1])
182 {
183 return NULL;
184 }
185
186 if ((cur = (struct pid *) malloc(sizeof(struct pid))) == NULL)
187 {
188 return NULL;
189 }
190
191 if (pipe(pdes) < 0)
192 {
193 free(cur);
194
195 return NULL;
196 }
197
198 //
199 // Block all signals until command is exited.
200 // We need to gather information about the
201 // child in Pclose().
202 //
203
204 DisableSignals();
205
206 switch (pid = Fork())
207 {
208 case -1:
209 {
210 //
211 // Error.
212 //
213
214 #ifdef PANIC
215 *logofs << "Popen: PANIC! Function fork failed. "
216 << "Error is " << EGET() << " '" << ESTR()
217 << "'.\n" << logofs_flush;
218 #endif
219
220 cerr << "Error" << ": Function fork failed. "
221 << "Error is " << EGET() << " '" << ESTR()
222 << "'.\n";
223
224 close(pdes[0]);
225 close(pdes[1]);
226
227 free(cur);
228
229 return NULL;
230 }
231 case 0:
232 {
233 //
234 // Child.
235 //
236
237 setgid(getgid());
238 setuid(getuid());
239
240 if (*type == 'r')
241 {
242 if (pdes[1] != 1)
243 {
244 //
245 // Set up stdout.
246 //
247
248 dup2(pdes[1], 1);
249 close(pdes[1]);
250 }
251
252 close(pdes[0]);
253 }
254 else
255 {
256 if (pdes[0] != 0)
257 {
258 //
259 // Set up stdin.
260 //
261
262 dup2(pdes[0], 0);
263 close(pdes[0]);
264 }
265
266 close(pdes[1]);
267 }
268
269 execvp(parameters[0], parameters + 1);
270
271 exit(127);
272 }
273 }
274
275 //
276 // Parent. Save data about the child.
277 //
278
279 RegisterChild(pid);
280
281 if (*type == 'r')
282 {
283 iop = fdopen(pdes[0], type);
284
285 close(pdes[1]);
286 }
287 else
288 {
289 iop = fdopen(pdes[1], type);
290
291 close(pdes[0]);
292 }
293
294 cur -> fp = iop;
295 cur -> self = pid;
296 cur -> next = pidlist;
297
298 pidlist = cur;
299
300 #ifdef TEST
301 *logofs << "Popen: Executing ";
302
303 for (int i = 0; i < 256 && parameters[i] != NULL; i++)
304 {
305 *logofs << "[" << parameters[i] << "]";
306 }
307
308 *logofs << " with descriptor " << fileno(iop)
309 << ".\n" << logofs_flush;
310 #endif
311
312 return iop;
313 }
314
Popen(const char * command,const char * type)315 FILE *Popen(const char *command, const char *type)
316 {
317 char *parameters[256];
318
319 if (Psplit(command, parameters, 256) > 0)
320 {
321 FILE *file = Popen(parameters, type);
322
323 for (int i = 0; i < 256; i++)
324 {
325 delete [] parameters[i];
326 }
327
328 return file;
329 }
330 else
331 {
332 #ifdef PANIC
333 *logofs << "Popen: PANIC! Failed to parse command '"
334 << command << "'.\n" << logofs_flush;
335 #endif
336
337 cerr << "Error" << ": Failed to parse command '"
338 << command << "'.\n";
339
340 return NULL;
341 }
342 }
343
Pclose(FILE * iop)344 int Pclose(FILE *iop)
345 {
346 struct pid *cur, *last;
347
348 int pstat;
349 int pid;
350
351 #ifdef TEST
352 *logofs << "Pclose: Closing command with output "
353 << "on descriptor " << fileno(iop) << ".\n"
354 << logofs_flush;
355 #endif
356
357 fclose((FILE *) iop);
358
359 for (last = NULL, cur = pidlist; cur; last = cur, cur = cur -> next)
360 {
361 if (cur -> fp == iop)
362 {
363 break;
364 }
365 }
366
367 if (cur == NULL)
368 {
369 #ifdef PANIC
370 *logofs << "Pclose: PANIC! Failed to find the process "
371 << "for descriptor " << fileno(iop) << ".\n"
372 << logofs_flush;
373 #endif
374
375 cerr << "Error" << ": Failed to find the process "
376 << "for descriptor " << fileno(iop) << ".\n";
377
378 return -1;
379 }
380
381 do
382 {
383 #ifdef TEST
384 *logofs << "Pclose: Going to wait for process "
385 << "with pid '" << cur -> self << "'.\n"
386 << logofs_flush;
387 #endif
388
389 pid = waitpid(cur -> self, &pstat, 0);
390 }
391 while (pid == -1 && errno == EINTR);
392
393 if (last == NULL)
394 {
395 pidlist = cur -> next;
396 }
397 else
398 {
399 last -> next = cur -> next;
400 }
401
402 free(cur);
403
404 //
405 // Child has finished and we called the
406 // waitpid(). We can enable signals again.
407 //
408
409 EnableSignals();
410
411 return (pid == -1 ? -1 : pstat);
412 }
413