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