2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
24 * File: spc-exec.c $TOG: spc-exec.c /main/9 1998/10/26 17:22:38 mgreess $
27 * (c) Copyright 1988, Hewlett-Packard Company, all rights reserved.
29 * (c) Copyright 1993, 1994 Hewlett-Packard Company *
30 * (c) Copyright 1993, 1994 International Business Machines Corp. *
31 * (c) Copyright 1993, 1994 Sun Microsystems, Inc. *
32 * (c) Copyright 1993, 1994 Novell, Inc. *
35 #include <bms/sbport.h> /* NOTE: sbport.h must be the first include. */
39 #include <sys/param.h>
45 #include <bms/MemoryMgr.h>
47 #include <SPC/spc-proto.h>
54 #include "DtSvcLock.h"
58 SPC_Connection_Ptr write_terminator=NULL, read_terminator=NULL;
61 * Forward declarations
63 static char *get_path_from_context (
65 static int remove_variable(
67 static void resolve_variable_reference(
71 * This array contains the process id's of the sub-processes
72 * started by the daemon. When a sub-process terminates, its
73 * entry will be set to SPCD_DEAD_PROCESS. This list of pid's
74 * is kept beause when the exit timer expires, if the daemon
75 * has no sub-processes running, it will exit.
77 pid_t *SPC_pid_list = NULL;
80 * This global variable is set by the daemon when the client
83 int SPC_client_version_number = SPC_PROTOCOL_VERSION;
86 * If this variable is not NULL, it will contain the name of
87 * the mount point environment variable plus its value thus it
90 char *SPC_mount_point_env_var = NULL;
92 /* External definitions */
94 extern XeChar spc_logging;
95 extern XeString *environ;
98 * Routines for handling Sub-Processes
103 ** Initialize synchronous terminators.
107 /*----------------------------------------------------------------------+*/
108 SPC_Setup_Synchronous_Terminator(void)
109 /*----------------------------------------------------------------------+*/
114 if(write_terminator) {
115 _DtSvcProcessUnlock();
120 SPC_Error(SPC_No_Pipe);
121 _DtSvcProcessUnlock();
125 if((write_terminator=SPC_Alloc_Connection())==SPC_ERROR) {
126 _DtSvcProcessUnlock();
129 SPC_Add_Connection(write_terminator);
131 if((read_terminator=SPC_Alloc_Connection())==SPC_ERROR) {
132 _DtSvcProcessUnlock();
135 SPC_Add_Connection(read_terminator);
137 write_terminator->sid=pipes[WRITE_SIDE];
138 write_terminator->connected=TRUE;
140 read_terminator->sid=pipes[READ_SIDE];
141 read_terminator->connected=TRUE;
142 SPC_XtAddInput(NULL, &read_terminator->termination_id, read_terminator->sid,
143 SPC_Conditional_Packet_Handler, SPC_Terminator);
145 _DtSvcProcessUnlock();
149 /*----------------------------------------------------------------------+*/
150 SPC_Connection_Ptr SPC_Channel_Terminator_Connection(SPC_Channel_Ptr channel)
151 /*----------------------------------------------------------------------+*/
153 if(IS_REMOTE(channel))
154 return(channel->connection);
156 return(read_terminator);
159 /*----------------------------------------------------------------------+*/
160 void SPC_Close_Unused(void)
161 /*----------------------------------------------------------------------+*/
163 /* Close any and all unused file descriptors */
166 for (fd = STDERR + 1; fd < max_fds; fd++) spc_close(fd);
169 /*----------------------------------------------------------------------+*/
170 SPC_MakeSystemCommand(SPC_Channel_Ptr channel)
171 /*----------------------------------------------------------------------+*/
177 XeChar newargtwo[_POSIX_ARG_MAX];
178 int argtwolen=0, tmplen=0;
180 /* Allocate our memory up front */
184 newargtwo[argtwolen]=0;
186 /* copy path into newargtwo */
188 strncat(newargtwo, channel->path, _POSIX_ARG_MAX-1);
189 strcat(newargtwo, (XeString)" ");
190 argtwolen=strlen(newargtwo);
192 /* copy argv into newargtwo */
193 for(tmp_argv=channel->argv; tmp_argv && *tmp_argv; tmp_argv++) {
194 tmplen=strlen(*tmp_argv)+1; /* Room for extra space */
195 if((tmplen+argtwolen)<_POSIX_ARG_MAX-1) {
196 strcat(newargtwo, *tmp_argv);
197 strcat(newargtwo, (XeString)" ");
202 errbuf = malloc(sizeof(XeChar) * 100);
206 sprintf(errbuf,"(%d chars), max. length is %d",tmplen,_POSIX_ARG_MAX);
207 SPC_Error(SPC_Arg_Too_Long, tmp_argv, _POSIX_ARG_MAX);
215 First use the value of $SB_SHELL (if any),
217 then use DEFAULT_SHELL
220 if(!(shell=getenv((XeString)"SB_SHELL")))
221 if(!(shell=getenv((XeString)"SHELL")))
222 shell = DEFAULT_SHELL;
224 /* setup argv properly */
226 argv[0]=SPC_copy_string(shell);
227 argv[1]=SPC_copy_string((XeString)"-c");
228 argv[2]=SPC_copy_string(newargtwo);
230 channel->argv = argv;
231 channel->IOMode |= SPCIO_DEALLOC_ARGV;
233 /* Now set this shell as the path */
235 channel->path = shell;
241 * Routines for handling child process termination
244 /*----------------------------------------------------------------------+*/
245 /* This is the right way according to the Spec 1170 */
246 void SPC_Child_Terminated(int i)
247 /*----------------------------------------------------------------------+*/
249 /* This catches signals for sub-process termination */
250 int type, cause, status;
252 SPC_Channel_Ptr channel;
253 protocol_request req, *prot;
254 buffered_data data, *pdata;
257 int saved_errno = errno;
265 while(pid = waitpid(wait_pid, &status, WNOHANG)) {
266 if((pid == -1 && errno == ECHILD) || pid == 0) {
267 /* no more children. Return */
272 /* Okay, we got the process ID of a terminated child. Find the
273 channel associated with this PID. */
274 channel=SPC_Find_PID(pid);
276 fprintf(stderr, (XeString)"got SIGCHLD, pid: %d, channel: %p\n", pid, channel);
285 * Look for this process in the pid list. If found, mark it
288 if (SPC_pid_list != NULL) {
289 for (indx=0; SPC_pid_list[indx] != NULL; indx++)
290 if (SPC_pid_list[indx] == pid) {
291 SPC_pid_list[indx] = SPCD_DEAD_PROCESS;
295 _DtSvcProcessUnlock();
297 /* We have the channel. Mark it as being closed. */
299 channel->status = status;
301 /* If we this channel is set up for synchronous termination,
302 write the protocol request to record that this guy died.
303 Otherwise, call the termination handler directly. */
305 if(IS_SPCIO_SYNC_TERM(channel->IOMode)) {
307 /* This code is basically what SPC_Write_Protocol_Request does.
308 It is replicated here because a call to SPC_W_P_R would have
309 to be re-enterant if we called it here, and SPC_W_P_R is not
310 re-enterant at this time. */
312 SPC_Reset_Protocol_Ptr(prot, channel, APPLICATION_DIED, 0);
313 pdata->len=WRITE_APPLICATION_DIED(pdata, status);
314 length=WRITE_HEADER(pdata, channel->cid,
318 pdata->data[length]=(XeChar)' ';
319 length=pdata->len+REQUEST_HEADER_LENGTH;
320 if(write(write_terminator->sid, pdata->data, length)==ERROR)
321 SPC_Error(SPC_Internal_Error);
322 pdata->offset=REQUEST_HEADER_LENGTH;
323 print_protocol_request((XeString) (XeString)" <-- INTERNAL APPLICATION_DIED", prot);
326 SPC_Change_State(channel, NULL, -1, 0);
327 if(channel->Terminate_Handler) {
328 XeSPCGetProcessStatus(channel, &type, &cause);
329 (* channel->Terminate_Handler)
330 (channel, channel->pid, type, cause, channel->Terminate_Data);
333 /* Loop around & get another PID */
340 *** Check to see if a given path names an executable file. Thus, we
341 *** need to know if the file exists. If it is a directory, we want
342 *** to fail. Otherwise, we want to us the system rules for checking
343 *** on the file, and thus the 'access' call.
347 static Boolean executable_predicate(XeString path, XeString dir, XeString file)
350 struct stat file_status;
355 if(stat(path, &file_status) != 0)
358 if(S_ISDIR(file_status.st_mode))
361 return(access(path, X_OK | F_OK) == 0);
364 /*----------------------------------------------------------------------+*/
365 int exec_proc_local_channel_object(SPC_Channel_Ptr channel)
366 /*----------------------------------------------------------------------+*/
368 sigset_t newsigmask, oldsigmask;
372 XeString dir = XeString_NULL;
374 int i, reuse_pid = 0;
376 call_parent_method(channel, exec_proc, (channel), result);
378 if(result==SPC_ERROR)
381 /* Check to see if the channel pathname points to a valid executable.
382 We do this by using the _path_search function. If the channel
383 has a PATH variable set in its local environment, use it,
384 otherwise use the "global" environment. We can accomplish this
385 by using the spc_getenv call in the _path_search call. If the
386 channel doesn't have a PATH variable, then spc_getenv will
387 return NULL, which indicates use of the global environment.
390 if(!_path_search(SPC_Getenv("PATH", channel->envp),
392 executable_predicate)) {
393 SPC_Error(SPC_Cannot_Exec, channel->path);
397 /* If we were passed a host:dir to cd to, make sure it exists. */
398 /* We want to do this before we fork the child. */
400 if((channel->context_dir) && (channel->context_dir[0])) {
401 struct stat stat_info;
405 if (SPC_client_version_number < SPC_PROTOCOL_VERSION_CDE_BASE)
406 dir = get_path_from_context(channel->context_dir);
409 * context_dir is actually a "netfile" so it needs to
410 * be converted to a "real" path.
412 dir = (char *) tt_netfile_file (channel->context_dir);
413 if (tt_ptr_error (dir) != TT_OK)
416 _DtSvcProcessUnlock();
419 /* can't make connection ... */;
420 else if (stat(dir,&stat_info) != 0)
421 /* directory not there */;
423 else if ((stat_info.st_mode & S_IFDIR) == 0)
424 /* path is not a directory ... */;
428 if (!ok && IS_SPCIO_FORCE_CONTEXT(channel->IOMode)) {
429 if (dir != NULL && (strcmp (dir, channel->context_dir) != 0))
430 SPC_Error(SPC_cannot_Chdir, dir);
431 SPC_Error(SPC_cannot_Chdir, channel->context_dir);
438 if(mempf0(channel, pre_fork)==SPC_ERROR)
441 /* When using HP NLIO (xj0input) we have a problem. Namely, */
442 /* the xj0 processs uses signal() to deal with SIGCLD which */
443 /* is incompatible with sigaction/sigprogmask/etc. Even */
444 /* though xj0 resets the signal handler, since the signal */
445 /* routines are incompatible, our original handler gets lost. */
446 /* Hence, we need to reset it. We do it here everytime we */
447 /* fork a child just to be on the safe side. */
449 SPC_ResetTerminator();
451 sigemptyset(&newsigmask);
452 sigemptyset(&oldsigmask);
453 sigaddset(&newsigmask, SIGCHLD);
455 if (sigprocmask(SIG_BLOCK, &newsigmask, &oldsigmask) == ERROR)
458 pid = channel->pid = fork();
461 * Must save this pid so that when the daemon's timer goes off,
462 * if there has been no activity and there are no sub-processes
463 * running, the daemon can exit.
467 if (SPC_pid_list == NULL)
469 * Create the first block plus the NULL terminator.
471 SPC_pid_list = (pid_t *) malloc (2 * sizeof (pid_t));
474 * If a dead pid entry exists, reuse it; otherwise, must create
475 * room for the new pid.
477 for (i = 0; SPC_pid_list[i] != NULL; i++)
478 if (SPC_pid_list[i] == SPCD_DEAD_PROCESS) {
479 SPC_pid_list[i] = pid;
484 SPC_pid_list = (pid_t *) realloc (SPC_pid_list,
485 (i+2) * sizeof (pid_t));
488 SPC_pid_list[i] = pid;
489 SPC_pid_list[i+1] = NULL;
491 _DtSvcProcessUnlock();
496 /* Did we really fork? */
498 SPC_Error(SPC_Cannot_Fork);
501 /* Do any set up for the parent process here */
502 mempf1(channel, post_fork, pid);
506 /* Reinstate the old signal mask (unblock SIGCLD). */
508 sigprocmask(SIG_SETMASK, &oldsigmask, (sigset_t *)NULL);
512 /* Child process: connect wires, make environment and exec sub-process */
514 sigprocmask(SIG_SETMASK, &oldsigmask, (sigset_t *)NULL);
516 /* Make sure the child is the process group leader. In the case of
517 ptys, we also want to break the current terminal affiliation.
518 We want to be the process group leader so XeSPCKillProcess (which
519 does a kill(-pid, 9)) will kill all processes associated with us.
521 For PTY's, we need to break the terminal affiliation so the next
522 open (which will be a pty) will cause us to become affiliated with
523 the pty. We do this so when the parent process closes the master
524 side of the pty, the slave side processes get SIGHUP. If they
525 ignore SIGHUP, they will never die. So it goes...
528 if(IS_SPCIO_PTY(channel->IOMode))
531 pid_t tmppid = getpid();
532 if(setpgid(tmppid, tmppid) == -1)
533 fprintf(stderr, (XeString)"setpgid failed, errno: %d\n", errno);
536 /* Connect wires to sub-process standard files */
537 result=mempf1(channel, post_fork, pid);
539 if(result!=SPC_ERROR) {
545 * Before adding in the list of environment variables
546 * from the environment variable files, must search
547 * the list for LANG definitions. If found, the
548 * last definition must be putenv'ed to assure the
549 * multi-byte parsing code is using the correct locale.
551 for (i = 0, ppch = channel->envp; *ppch; *ppch++, i++)
552 if (!strncmp (*ppch, "LANG=", 5))
556 resolve_variable_reference (&channel->envp[indx]);
559 if (!setlocale (LC_CTYPE, ""))
561 * setlocale failed - log a message but execute
562 * the command anyway.
564 if (SPC_Print_Protocol != NULL)
565 (void) fprintf(SPC_Print_Protocol,
566 "+++> Failed to 'setlocale'; LANG = %s\n",
568 /* Mix in the stashed environment */
570 for(envp=channel->envp; *envp; envp++)
571 resolve_variable_reference(&*envp);
573 if (SPC_mount_point_env_var != NULL)
575 * The mount point environment variable was
576 * inherited by the daemon or was given to the
577 * daemon via the command line. In either case
578 * this subprocess must inherit the daemon's
581 (void) putenv (SPC_mount_point_env_var);
582 _DtSvcProcessUnlock();
584 /* Connect to the context directory */
585 /* We have already validated this guy exists */
593 if(result!=SPC_ERROR) {
595 /* Compiler barfs without cast ? */
596 #if defined(__hpux_8_0) || defined(__aix)
597 result=execvp(channel->path, channel->argv);
599 result=execvp(channel->path, channel->argv);
601 /* If we return from exec, it failed */
602 SPC_Error(SPC_Cannot_Exec, channel->path);
605 /* We want to get rid of this child image (carefully) */
612 /****************************************************************************
614 * get_path_from_context - given a 'context' string in the following form:
618 * return the path component.
620 * NOTE - the caller must free the returned string.
624 * char *context - the context string to parse
628 * A NULL if a pathname cannot be constructed.
630 ****************************************************************************/
632 static char *get_path_from_context (
637 char *netfile = NULL;
639 char tmp[MAXPATHLEN];
643 * Break context into its host and file parts.
648 (void) strcpy (tmp, context);
651 if ((pch = (char *) strchr (tmp, ':')) != NULL) {
658 return (strdup (file));
660 netfile = (char *) tt_host_file_netfile (host, file);
661 if (tt_ptr_error (netfile) != TT_OK) {
662 SPC_Error (SPC_Cannot_Create_Netfilename, context, host);
666 path = (char *) tt_netfile_file (netfile);
668 if (tt_ptr_error (path) != TT_OK) {
669 SPC_Error (SPC_Cannot_Create_Netfilename, context, host);
677 /**************************************************************************
681 * This takes a string of the format:
683 * var_name=some_value | <remove_variable_keyword> var_name
685 * and if the second form is found, 'var_name' is removed from
690 * char *string see the format above.
694 * int 0 if 'string' contains a 'remove variable' command
695 * 1 if string does not contain a 'remove variable' command
699 * char **environ 'var_name' is removed from the environment
701 **************************************************************************/
714 * If string contains some white space before the variable
715 * or keyword, skip the white space.
720 ((mblen (pch, MB_CUR_MAX) == 1)) &&
722 isspace ((u_char)*pch))
727 if (strncmp (pch, SPC_REMOVE_VAR, strlen (SPC_REMOVE_VAR)))
731 * Skip the white space after the keyword and move to the
732 * beginning of the variable.
734 pch = pch + strlen (SPC_REMOVE_VAR);
737 ((mblen (pch, MB_CUR_MAX) == 1)) &&
739 isspace ((u_char)*pch))
746 * pch should now point to the variable to be removed.
748 * tmp_var will be equal to the variable with a trailing '='.
749 * This is added so the future comparison will not match
750 * against variables that start with 'tmp_var' but then
751 * have something else.
753 tmp_var = malloc ((strlen (pch) + 2) * sizeof (char));
754 (void) sprintf (tmp_var, "%s=", pch);
755 tmp_len = strlen (tmp_var);
758 * Scan 'environ' for 'tmp_var'
760 for (ppch = environ; *ppch; *ppch++) {
761 if (!strncmp (tmp_var, *ppch, tmp_len)) {
763 * Found the variable so remove it by moving all
764 * variables after *ppch up.
766 for (ppch2 = ppch; *ppch2; *ppch2++) {
774 free ((char *) tmp_var);
779 /**************************************************************************
781 * resolve_variable_reference ()
783 * This function takes a string of the format:
785 * var_name=some_value
787 * and if 'some_value' contains a reference to an environment
788 * variable, the reference is replaced with the value of the
791 * For example, if 'string' is
795 * and $VFA_TOP is '/some_tree', then 'string' will be changed to:
801 * o 'putenv' will be invoked with argument 'string', even if 'string'
804 * o A valid variable name consists of alphanumerics and underscore
808 * char **string MODIFIED - the environment variable
809 * definition to be parsed
815 **************************************************************************/
818 resolve_variable_reference(
819 char **string ) /* MODIFIED */
825 int n; /* number of bytes in a string */
830 char *pbeg = *string;
831 char *string_start = *string;
833 if (!remove_variable (*string))
843 * Look for '$' - the beginning of a variable reference.
844 * If a '$' character is not found, exit the loop
849 if (((len = mblen (pch, MB_CUR_MAX)) == 1) && (*pch == '$'))
853 * Move past this char
859 * pch is null or it points to an invalid mulit-byte
865 pch = strchr (pch, '$');
868 if (pch == NULL || *pch == '\000')
870 * The string doesn't contain any (more) variables
874 string_start = *string;
877 * Found a '$' - the beginning of an environment variable so
878 * skip it and check the next char for '{' and if it is found
879 * skip it and move to the real beginning of an env variable.
886 if ((mblen (pch, MB_CUR_MAX) == 1) && (*pch == '{')) {
890 pch++; /* input = ${ */
893 if (*pch == '\0') /* input = $\0 or ${\0 */
897 * Find the end of the variable name - it is assumed to
898 * be the first character that is not an alpha-numeric
906 if ((mblen (pch, MB_CUR_MAX) > 1) ||
907 ((mblen (pch, MB_CUR_MAX) == 1) &&
908 ((*pch == '_') || (isalnum (*pch))))) {
910 if (isalnum (*pch) || *pch == '_') {
914 len = mblen (pch, MB_CUR_MAX);
924 if (found_brace && (mblen (pch, MB_CUR_MAX) == 1) && (*pch == '}'))
926 if (found_brace && *pch == '}')
929 * Move past the closing brace
937 * Nothing 'recognizable' follows the $ or ${
944 * Stuff the environment variable name in 'variable' and then
945 * get its value. If the variable doesn't exist, leave its
946 * name in the string.
948 (void) strncpy (variable, var_start, n);
951 if ((value = getenv (variable)) == NULL) {
953 * Leave what looks like an environment variable in place.
959 if (strlen (value) == 0) {
965 * Need to replace the variable definition with the string
966 * pointed to by 'value'. So create a string that contains the
967 * characters before the environment variable, then the contents
968 * of 'value' and finally, the characters after the environment
971 if (string_end == string_start)
973 * There is nothing to prepend before 'value'.
977 (void) strncpy (buf, string_start, (string_end - string_start));
978 buf[(string_end - string_start)] = '\0';
980 (void) strcat (buf, value);
983 (void) strcat (buf, pch);
986 * Now put 'buf' into 'string'.
988 *string = realloc (*string, strlen (buf) + 1);
989 (void) strcpy (*string, buf);
995 * Even if no substitutions were made, the variable must
996 * be put in the environment.
998 (void) putenv (*string);