1 /*****************************************************************************/
2 /* Software Testing Automation Framework (STAF)                              */
3 /* (C) Copyright IBM Corp. 2001                                              */
4 /*                                                                           */
5 /* This software is licensed under the Eclipse Public License (EPL) V1.0.    */
6 /*****************************************************************************/
7 
8 #include "STAF.h"
9 #include "STAF_iostream.h"
10 #include <sys/types.h>
11 #include <cstdlib>
12 #include <sys/stat.h>
13 #include <sys/wait.h>
14 #include <fcntl.h>
15 #include <signal.h>
16 #include <ctype.h>
17 #include <deque>
18 #include <map>
19 #include <list>
20 #include "STAFProcess.h"
21 #include "STAFMutexSem.h"
22 #include "STAFEventSem.h"
23 #include "STAFUtil.h"
24 #include "STAFUtilUnix.h"
25 #include "STAFThreadManager.h"
26 #include "STAFTrace.h"
27 #include "STAFInternalProcess.h"
28 
29 #ifdef STAF_OS_NAME_ZOS
30 #include <spawn.h>
31 
32 extern char **environ;
33 #endif
34 
35 extern "C"
36 {
37 // XXX: What's up with these #define's?
38 #ifndef   _XOPEN_SOURCE
39 #define   _XOPEN_SOURCE
40 #endif // _XOPEN_SOURCE
41 
42 #include <pwd.h>
43 }
44 
45 #define MONITOR_SLEEP_SECONDS 1
46 
47 // Static data used to start process
48 struct ProcessCreateInfo
49 {
ProcessCreateInfoProcessCreateInfo50     ProcessCreateInfo(STAFProcessCommandType_t theCommandType =
51                           kSTAFProcessCommand,
52                       const STAFStringBufferPtr &cmd  =
53                           STAFStringBufferPtr(),
54                       char **arg = 0, char **env = 0,
55                       STAFUserID_t  UID = getuid(),
56                       STAFGroupID_t GID = getgid(),
57                       const STAFStringBufferPtr &username  =
58                           STAFStringBufferPtr(),
59                       const STAFStringBufferPtr &dir  =
60                           STAFStringBufferPtr(),
61                       STAFProcessRedirectedIOMode_t theStdinMode =
62                           kSTAFProcessIONoRedirect,
63                       int stdinFD = dup(0),
64                       STAFProcessRedirectedIOMode_t theStdoutMode =
65                           kSTAFProcessIONoRedirect,
66                       int stdoutFD = dup(1),
67                       STAFProcessRedirectedIOMode_t theStderrMode =
68                           kSTAFProcessIONoRedirect,
69                       int stderrFD = dup(2),
70                       STAFProcessEndCallbackLevel1 cback =
71                           STAFProcessEndCallbackLevel1())
72             : commandType(theCommandType), command(cmd), argv(arg), envp(env),
73               workdir(dir), uid(UID), gid(GID), userName(username),
74               stdinMode(theStdinMode), child_stdin(stdinFD),
75               stdoutMode(theStdoutMode), child_stdout(stdoutFD),
76               stderrMode(theStderrMode), child_stderr(stderrFD),
77               callback(cback)
78     {  /* Do nothing */ }
79 
80     int                           pid;
81     STAFUserID_t                  uid;
82     STAFGroupID_t                 gid;
83     STAFStringBufferPtr           userName;
84     char                        **argv;
85     char                        **envp;
86     STAFProcessCommandType_t      commandType;
87     STAFStringBufferPtr           command;
88     STAFStringBufferPtr           workdir;
89     STAFProcessRedirectedIOMode_t stdinMode;
90     int                           child_stdin;
91     STAFProcessRedirectedIOMode_t stdoutMode;
92     int                           child_stdout;
93     STAFProcessRedirectedIOMode_t stderrMode;
94     int                           child_stderr;
95     STAFProcessEndCallbackLevel1  callback;
96 };
97 
98 // Data passed to the callback thread
99 struct ProcessMonitorCallbackInfo
100 {
ProcessMonitorCallbackInfoProcessMonitorCallbackInfo101     ProcessMonitorCallbackInfo(STAFProcessEndCallbackLevel1 theCallback,
102                                STAFProcessID_t thePID,
103                                STAFProcessHandle_t theHandle,
104                                unsigned int theRC)
105         : callback(theCallback), pid(thePID), handle(theHandle), rc(theRC)
106     { /* Do Nothing */ }
107 
108     STAFProcessEndCallbackLevel1 callback;
109     STAFProcessID_t pid;
110     STAFProcessHandle_t handle;
111     unsigned int rc;
112 };
113 
114 struct ProcessMonitorInfo
115 {
ProcessMonitorInfoProcessMonitorInfo116     ProcessMonitorInfo (STAFProcessHandle_t aHandle = 0,
117                         STAFProcessID_t     aPID    = 0,
118                         STAFProcessEndCallbackLevel1 aCallback =
119                             STAFProcessEndCallbackLevel1())
120         : handle(aHandle), pid(aPID), callback(aCallback)
121     {
122         /* Do nothing */
123     }
124 
125     STAFProcessHandle_t handle;
126     STAFProcessID_t pid;
127     STAFProcessEndCallbackLevel1 callback;
128 };
129 
130 typedef std::deque<ProcessMonitorInfo> ProcessMonitorList;
131 typedef std::map<STAFProcessID_t, ProcessMonitorList> ProcessMonitorMap;
132 
133 // Perform OS Specific User Authentication
134 static unsigned int UserAuthenticate(STAFUserID_t &uid, STAFGroupID_t &gid,
135                                      const STAFString &username,
136                                      const STAFString &password,
137                                      bool mustValidate, unsigned int *osRC);
138 
139 static unsigned int ProcessMonitorCallbackThread(void *data);
140 static unsigned int ProcessMonitorThread(void *);
141 static int ParseCommandParms(STAFString &, char ***);
142 
143 static ProcessCreateInfo sProcessCreateInfo;
144 static STAFMutexSem sMonitorDataSem;
145 static ProcessMonitorMap sMonitorMap;
146 static STAFUserID_t  sOurUID;
147 static STAFGroupID_t sOurGID;
148 static STAFMutexSem sCreateProcessSem;
149 static STAFEventSem sProcessCreated;
150 static STAFEventSem sProcessThread;
151 
152 // private prototypes for user authentication functions
153 static unsigned int sAuthNone(const char *, const char *);
154 static unsigned int sAuthPam(const char *, const char *);
155 
156 // private function pointer must be initialized to whatever fAuthMode is
157 static unsigned int (*sAuthenticateFuncPtr)(const char *username,
158                                             const char *password) = sAuthNone;
159 
160 // Default stdout/stderr redirection file if new console
161 static STAFString sDefaultRedirectFile = STAFString("/dev/null");
162 
163 // Substitution characters valid for a shell command on Unix
164 const char *gSTAFProcessShellSubstitutionChars = "cCpPtTuUwWxX%";
165 
166 
getProcessThreadManager()167 static STAFThreadManager &getProcessThreadManager()
168 {
169   static STAFThreadManager theThreadManager(1);
170   return theThreadManager;
171 }
172 
173 
174 // XXX: This function needs to be called in a number of different places.
175 //      Check in windows version also.
176 
InitProcessManager()177 static void InitProcessManager()
178 {
179     static STAFMutexSem initSem;
180     static bool alreadyInited = false;
181 
182     if (alreadyInited) return;
183 
184     STAFMutexSemLock initLock(initSem);
185 
186     if (alreadyInited) return;
187 
188     sOurUID = getuid();
189     sOurGID = getgid();
190 
191     unsigned int rc = getProcessThreadManager().dispatch(
192         ProcessMonitorThread, 0);
193 
194     if (rc != 0)
195     {
196         STAFTrace::trace(
197             kSTAFTraceError,
198             STAFString("STAFProcess::InitProcessManager: "
199                        "Error dispatching a thread, RC: ") + rc);
200 
201         return;
202     }
203 
204     alreadyInited = true;
205 }
206 
207 
ProcessMonitorCallbackThread(void * data)208 unsigned int ProcessMonitorCallbackThread(void *data)
209 {
210     ProcessMonitorCallbackInfo *pInfo =
211         static_cast<ProcessMonitorCallbackInfo *>(data);
212 
213     pInfo->callback.callback(pInfo->pid, pInfo->handle, pInfo->rc,
214                              pInfo->callback.data);
215 
216     delete pInfo;
217 
218     return 0;
219 }
220 
221 
ProcessMonitorThread(void *)222 unsigned int ProcessMonitorThread(void *)
223 {
224     // Monitor processes in a forever loop until STAF shuts down.  This thread
225     // will clean both processes registered outside of STAF (outsiders) and
226     // processes registered from inside of STAF (insiders).
227 
228     // Due to the linux threading model, we have changed this code so that
229     // processes get started from the thread that monitors which processes have
230     // terminated.  This is b/c of a bug on waitpid() which does not work if a
231     // process gets started from another thread, which was the case before.
232 
233     std::list<STAFProcessID_t> fgPIDList;
234     int theTTY = open("/dev/tty", O_RDONLY);
235 
236     if (theTTY < 0)
237     {
238         STAFString errorMessage("STAFProcess::processMonitorThread: "
239                                 "Error opening /dev/tty, errno: ");
240         errorMessage += errno;
241         STAFTrace::trace(kSTAFTraceError, errorMessage);
242     }
243 
244     while (1)
245     {
246         int foundProcess = 0;
247 
248         try
249         {
250             // NOTE: waitpid() returns -1 if no more child processes exist,
251             //       returns 0 if no child processes have ended, or returns
252             //       pid of process that has sent a stop or terminate signal.
253             //       Outsider processes are NOT considered child processes.
254 
255             ProcessMonitorList processMonitorList;
256             int retCode = 0;
257             int childId = 0;
258 
259             {
260                 // Acquire lock
261 
262                 STAFMutexSemLock lock(sMonitorDataSem);
263 
264                 // Note: the waitpid function with WNOHANG does NOT block, so
265                 // the lock will NOT be held for long periods of time.
266 
267                 // waitpid() is protected to avoid the following situation:
268                 // assume no insiders exist (ie. no children) and 0 or more
269                 // outsiders exist. then waitpid would return -1 (childId = -1)
270                 // and set retCode to 0.  This thread gets preempted by
271                 // startProcess2() and an internal process is added to the front
272                 // of the list terminating quickly after being added.  The
273                 // thread then gets control back.  The first condition fails
274                 // since childId < 0, and the second condition succeeds since
275                 // childId < 0 and the process has terminated.  This makes the
276                 // return code of the process to be a fake 0 rather than its
277                 // real return code.  Locking before calling waitpid() solves
278                 // this problem.
279 
280                 childId = waitpid(-1, &retCode, WNOHANG | WUNTRACED);
281 
282                 // if the childId > 0 then we look for it in the list, else
283                 // if the childId <= 0, we look for a victim outsider process
284                 // that has terminated.
285                 // Note:  For outsider processes, we have to check that kill()
286                 //         didn't set errno to EPERM because that means it does
287                 //         not have permission to send the signal to the
288                 //         process pid (e.g. could have been started by another
289                 //         user), not that the process is terminated.
290 
291                 ProcessMonitorMap::iterator iter = sMonitorMap.begin();
292 
293                 while (iter != sMonitorMap.end())
294                 {
295                     if (((childId > 0) && (iter->first == childId) &&
296                         (WIFEXITED(retCode) || WIFSIGNALED(retCode))) ||
297                         ((childId <= 0) && (kill(iter->first, 0) == -1) &&
298                          (errno != EPERM)))
299                     {
300                         // If the child is no longer running, then remove it
301                         // from the list of foreground waiters
302 
303                         if (childId > 0) fgPIDList.remove(childId);
304 
305                         // If the child was in the foreground then put STAF
306                         // back in the foreground.  Farther below, we check for
307                         // other children who want the foreground.
308 
309                         if ((childId > 0) && (tcgetpgrp(theTTY) == childId) &&
310                             (tcsetpgrp(theTTY, getpgrp()) < 0))
311                         {
312                             STAFString errorMessage("STAFProcess::"
313                                                     "processMonitorThread: "
314                                                     "Error on tcsetpgrp(), "
315                                                     "errno: ");
316                             errorMessage += errno;
317                             STAFTrace::trace(kSTAFTraceError, errorMessage);
318                         }
319 
320                         processMonitorList = iter->second;
321                         sMonitorMap.erase(iter->first);
322                         foundProcess = 1;
323                         break;
324                     }
325 
326                     iter++;
327                 }
328 
329             }   // release lock
330 
331             if (foundProcess)
332             {
333                 // Note: The callback needs to be executed by a separate
334                 // thread to avoid deadlock with STAFProcessService when
335                 // started process finishes before it's added to the list
336                 // and attempts to call it's callback function.
337 
338                 for (ProcessMonitorList::iterator monitorIter =
339                          processMonitorList.begin();
340                      (monitorIter != processMonitorList.end()) &&
341                          (monitorIter->callback.callback != 0);
342                      ++monitorIter)
343                 {
344                     unsigned int dispatchRC = getProcessThreadManager().dispatch(
345                         ProcessMonitorCallbackThread,
346                         new ProcessMonitorCallbackInfo(monitorIter->callback,
347                         monitorIter->pid, monitorIter->handle,
348                         WIFEXITED(retCode) ? WEXITSTATUS(retCode) :
349                         WTERMSIG(retCode)));
350 
351                     if (dispatchRC != 0)
352                     {
353                         STAFTrace::trace(
354                             kSTAFTraceError,
355                             STAFString("STAFProcess::processMonitorThread: "
356                                        "Error dispatching a thread, RC: ") +
357                             dispatchRC);
358                     }
359 
360                     // Wait for 1 millisecond to prevent a flood of callback
361                     // requests being submitted all at once, possibly
362                     // resulting in new threads being created to run the
363                     // callback requests
364 
365                     STAFThreadSleepCurrentThread(1, 0);
366                 }
367             }
368             else if (!foundProcess && (childId > 0))
369             {
370                 if (WIFSTOPPED(retCode))
371                 {
372                     fgPIDList.push_back(childId);
373                 }
374                 else
375                 {
376                     STAFTrace::trace(kSTAFTraceError, "STAFProcess::"
377                                      "processMonitorThread: Signaled process "
378                                      "(PID: " + STAFString(childId) +
379                                      ") not in process monitor's thread list");
380                 }
381             }
382 
383             // If STAF is in the foreground and the list of children wanting
384             // the foreground is not empty, then put the first one in the list
385             // in the foreground
386 
387             if (!fgPIDList.empty() && (getpgrp() == tcgetpgrp(theTTY)))
388             {
389                 STAFProcessID_t nextFGPID = fgPIDList.front();
390 
391                 fgPIDList.pop_front();
392 
393                 if (tcsetpgrp(theTTY, nextFGPID) < 0)
394                 {
395                     STAFString errorMessage("STAFProcess::"
396                                             "processMonitorThread: Error on "
397                                             "tcsetpgrp(), errno: ");
398                     errorMessage += errno;
399                     STAFTrace::trace(kSTAFTraceError, errorMessage);
400                 }
401 
402                 if (kill(nextFGPID, SIGCONT) < 0)
403                 {
404                     STAFString errorMessage("STAFProcess::"
405                                             "processMonitorThread: "
406                                             "Error continuing PID: ");
407                     errorMessage += nextFGPID;
408                     errorMessage += ", errno: ";
409                     errorMessage += errno;
410                     STAFTrace::trace(kSTAFTraceError, errorMessage);
411                 }
412             }
413 
414             // If no process was found, lets go to sleep for some time,
415             // unless we get posted meaning we need to create a process
416 
417             if (!foundProcess)
418             {
419                 // if posted, create the process indicated in fProcessCreateInfo
420                 if (!sProcessThread.wait(MONITOR_SLEEP_SECONDS * 1000))
421                 {
422 #ifndef STAF_OS_NAME_ZOS
423                     // create the new process
424                     if ((sProcessCreateInfo.pid = STAF_FORK()) == 0)
425                     {
426                         // child process
427 
428                         // setgid must be done first, as if we change the uid
429                         // first then we are not able to change the gid since
430                         // we may no longer have enough privilege to do that.
431 
432                         if (setgid(sProcessCreateInfo.gid) < 0)
433                         {
434                             STAFTrace::trace(kSTAFTraceError,
435                                 STAFString("STAFProcess::"
436                                            "processMonitorThread: Child could "
437                                            "not set child's gid: ") + errno);
438                             _exit(1);
439                         }
440 
441                         if (setuid(sProcessCreateInfo.uid) < 0)
442                         {
443                             STAFTrace::trace(kSTAFTraceError,
444                                 STAFString("STAFProcess::"
445                                            "processMonitorThread: Child could "
446                                            "not set child's uid: ") + errno);
447                             _exit(1);
448                         }
449 
450                         if (setpgid(0, 0) < 0)
451                         {
452                             STAFTrace::trace(kSTAFTraceInfo,
453                                 STAFString("STAFProcess::"
454                                            "processMonitorThread: Child could "
455                                            "not set child's pgid: ") + errno);
456                             _exit(1);
457                         }
458 
459                         close(0);
460                         dup(sProcessCreateInfo.child_stdin);
461                         close(sProcessCreateInfo.child_stdin);
462 
463                         close(1);
464                         dup(sProcessCreateInfo.child_stdout);
465                         close(sProcessCreateInfo.child_stdout);
466 
467                         close(2);
468                         dup(sProcessCreateInfo.child_stderr);
469                         close(sProcessCreateInfo.child_stderr);
470 
471                         // change working directory if appropriate
472                         if (sProcessCreateInfo.workdir->length() != 0)
473                         {
474                             chdir(sProcessCreateInfo.workdir->buffer());
475                         }
476 
477                         if (sProcessCreateInfo.envp == 0)
478                         {
479                             // overwrite memory with new process image
480                             if (execv((sProcessCreateInfo.commandType ==
481                                        kSTAFProcessCommand) ?
482                                           sProcessCreateInfo.command->buffer() :
483                                           "/bin/sh",
484                                       sProcessCreateInfo.argv))
485                             {
486                                 STAFTrace::trace(kSTAFTraceError,
487                                                  STAFString("STAFProcess::"
488                                                  "processMonitorThread: Could "
489                                                  "not start process (execve):") +
490                                                  errno);
491                                 _exit(1);  // this is the child process
492                             }
493                         }
494                         else
495                         {
496                             // overwrite memory with new process image
497                             if (execve((sProcessCreateInfo.commandType ==
498                                         kSTAFProcessCommand) ?
499                                            sProcessCreateInfo.command->buffer() :
500                                            "/bin/sh",
501                                        sProcessCreateInfo.argv,
502                                        sProcessCreateInfo.envp))
503                             {
504                                 STAFTrace::trace(kSTAFTraceError,
505                                                  STAFString("STAFProcess::"
506                                                  "processMonitorThread: Could "
507                                                  "not start process (execve):") +
508                                                  errno);
509                                 _exit(1);  // this is the child process
510                             }
511                         }
512                     }
513 
514                     // parent process
515 
516                     if (setpgid(sProcessCreateInfo.pid,
517                                 sProcessCreateInfo.pid) < 0)
518                     {
519                         STAFTrace::trace(kSTAFTraceInfo,
520                             STAFString("STAFProcess::"
521                                        "processMonitorThread: Parent could not "
522                                        "set child's pgid: ") + errno);
523                     }
524 
525 #else // STAF_OS_NAME_ZOS
526 
527                     struct __inheritance inherit;
528 
529                     // Set inheritance flags to create a new process group
530                     inherit.flags = SPAWN_SETGROUP;
531                     inherit.pgroup = SPAWN_NEWPGROUP;
532 
533                     int fd_count = 3;
534                     int fd_map[3] = { sProcessCreateInfo.child_stdin,
535                                       sProcessCreateInfo.child_stdout,
536                                       sProcessCreateInfo.child_stderr };
537 
538                     //Set working directory
539                     if (sProcessCreateInfo.workdir->length() != 0) {
540                         inherit.flags |= SPAWN_SETCWD;
541                         inherit.cwdptr = const_cast<char *>(
542                                          sProcessCreateInfo.workdir->buffer());
543                         inherit.cwdlen = sProcessCreateInfo.workdir->length();
544                     }
545 
546                     //Set userid
547                     if (sProcessCreateInfo.uid != getuid())
548                     {
549                         inherit.flags |= SPAWN_SETUSERID;
550 
551                         // Note: The length of the inherit.userid buffer is
552                         //       eight.  Thus, we don't want to overwrite
553                         //       anything after it.
554 
555                         strncpy(inherit.userid,
556                                 sProcessCreateInfo.userName->buffer(), 8);
557                         inherit.userid[8] = 0;
558                     }
559 
560                     // create the new process
561                     sProcessCreateInfo.pid = __spawn2(
562                         (sProcessCreateInfo.commandType == kSTAFProcessCommand)
563                             ? sProcessCreateInfo.command->buffer()
564                             : "/bin/sh",
565                         fd_count, fd_map, &inherit,
566                         (const char**) sProcessCreateInfo.argv,
567                         (const char**) ((sProcessCreateInfo.envp == 0) ?
568                                         environ : sProcessCreateInfo.envp));
569 
570                     // parent process
571                     if (sProcessCreateInfo.pid == -1)
572                     {
573                         STAFTrace::trace(kSTAFTraceError,
574                                          STAFString("STAFProcess::"
575                                          "processMonitorThread(): Could "
576                                          "not start process: ") + errno);
577                     }
578 #endif
579                     //Close file descriptors in the parent
580                     close(sProcessCreateInfo.child_stdin);
581                     close(sProcessCreateInfo.child_stdout);
582                     close(sProcessCreateInfo.child_stderr);
583 
584                     {   // acquire lock
585                         STAFMutexSemLock lock(sMonitorDataSem);
586 
587                         // add new process to list
588                         sMonitorMap[sProcessCreateInfo.pid].push_back(
589                             ProcessMonitorInfo(sProcessCreateInfo.pid,
590                                                sProcessCreateInfo.pid,
591                                                sProcessCreateInfo.callback));
592                     }   // release lock
593 
594                     // reset this thread's sem and post the process manager
595                     // so that it can deallocate any used resources
596 
597                     sProcessThread.reset();
598                     sProcessCreated.post();
599 
600                 }  // if process thread was posted during wait
601             }  // if not process found by waitpid
602         } // try block
603         catch (STAFException &se)
604         {
605             se.trace("STAFProcess::ProcessMonitorThread()");
606         }
607         catch (...)
608         {
609             STAFTrace::trace(
610                 kSTAFTraceError,
611                 "Caught unknown exception in "
612                 "STAFProcess::ProcessMonitorThread()");
613         }
614     }  // endless loop
615 
616     return kSTAFOk;
617 }
618 
STAFProcessStart(STAFProcessID_t * pid,STAFProcessHandle_t * procHandle,void * data,unsigned int startDataLevel,unsigned int * osRC)619 STAFRC_t STAFProcessStart(STAFProcessID_t *pid, STAFProcessHandle_t *procHandle,
620                           void *data, unsigned int startDataLevel,
621                           unsigned int *osRC)
622 {
623     STAFString_t errorBuffer = 0;
624 
625     return STAFProcessStart2(pid, procHandle, data, startDataLevel,
626                              osRC, &errorBuffer);
627 }
628 
STAFProcessStart2(STAFProcessID_t * pid,STAFProcessHandle_t * procHandle,void * data,unsigned int startDataLevel,unsigned int * osRC,STAFString_t * errorBuffer)629 STAFRC_t STAFProcessStart2(STAFProcessID_t *pid, STAFProcessHandle_t *procHandle,
630                            void *data, unsigned int startDataLevel,
631                            unsigned int *osRC, STAFString_t *errorBuffer)
632 {
633     if (data == 0)
634     {
635         if (errorBuffer)
636         {
637             STAFString errMsg = STAFString(
638                 "Invalid data provided to STAFProcessStart2()");
639             *errorBuffer = errMsg.adoptImpl();
640         }
641 
642         return kSTAFInvalidValue;
643     }
644 
645     if (startDataLevel != 1)
646     {
647         if (errorBuffer)
648         {
649             STAFString errMsg = STAFString(
650                 "Invalid level provided to STAFProcessStart2(): ") +
651                 startDataLevel;
652             *errorBuffer = errMsg.adoptImpl();
653         }
654 
655         return kSTAFInvalidValue;
656     }
657 
658     unsigned int systemErrorCode = 0;
659 
660     STAFProcessStartInfoLevel1 *startData =
661         reinterpret_cast<STAFProcessStartInfoLevel1 *>(data);
662 
663     InitProcessManager();
664 
665     STAFString command(startData->command);
666 
667     struct stat fileinfo;
668 
669     if (startData->commandType == kSTAFProcessShell)
670         command = command.subWord(0, 1);
671 
672     // Since a shell command could be "date; grep abc abc", cannot verify
673     // that the first word (e.g. date;) is a valid command so only do this
674     // check that the command is valid if not a shell command.
675     if (startData->commandType != kSTAFProcessShell)
676     {
677         // Note: this checking is due to stat which requires a path'd file
678         if (command.find(kUTF8_SLASH) == STAFString::kNPos)
679         {
680             STAFString_t path;
681 
682             // If no slashes found, resolve the command to form its path'd name
683             // Note: getFilePath will always return a path that does not term-
684             // inate with "/"
685 
686             STAFRC_t rc = STAFUtilUnixGetFilePath(command.getImpl(), &path,
687                                                   osRC);
688             if (rc != kSTAFOk)
689             {
690                 if (rc == kSTAFDoesNotExist)
691                 {
692                     if (osRC) *osRC = ENOENT;
693 
694                     if (errorBuffer)
695                     {
696                         STAFString errMsg = STAFString(
697                             "Command does not exist: ") + command;
698                         *errorBuffer = errMsg.adoptImpl();
699                     }
700 
701                     return kSTAFBaseOSError;
702                 }
703 
704                 if (errorBuffer)
705                 {
706                     STAFString errMsg = STAFString("Invalid command: ") +
707                         command;
708                     *errorBuffer = errMsg.adoptImpl();
709                 }
710 
711                 return rc;
712             }
713 
714             // Command contains path of startData->command (from getFilePath
715             // call), so here we construct something like "/usr/bin" + "/" +
716             // "ls" = "/usr/bin/ls"
717 
718             // ???: This used to end with startData->command, not just command.
719             //      This messed things up when SHELL was added.  There doesn't
720             //      seem to be a reason why startData->command was used instead
721             //      of just command.
722 
723             command = STAFString(path, STAFString::kShallow) +
724                       STAFString("/") + command;
725         }
726 
727         // Check if command is a regular file and has execute perms
728         if (stat(command.toCurrentCodePage()->buffer(), &fileinfo) == -1)
729         {
730             systemErrorCode = errno;
731 
732             if (osRC) *osRC = systemErrorCode;  // set by stat
733 
734             if (errorBuffer)
735             {
736                 STAFString errMsg = STAFString("Invalid command: ") + command +
737                     "\nThe command is not a file or does not have execute "
738                     "permissions.\nOS RC " + systemErrorCode;
739                 *errorBuffer = errMsg.adoptImpl();
740             }
741 
742             return kSTAFBaseOSError;
743         }
744         else if (!S_ISREG(fileinfo.st_mode))
745         {
746             if (osRC) *osRC = ENOEXEC; // exec format error
747 
748             if (errorBuffer)
749             {
750                 STAFString errMsg = STAFString("Invalid command: ") +
751                     command + "\nThe command is not a valid executable.";
752                 *errorBuffer = errMsg.adoptImpl();
753             }
754 
755             return kSTAFBaseOSError;
756         }
757         else if (!S_ISEXE(fileinfo.st_mode))
758         {
759             if (osRC) *osRC = EACCES;  // permission denied
760 
761             if (errorBuffer)
762             {
763                 STAFString errMsg = STAFString("Cannot execute command: ") +
764                     command + "\nIt does not have the necessary permissions.";
765                 *errorBuffer = errMsg.adoptImpl();
766             }
767 
768             return kSTAFBaseOSError;
769         }
770     }
771 
772     // Check workdir (if any) is a directory file and has execute perms
773     if (startData->workdir != 0)
774     {
775         if (stat(STAFString(startData->workdir).toCurrentCodePage()->buffer(),
776             &fileinfo) == -1)
777         {
778             systemErrorCode = errno;
779 
780             if (osRC) *osRC = systemErrorCode;  // set by stat
781 
782             if (errorBuffer)
783             {
784                 STAFString errMsg = STAFString("Invalid working directory: ") +
785                     startData->workdir + "\nOS RC " + systemErrorCode;
786                 *errorBuffer = errMsg.adoptImpl();
787             }
788 
789             return kSTAFBaseOSError;
790         }
791         else if (!S_ISDIR(fileinfo.st_mode))
792         {
793             if (osRC) *osRC = ENOTDIR;  // not a directory
794 
795             if (errorBuffer)
796             {
797                 STAFString errMsg = STAFString("Working directory is not ") +
798                     "a directory: " + startData->workdir;
799                 *errorBuffer = errMsg.adoptImpl();
800             }
801 
802             return kSTAFBaseOSError;
803         }
804         else if (!S_ISEXE(fileinfo.st_mode))
805         {
806             if (osRC) *osRC = EACCES;   // permission denied
807 
808             if (errorBuffer)
809             {
810                 STAFString errMsg = STAFString("Working directory ") +
811                     startData->workdir +
812                     " does not have the necessary permissions.";
813                 *errorBuffer = errMsg.adoptImpl();
814             }
815 
816             return kSTAFBaseOSError;
817         }
818     }
819 
820     // Note: These descriptors are closed in ProcessMonitorThread()
821 
822     int child_stdin  = dup(0);
823     int child_stdout = dup(1);
824     int child_stderr = dup(2);
825 
826     // change stdin if appropriate
827     if (startData->stdinMode == kSTAFProcessIOReadFile)
828     {
829         close(child_stdin);
830 
831         child_stdin = open(STAFString(startData->stdinRedirect)
832                            .toCurrentCodePage()->buffer(), O_RDONLY);
833 
834         if (child_stdin == -1)
835         {
836             systemErrorCode = errno;
837 
838             close(child_stdout);
839             close(child_stderr);
840 
841             if (osRC) *osRC = systemErrorCode;
842 
843             if (errorBuffer)
844             {
845                 STAFString errMsg = STAFString("Error opening stdin file ") +
846                     startData->stdinRedirect + " as readonly.\nOS RC " +
847                     systemErrorCode;
848                 *errorBuffer = errMsg.adoptImpl();
849             }
850 
851             return kSTAFBaseOSError;
852         }
853     }
854 
855     // Check if is NEW_CONSOLE specified.  If no stdout file was specified,
856     // redirect stdout to /dev/null instead of to STAFProc's stdout.
857     // If no stderr file was specified, redirect stderr to /dev/null instead
858     // of to STAFProc's stdout.
859 
860     if (startData->consoleMode == kSTAFProcessNewConsole)
861     {
862         if (startData->stdoutMode == kSTAFProcessIONoRedirect &&
863             startData->stderrMode == kSTAFProcessIONoRedirect)
864         {
865             // Redirect stdout and stderr to /dev/null (instead of to
866             // STAFProc's stdout/stderr)
867             startData->stdoutMode = kSTAFProcessIOReplaceFile;
868             startData->stdoutRedirect = sDefaultRedirectFile.getImpl();
869             startData->stderrMode = kSTAFProcessIOStdout;
870         }
871         else if (startData->stdoutMode == kSTAFProcessIONoRedirect)
872         {
873             // Redirect stdout to /dev/null (instead of to STAFProc's stdout)
874             startData->stdoutMode = kSTAFProcessIOReplaceFile;
875             startData->stdoutRedirect = sDefaultRedirectFile.getImpl();
876         }
877         else if (startData->stderrMode == kSTAFProcessIONoRedirect)
878         {
879             // Redirect stderr to /dev/null (instead of to STAFProc's stderr)
880             startData->stderrMode = kSTAFProcessIOReplaceFile;
881             startData->stderrRedirect = sDefaultRedirectFile.getImpl();
882         }
883     }
884 
885     // change stdout if appropriate
886     if (startData->stdoutMode != kSTAFProcessIONoRedirect)
887     {
888         int outFlags = O_CREAT | O_WRONLY;
889 
890         if (startData->stdoutMode == kSTAFProcessIOAppendFile)
891             outFlags |= O_APPEND;
892         else
893             outFlags |= O_TRUNC;
894 
895         close(child_stdout);
896 
897         child_stdout = open(STAFString(startData->stdoutRedirect)
898                             .toCurrentCodePage()->buffer(), outFlags,
899                             S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
900                             S_IROTH | S_IWOTH);
901 
902         if (child_stdout == -1)
903         {
904             systemErrorCode = errno;
905 
906             close(child_stdin);
907             close(child_stderr);
908 
909             if (osRC) *osRC = systemErrorCode;
910 
911             if (errorBuffer)
912             {
913                 STAFString errMsg = STAFString("Error opening stdout file: ") +
914                     startData->stdoutRedirect + "\nOS RC " + systemErrorCode;
915                 *errorBuffer = errMsg.adoptImpl();
916             }
917 
918             return kSTAFBaseOSError;
919         }
920     }
921 
922     // change stderr if appropriate
923     if (startData->stderrMode == kSTAFProcessIOStdout)
924     {
925         close(child_stderr);
926         child_stderr = dup(child_stdout);
927     }
928     else if (startData->stderrMode != kSTAFProcessIONoRedirect)
929     {
930         int errFlags = O_CREAT | O_WRONLY;
931 
932         if (startData->stderrMode == kSTAFProcessIOAppendFile)
933             errFlags |= O_APPEND;
934         else
935             errFlags |= O_TRUNC;
936 
937         close(child_stderr);
938 
939         child_stderr = open(STAFString(startData->stderrRedirect)
940                             .toCurrentCodePage()->buffer(), errFlags,
941                             S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
942                             S_IROTH | S_IWOTH);
943 
944         if (child_stderr == -1)
945         {
946             systemErrorCode = errno;
947 
948             close(child_stdin);
949             close(child_stdout);
950 
951             if (osRC) *osRC = systemErrorCode;
952 
953             if (errorBuffer)
954             {
955                 STAFString errMsg = STAFString("Error opening stderr file: ") +
956                     startData->stderrRedirect + "\nOS RC " + systemErrorCode;
957                 *errorBuffer = errMsg.adoptImpl();
958             }
959 
960             return kSTAFBaseOSError;
961         }
962     }
963 
964     // determine what to do with authentication based on the following
965     // variables: authMode, default username/password, request user -
966     // name/password, and operating system
967 
968     bool mustValidate = true;
969 
970     if (startData->authMode == kSTAFProcessAuthDisabled)
971     {
972         mustValidate = false;
973 
974         if (startData->disabledAuthAction == kSTAFProcessDisabledAuthError)
975         {
976             if (startData->username != 0)
977             {
978                 if (osRC) *osRC = EACCES;
979 
980                 if (errorBuffer)
981                 {
982                     STAFString errMsg = STAFString(
983                         "Process authentication denied.  You cannot specify a "
984                         "userid when process authentication is disabled and "
985                         "the process authentication disabled action is set "
986                         "to 'Error'.");
987                     *errorBuffer = errMsg.adoptImpl();
988                 }
989 
990                 return kSTAFProcessAuthenticationDenied;
991             }
992         }
993     }
994 
995     STAFUserID_t  usrID = sOurUID;
996     STAFGroupID_t grpID = sOurGID;
997 
998     unsigned int validUser = UserAuthenticate(usrID, grpID,
999                                               STAFString(startData->username),
1000                                               STAFString(startData->password),
1001                                               mustValidate, osRC);
1002 
1003     if (!validUser || ((sOurUID != 0) && (sOurUID != usrID)))
1004     {
1005         if (errorBuffer)
1006         {
1007             STAFString errMsg = STAFString("Error during process ") +
1008                 "authentication for user name " + startData->username;
1009             *errorBuffer = errMsg.adoptImpl();
1010         }
1011 
1012         return kSTAFProcessAuthenticationDenied;
1013     }
1014 
1015     STAFString buffer = startData->command;
1016 
1017     if (startData->parms != 0)
1018         buffer += STAFString(" ") + startData->parms;
1019 
1020     // create argument table from the command and parameters passed. argv is
1021     // allocated by parseCommandParms and it must be deleted from this point
1022     // on before any return or thrown exception
1023 
1024     char **argv = 0;
1025     unsigned int argc = 4;
1026 
1027     if (startData->commandType == kSTAFProcessCommand)
1028     {
1029         argc = ParseCommandParms(buffer, &argv);
1030 
1031         if (argc == 0)
1032         {
1033             if (osRC) *osRC = EINVAL;  // invalid argument
1034             delete[] argv;
1035 
1036             if (errorBuffer)
1037             {
1038                 STAFString errMsg = STAFString("Parsing command arguments ") +
1039                     "failed.  Invalid command: " + buffer;
1040                 *errorBuffer = errMsg.adoptImpl();
1041             }
1042 
1043             return kSTAFBaseOSError;
1044         }
1045     }
1046     else
1047     {
1048         // startData->commandType == kSTAFProcessShell
1049 
1050         STAFString commandString;
1051         STAFString output;
1052 
1053         if (startData->shellCommand != 0)
1054         {
1055             // Substitute the command and possibly other data
1056 
1057             commandString = startData->shellCommand;
1058 
1059             STAFProcessShellSubstitutionData subData;
1060             subData.command = buffer;
1061             subData.title = startData->title;
1062             subData.workload = startData->workload;
1063             subData.username = startData->username;
1064             subData.password = startData->password;
1065 
1066             if (startData->stdinMode == kSTAFProcessIOReadFile)
1067                 subData.stdinfile = "< " +
1068                                     STAFString(startData->stdinRedirect);
1069 
1070             if (startData->stdoutMode != kSTAFProcessIONoRedirect)
1071             {
1072                 if (startData->stdoutMode == kSTAFProcessIOAppendFile)
1073                     subData.stdoutfile = ">> " +
1074                                          STAFString(startData->stdoutRedirect);
1075                 else
1076                     subData.stdoutfile = "> " +
1077                                          STAFString(startData->stdoutRedirect);
1078             }
1079 
1080             if (startData->stderrMode == kSTAFProcessIOStdout)
1081             {
1082                 subData.stdoutfile = subData.stdoutfile + " 2>&1";
1083             }
1084             else if (startData->stderrMode != kSTAFProcessIONoRedirect)
1085             {
1086                 if (startData->stderrMode == kSTAFProcessIOAppendFile)
1087                     subData.stderrfile = "2>> " +
1088                                          STAFString(startData->stderrRedirect);
1089                 else
1090                     subData.stderrfile = "2> " +
1091                                          STAFString(startData->stderrRedirect);
1092             }
1093 
1094             int rc = STAFProcessReplaceShellSubstitutionChars(commandString,
1095                                                               subData, output);
1096             if (rc != kSTAFOk)
1097             {
1098                 if (errorBuffer)
1099                 {
1100                     STAFString errMsg = STAFString("Invalid shell command") +
1101                         " value: " + startData->shellCommand;
1102                     *errorBuffer = errMsg.adoptImpl();
1103                 }
1104 
1105                 return rc;
1106             }
1107 
1108             buffer = output;
1109         }
1110 
1111         argv = new char *[4];
1112         argv[0] = new char [3];
1113         argv[1] = new char [3];
1114         argv[2] = new char [buffer.toCurrentCodePage()->length() + 1];
1115         argv[3] = 0;
1116 
1117         strcpy(argv[0], "sh");
1118         strcpy(argv[1], "-c");
1119         strcpy(argv[2], buffer.toCurrentCodePage()->buffer());
1120     }
1121 
1122     // create environment table if appropriate (this block requires 2 passes
1123     // of the memory block, one pass to count number of variables so that we
1124     // can allocate the correct amount of memory for the table, and a second
1125     // pass to initialize the table to point to string in the memory block.
1126     // NOTE: this block allocates memory for the envp table. the memory gets
1127     // deleted before any return point in the parent process, whereas in the
1128     // child process it gets overwritten by execve
1129 
1130     int       i = 0;
1131     char **envp = 0;
1132     int    envc = 0;
1133     char *envstr = startData->environment;
1134 
1135     if (envstr != 0)
1136     {
1137         // count number of entries
1138         while (*envstr)
1139         {
1140             envc++;
1141             envstr += strlen(envstr) + 1;
1142         }
1143 
1144         // allocate memory for the environment table
1145         envp = new char* [envc + 1];
1146         envstr = startData->environment;
1147 
1148         // initialize environment table
1149         while (*envstr)
1150         {
1151             envp[i++] = envstr;
1152             envstr += strlen(envstr) + 1;
1153         }
1154         envp[i] = 0;
1155     }
1156 
1157     // now argv and envp are ready, so lock the static variables and let
1158     // the processMonitorThread know that we are ready to start a process
1159 
1160     STAFMutexSemLock lock(sCreateProcessSem);
1161     STAFRC_t rc = kSTAFOk;
1162     STAFProcessEndCallbackLevel1 dummyCallback = { 0 };
1163 
1164     sProcessCreateInfo = ProcessCreateInfo(
1165         startData->commandType, command.toCurrentCodePage(), argv, envp,
1166         usrID, grpID, STAFString(startData->username).toCurrentCodePage(),
1167         STAFString(startData->workdir).toCurrentCodePage(),
1168         startData->stdinMode, child_stdin,
1169         startData->stdoutMode, child_stdout,
1170         startData->stderrMode, child_stderr,
1171         (startData->callback != 0) ? *startData->callback : dummyCallback);
1172 
1173     // we have a process we want to start so let process thread know
1174     sProcessThread.post();
1175 
1176     // wait until process has been created so that we can pick pid
1177     sProcessCreated.wait();
1178     sProcessCreated.reset();
1179 
1180     if (pid)        *pid = sProcessCreateInfo.pid;
1181     if (procHandle) *procHandle = sProcessCreateInfo.pid;
1182 
1183     if (sProcessCreateInfo.pid == -1)
1184     {
1185         rc = kSTAFBaseOSError;
1186 
1187         if (errorBuffer)
1188         {
1189             STAFString errMsg = STAFString("Error starting the process.");
1190             *errorBuffer = errMsg.adoptImpl();
1191         }
1192     }
1193 
1194     // recurse over arg vector and clean up
1195     for (unsigned int j = 0; j < argc; j++)
1196         delete[] argv[j];
1197 
1198     delete[] argv;
1199     delete[] envp;
1200 
1201     return rc;
1202 }
1203 
1204 
STAFProcessStop(STAFProcessID_t pid,STAFProcessStopMethod_t stopMethod,unsigned int * osRC)1205 STAFRC_t STAFProcessStop(STAFProcessID_t pid,
1206                          STAFProcessStopMethod_t stopMethod,
1207                          unsigned int *osRC)
1208 {
1209     return STAFProcessStop2(pid, stopMethod, kSTAFProcessStopRequest, osRC);
1210 }
1211 
STAFProcessStop2(STAFProcessID_t pid,STAFProcessStopMethod_t stopMethod,STAFProcessStopFlag_t stopFlag,unsigned int * osRC)1212 STAFRC_t STAFProcessStop2(STAFProcessID_t pid,
1213                           STAFProcessStopMethod_t stopMethod,
1214                           STAFProcessStopFlag_t stopFlag,
1215                           unsigned int *osRC)
1216 {
1217     // Determine the signal to send based on the stopMethod
1218 
1219     int theSignal = 0;
1220     bool sendSignalToChildProcesses = false;
1221 
1222     if (stopMethod == kSTAFProcessStopWithSigKillAll)
1223     {
1224         theSignal = SIGKILL;
1225         sendSignalToChildProcesses = true;
1226     }
1227     else if (stopMethod == kSTAFProcessStopWithSigKill)
1228     {
1229         theSignal = SIGKILL;
1230     }
1231     else if (stopMethod == kSTAFProcessStopWithSigTermAll)
1232     {
1233         theSignal = SIGTERM;
1234         sendSignalToChildProcesses = true;
1235     }
1236     else if (stopMethod == kSTAFProcessStopWithSigTerm)
1237     {
1238         theSignal = SIGTERM;
1239     }
1240     else if (stopMethod == kSTAFProcessStopWithSigIntAll)
1241     {
1242         theSignal = SIGINT;
1243         sendSignalToChildProcesses = true;
1244     }
1245     else if (stopMethod == kSTAFProcessStopWithSigInt)
1246     {
1247         theSignal = SIGINT;
1248     }
1249     else
1250     {
1251         return kSTAFInvalidValue;
1252     }
1253 
1254     // Check if a process with the specified pid exists
1255 
1256     if (stopFlag == kSTAFProcessKillRequest)
1257     {
1258         // Check if any process with this pid exists
1259 
1260         if (kill(pid, 0) == -1)
1261             return kSTAFDoesNotExist;
1262     }
1263     else // stopFlag == kSTAFProcessStopRequest
1264     {
1265         // Check if a process started by STAF exists by checking if the
1266         // process exists in sMonitorList
1267 
1268         STAFMutexSemLock lock(sMonitorDataSem);
1269         ProcessMonitorInfo processMonitorInfo;
1270         ProcessMonitorMap::iterator iter = sMonitorMap.find(pid);
1271 
1272         if (iter == sMonitorMap.end())
1273         {
1274             // Did not find a process with the specified pid
1275             return kSTAFHandleDoesNotExist;
1276         }
1277     }
1278 
1279     // Stop the process(es) using the specified stop method by sending a
1280     // signal to the process(es).  If the signal is to be sent to child
1281     // processes, we do that by specifying the negative value of the pid
1282     // to the kill() command in order to send the signal to every process
1283     // in the process group.
1284 
1285     if (kill((sendSignalToChildProcesses) ? -pid : pid, theSignal) == -1)
1286     {
1287         // Error sending the signal to the process(es)
1288 
1289         if (osRC) *osRC = errno;
1290         return kSTAFBaseOSError;
1291     }
1292 
1293     return kSTAFOk;
1294 }
1295 
STAFProcessIsValidAuthMode(STAFProcessAuthenticationMode_t authMode)1296 STAFRC_t STAFProcessIsValidAuthMode(STAFProcessAuthenticationMode_t authMode)
1297 {
1298 
1299     if ((authMode != kSTAFProcessAuthDisabled) &&
1300         (authMode != kSTAFProcessAuthNone))
1301         return kSTAFInvalidValue;
1302     else
1303         return kSTAFOk;
1304 }
1305 
1306 
STAFProcessIsValidStopMethod(STAFProcessStopMethod_t stopMethod)1307 STAFRC_t STAFProcessIsValidStopMethod(STAFProcessStopMethod_t stopMethod)
1308 {
1309     if (stopMethod == kSTAFProcessStopWithWM_CLOSE)
1310         return kSTAFInvalidValue;
1311     else
1312         return kSTAFOk;
1313 }
1314 
1315 
STAFProcessRegisterEndCallback(STAFProcessID_t pid,STAFProcessHandle_t procHandle,void * genericCallback,unsigned int callbackLevel)1316 STAFRC_t STAFProcessRegisterEndCallback(STAFProcessID_t pid,
1317                                         STAFProcessHandle_t procHandle,
1318                                         void *genericCallback,
1319                                         unsigned int callbackLevel)
1320 {
1321     // NOTE: Registered processes will always return a fake rc of 0
1322 
1323     if (genericCallback == 0) return kSTAFInvalidValue;
1324     if (callbackLevel != 1) return kSTAFInvalidValue;
1325 
1326     STAFMutexSemLock lock(sMonitorDataSem);
1327     STAFProcessEndCallbackLevel1 *callback =
1328         reinterpret_cast<STAFProcessEndCallbackLevel1 *>(genericCallback);
1329 
1330     sMonitorMap[pid].push_back(ProcessMonitorInfo(procHandle, pid, *callback));
1331 
1332     // Needed here too in case "outsider" processes are run before any
1333     // "insider" processes are run
1334     InitProcessManager();
1335 
1336     return kSTAFOk;
1337 }
1338 
STAFProcessGetHandleFromID(STAFProcessID_t processID,STAFProcessHandle_t * processHandle,unsigned int * osRC)1339 STAFRC_t STAFProcessGetHandleFromID(STAFProcessID_t processID,
1340                                     STAFProcessHandle_t *processHandle,
1341                                     unsigned int *osRC)
1342 {
1343     return STAFProcessGetHandleFromID2(
1344         processID, processHandle, kSTAFProcessStopRequest, osRC);
1345 }
1346 
STAFProcessGetHandleFromID2(STAFProcessID_t processID,STAFProcessHandle_t * procHandle,STAFProcessStopFlag_t stopFlag,unsigned int * osRC)1347 STAFRC_t STAFProcessGetHandleFromID2(STAFProcessID_t processID,
1348                                      STAFProcessHandle_t *procHandle,
1349                                      STAFProcessStopFlag_t stopFlag,
1350                                      unsigned int *osRC)
1351 {
1352     if (procHandle == 0) return kSTAFInvalidValue;
1353 
1354     *procHandle = processID;
1355 
1356     return kSTAFOk;
1357 }
1358 
1359 
STAFProcessIsRunning(STAFProcessHandle_t processHandle,unsigned int * isRunning,unsigned int * osRC)1360 STAFRC_t STAFProcessIsRunning(STAFProcessHandle_t processHandle,
1361                               unsigned int *isRunning,
1362                               unsigned int *osRC)
1363 {
1364     if (isRunning == 0) return kSTAFInvalidValue;
1365 
1366     if (kill(processHandle, 0) != -1) *isRunning = 1;
1367     else *isRunning = 0;
1368 
1369     return kSTAFOk;
1370 }
1371 
1372 
ParseCommandParms(STAFString & command,char *** argv)1373 int ParseCommandParms(STAFString &command, char ***argv)
1374 {
1375     static STAFString dquote = STAFString(kUTF8_DQUOTE);
1376     static STAFString bslash = STAFString(kUTF8_BSLASH);
1377     static STAFString space  = STAFString(kUTF8_SPACE);
1378 
1379     STAFString currChar, nextArg;
1380 
1381     // we actually allocate words (which may not match the number
1382     // of real args, but should guarantee enough slots in the array)
1383 
1384     int words = command.numWords();
1385     *argv = new char *[words + 1];
1386     memset(*argv, 0, sizeof(char *) * (words + 1));
1387 
1388     int i = 0, j = 0;
1389     int inQuotes = 0;
1390     int inEscape = 0;
1391     int argReady = 0;
1392 
1393     for (i = 0; i < command.length(STAFString::kChar); ++i)
1394     {
1395         currChar = command.subString(i, 1, STAFString::kChar);
1396 
1397         if ((currChar == space) && !inEscape && !inQuotes)
1398         {
1399             if (argReady)
1400             {
1401                 argReady = 0;
1402                 STAFStringBufferPtr nextArgInCurrCP =
1403                     nextArg.toCurrentCodePage();
1404                 unsigned int nextArgInCurrCPLen =
1405                     nextArgInCurrCP->length();
1406                 (*argv)[j] = new char[nextArgInCurrCPLen + 1];
1407                 strcpy((*argv)[j++], nextArgInCurrCP->buffer());
1408                 nextArg = "";
1409             }
1410 
1411             continue;
1412         }
1413         else if ((currChar == dquote) && !inEscape)
1414         {
1415             if (inQuotes)
1416             {
1417                 // Needed to handle an empty string parameter
1418                 argReady = 1;
1419             }
1420 
1421             inQuotes = !inQuotes;
1422             continue;
1423         }
1424         else if ((currChar == bslash) && !inEscape)
1425         {
1426             inEscape = 1;
1427             continue;
1428         }
1429         else
1430         {
1431             inEscape = 0;
1432             argReady = 1;
1433             nextArg += currChar;
1434         }
1435     }
1436 
1437     if (argReady)
1438     {
1439         STAFStringBufferPtr nextArgInCurrCP =
1440             nextArg.toCurrentCodePage();
1441         unsigned int nextArgInCurrCPLen =
1442             nextArgInCurrCP->length();
1443         (*argv)[j] = new char[nextArgInCurrCPLen + 1];
1444         strcpy((*argv)[j++], nextArgInCurrCP->buffer());
1445     }
1446 
1447     // return the number of allocated slots in argv
1448     return j;
1449 }
1450 
UserAuthenticate(STAFUserID_t & uid,STAFGroupID_t & gid,const STAFString & username,const STAFString & password,bool mustValidate,unsigned int * osRC)1451 unsigned int UserAuthenticate(STAFUserID_t &uid, STAFGroupID_t &gid,
1452                               const STAFString &username,
1453                               const STAFString &password,
1454                               bool mustValidate, unsigned int *osRC)
1455 {
1456     // Note: if username has not been specified (neither in the request
1457     //       nor as a default) then we simply take no action at all
1458 
1459     if (username.length() == 0 || mustValidate == false) return 1;
1460 
1461     char realUsername[256] = { 0 };
1462     char realPassword[256] = { 0 };
1463 
1464     STAFStringBufferPtr realUsernameInCurrCP = username.toCurrentCodePage();
1465     STAFStringBufferPtr realPasswordInCurrCP = password.toCurrentCodePage();
1466 
1467     strcpy(realUsername, realUsernameInCurrCP->buffer());
1468     strcpy(realPassword, realPasswordInCurrCP->buffer());
1469 
1470     // Note: we already handled the case where username is empty (above)
1471 
1472     struct passwd *n = 0;
1473 
1474     if (username.isDigits())
1475     {
1476         // if username is uid, resolve & convert ...
1477         try
1478         {
1479             n = getpwuid(uid = username.asUInt());
1480 
1481             // XXX: This was the old way
1482             // if (n) username = n->pw_name;
1483             // but, I think this is actually correct
1484             if (n) strcpy(realUsername, n->pw_name);
1485         }
1486         catch (...)
1487         {
1488             // Note:  asUInt() will throw an exception if not in range
1489             // 0 to UINT_MAX
1490         }
1491     }
1492     else
1493     {
1494         // ... otherwise, get username and set uid
1495         n = getpwnam(realUsername);
1496 
1497         if (n) uid = n->pw_uid;
1498     }
1499 
1500     if (n == NULL)
1501     {
1502         return 0;
1503     }
1504 
1505     gid = n->pw_gid;
1506 
1507     return (*sAuthenticateFuncPtr)(realUsername, realPassword);
1508 }
1509 
sAuthNone(const char * user,const char * pswd)1510 unsigned int sAuthNone(const char *user, const char *pswd)
1511 {
1512     return 1;
1513 }
1514 
1515 ////////////////////////////////////////////////////////////////////////////////
1516 #ifdef    STAF_PAM_AVAILABLE
1517 #include <security/pam_appl.h>
1518 #include <security/pam_misc.h>
1519 static   struct pam_conv conv = { misc_conv, NULL };
1520 #endif // STAF_PAM_AVAILABLE
1521 
sAuthPam(const char * user,const char * pswd)1522 unsigned int sAuthPam(const char *user, const char *pswd)
1523 {
1524 
1525 #ifdef    STAF_PAM_AVAILABLE
1526 
1527     // DOCUMENTATION FOUND AT:
1528     // http://www.us.kernel.org/pub/linux/libs/pam/Linux-PAM-html/
1529     // pam_appl.html
1530 
1531     // Add the following lines to /etc/pam.conf
1532     // STAFProc  auth    required  /lib/security/pam_unix_auth.so
1533     // STAFProc  account required  /lib/security/pam_unix_acct.so
1534 
1535     int rc = PAM_SUCCESS;
1536     pam_handle_t *pamh = NULL;
1537     const char *serviceName = "STAFProc";
1538 
1539     rc = pam_start(serviceName, user, &conv, &pamh);
1540 
1541     if (rc == PAM_SUCCESS)
1542     {
1543         // is user really user?
1544         rc = pam_authenticate(pamh, 0);
1545     }
1546 
1547     if (rc == PAM_SUCCESS)
1548     {
1549         // permitted access?
1550         rc = pam_acct_mgmt(pamh, 0);
1551     }
1552 
1553     // This is where we have been authorized or not.
1554 
1555     if (rc == PAM_SUCCESS)
1556     {
1557         if (pam_end(pamh, rc) != PAM_SUCCESS)
1558         {
1559             STAFTrace::trace(
1560                 kSTAFTraceError,
1561                 serviceName + ": Failed to release authenticator");
1562         }
1563 
1564         return 1;
1565     }
1566 
1567 #endif // STAF_PAM_AVAILABLE
1568 
1569     return 0;
1570 }
1571 
1572 ////////////////////////////////////////////////////////////////////////////////
1573 
1574 
1575 
1576 
1577