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